feedx 0.12.2 → 0.12.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +3 -0
  3. data/.github/workflows/lint.yml +18 -0
  4. data/.github/workflows/test.yml +48 -0
  5. data/.gitignore +1 -0
  6. data/.golangci.yml +4 -0
  7. data/.rubocop.yml +14 -5
  8. data/Gemfile +0 -2
  9. data/Gemfile.lock +60 -50
  10. data/Makefile +6 -6
  11. data/README.md +1 -1
  12. data/compression.go +18 -0
  13. data/compression_test.go +17 -5
  14. data/consumer.go +12 -3
  15. data/consumer_test.go +50 -19
  16. data/ext/parquet/decoder.go +59 -0
  17. data/ext/parquet/decoder_test.go +88 -0
  18. data/ext/parquet/encoder.go +27 -0
  19. data/ext/parquet/encoder_test.go +70 -0
  20. data/ext/parquet/go.mod +12 -0
  21. data/ext/parquet/go.sum +193 -0
  22. data/ext/parquet/parquet.go +78 -0
  23. data/ext/parquet/parquet_test.go +28 -0
  24. data/ext/parquet/testdata/alltypes_plain.parquet +0 -0
  25. data/feedx.gemspec +6 -6
  26. data/feedx_ext_test.go +6 -0
  27. data/feedx_test.go +8 -6
  28. data/format.go +45 -15
  29. data/format_test.go +7 -5
  30. data/go.mod +8 -5
  31. data/go.sum +95 -32
  32. data/internal/testdata/testdata.pb.go +176 -77
  33. data/lib/feedx/cache/memory.rb +1 -0
  34. data/lib/feedx/format.rb +1 -1
  35. data/lib/feedx/producer.rb +18 -19
  36. data/lib/feedx/stream.rb +4 -8
  37. data/producer_test.go +4 -4
  38. data/reader_test.go +6 -5
  39. data/spec/feedx/cache/memory_spec.rb +2 -2
  40. data/spec/feedx/cache/value_spec.rb +1 -1
  41. data/spec/feedx/compression/gzip_spec.rb +1 -1
  42. data/spec/feedx/compression/none_spec.rb +1 -1
  43. data/spec/feedx/compression_spec.rb +2 -2
  44. data/spec/feedx/consumer_spec.rb +5 -4
  45. data/spec/feedx/format/abstract_spec.rb +2 -1
  46. data/spec/feedx/format/json_spec.rb +6 -6
  47. data/spec/feedx/format/parquet_spec.rb +1 -1
  48. data/spec/feedx/format/protobuf_spec.rb +1 -1
  49. data/spec/feedx/format_spec.rb +2 -2
  50. data/spec/feedx/producer_spec.rb +10 -9
  51. data/spec/feedx/stream_spec.rb +30 -18
  52. data/writer.go +1 -4
  53. data/writer_test.go +8 -8
  54. metadata +30 -25
  55. data/.travis.yml +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 898f80c359182952ab192f95fb05dc1e6ae806f0d661d71244813b779595d0f8
4
- data.tar.gz: 43778c6084131e8ffa9ba59b7836fe198f7595e5dc07af1c4e11a9b03ac49419
3
+ metadata.gz: 6a8328c3af95657d103cf62048d13cb5a14cc50466351f2178082612c347a8cf
4
+ data.tar.gz: f2fdb20a577c88f165f2cbac3dadf217889716b67b9ae20e4941ed9ef396e864
5
5
  SHA512:
6
- metadata.gz: fa8f2e4e26c140bb4c2732655ac73c39ef20055f215ac39c8c62bd77e7cf27d7d8140287ed6c90a4c4da6bf461d491123f76784ce5d770189a45da7fea897965
7
- data.tar.gz: a35d5296c5aec452b450e95985535c98b6b99f1a872624b3ad3974cbb0f56b0e6d0ae8395edd849c97242ae0a77ee77537404cb3dce22882a3a310a6e0f04a04
6
+ metadata.gz: 6cf78b2fb0917c7afb33014e6b3e30d045ffca874f4c8463092f25a7b22b268ab9f2cbd03a1dfda9df7a9314fe213d11c332ff965eb7b81436768cb94d80bf83
7
+ data.tar.gz: 9131ba95738e600ab4e731546b6e876cae77120782e148a6eac6e3ea4b90e398975f15f2af00b325e86f3fc73a79c39c4a6be25e80bc7298881ff636d61d150b
data/.editorconfig CHANGED
@@ -7,3 +7,6 @@ end_of_line = lf
7
7
  charset = utf-8
8
8
  trim_trailing_whitespace = true
9
9
  insert_final_newline = true
10
+
11
+ [{*.go,Makefile}]
12
+ indent_style = tab
@@ -0,0 +1,18 @@
1
+ name: Lint
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ pull_request:
7
+ branches:
8
+ - main
9
+ jobs:
10
+ golangci:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v2
15
+ - name: Run lint
16
+ uses: golangci/golangci-lint-action@v2
17
+ with:
18
+ version: latest
@@ -0,0 +1,48 @@
1
+ name: Test
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ pull_request:
7
+ branches:
8
+ - main
9
+ jobs:
10
+ go:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ go-version: [1.16.x, 1.17.x]
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v2
18
+ - name: Cache dependencies
19
+ uses: actions/cache@v2
20
+ with:
21
+ path: ~/go/pkg/mod
22
+ key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
23
+ restore-keys: |
24
+ ${{ runner.os }}-go-
25
+ - name: Setup Go
26
+ uses: actions/setup-go@v2
27
+ with:
28
+ go-version: ${{ matrix.go-version }}
29
+ - name: Run tests
30
+ run: make test
31
+ ruby:
32
+ runs-on: ubuntu-latest
33
+ strategy:
34
+ matrix:
35
+ ruby-version: ["2.7", "3.0", "3.1"]
36
+ steps:
37
+ - name: Install libarrow
38
+ run: |
39
+ wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
40
+ sudo apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
41
+ sudo apt update
42
+ sudo apt install -y libarrow-dev libarrow-glib-dev libarrow-dataset-dev libplasma-dev libplasma-glib-dev libgandiva-dev libgandiva-glib-dev libparquet-dev libparquet-glib-dev
43
+ - uses: actions/checkout@v2
44
+ - uses: ruby/setup-ruby@v1
45
+ with:
46
+ ruby-version: ${{ matrix.ruby-version }}
47
+ bundler-cache: true
48
+ - run: bundle exec rake
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  .rubocop-*
2
2
  pkg/
3
3
  *~
4
+ *.makefile
data/.golangci.yml ADDED
@@ -0,0 +1,4 @@
1
+ linters-settings:
2
+ errcheck:
3
+ exclude-functions:
4
+ - (*github.com/bsm/feedx.Writer).Discard
data/.rubocop.yml CHANGED
@@ -1,8 +1,17 @@
1
- require: rubocop-performance
2
- inherit_from:
3
- - https://gitlab.com/bsm/misc/raw/master/rubocop/default.yml
1
+ inherit_gem:
2
+ rubocop-bsm:
3
+ - default.yml
4
+ inherit_mode:
5
+ merge:
6
+ - Exclude
4
7
 
5
8
  AllCops:
6
- TargetRubyVersion: "2.4"
9
+ TargetRubyVersion: 2.7
10
+ Naming/MethodParameterName:
11
+ MinNameLength: 2
12
+ Naming/MemoizedInstanceVariableName:
13
+ Enabled: false
14
+ Naming/FileName:
15
+ Exclude: [lib/sortable-by.rb]
7
16
  Metrics/ParameterLists:
8
- Max: 10
17
+ Max: 8
data/Gemfile CHANGED
@@ -1,4 +1,2 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
-
4
- gem 'google-protobuf', '>= 3.7.0-rc2'
data/Gemfile.lock CHANGED
@@ -1,70 +1,82 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- feedx (0.12.2)
5
- bfs (>= 0.5.0)
4
+ feedx (0.12.7)
5
+ bfs (>= 0.8.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- ast (2.4.1)
11
- bfs (0.7.2)
12
- diff-lcs (1.4.4)
13
- extpp (0.0.8)
14
- gio2 (3.4.3)
15
- gobject-introspection (= 3.4.3)
16
- glib2 (3.4.3)
10
+ ast (2.4.2)
11
+ bfs (0.9.0)
12
+ bigdecimal (3.1.1)
13
+ diff-lcs (1.5.0)
14
+ extpp (0.1.0)
15
+ gio2 (3.4.9)
16
+ gobject-introspection (= 3.4.9)
17
+ glib2 (3.4.9)
17
18
  native-package-installer (>= 1.0.3)
18
19
  pkg-config (>= 1.3.5)
19
- gobject-introspection (3.4.3)
20
- glib2 (= 3.4.3)
21
- google-protobuf (3.12.2)
22
- native-package-installer (1.0.9)
23
- parallel (1.19.2)
24
- parser (2.7.1.4)
20
+ gobject-introspection (3.4.9)
21
+ glib2 (= 3.4.9)
22
+ google-protobuf (3.19.2)
23
+ native-package-installer (1.1.1)
24
+ parallel (1.21.0)
25
+ parser (3.1.0.0)
25
26
  ast (~> 2.4.1)
26
- pbio (0.2.1)
27
+ pbio (0.3.0)
27
28
  google-protobuf
28
- pkg-config (1.4.1)
29
+ pkg-config (1.4.6)
29
30
  rainbow (3.0.0)
30
- rake (13.0.1)
31
- red-arrow (0.17.1)
31
+ rake (13.0.6)
32
+ red-arrow (3.0.0)
33
+ bigdecimal (>= 2.0.3)
32
34
  extpp (>= 0.0.7)
33
35
  gio2 (>= 3.3.6)
34
36
  native-package-installer
35
37
  pkg-config
36
- red-parquet (0.17.1)
37
- red-arrow (= 0.17.1)
38
- regexp_parser (1.7.1)
39
- rexml (3.2.4)
40
- rspec (3.9.0)
41
- rspec-core (~> 3.9.0)
42
- rspec-expectations (~> 3.9.0)
43
- rspec-mocks (~> 3.9.0)
44
- rspec-core (3.9.2)
45
- rspec-support (~> 3.9.3)
46
- rspec-expectations (3.9.2)
38
+ red-parquet (3.0.0)
39
+ red-arrow (= 3.0.0)
40
+ regexp_parser (2.2.0)
41
+ rexml (3.2.5)
42
+ rspec (3.10.0)
43
+ rspec-core (~> 3.10.0)
44
+ rspec-expectations (~> 3.10.0)
45
+ rspec-mocks (~> 3.10.0)
46
+ rspec-core (3.10.1)
47
+ rspec-support (~> 3.10.0)
48
+ rspec-expectations (3.10.1)
47
49
  diff-lcs (>= 1.2.0, < 2.0)
48
- rspec-support (~> 3.9.0)
49
- rspec-mocks (3.9.1)
50
+ rspec-support (~> 3.10.0)
51
+ rspec-mocks (3.10.2)
50
52
  diff-lcs (>= 1.2.0, < 2.0)
51
- rspec-support (~> 3.9.0)
52
- rspec-support (3.9.3)
53
- rubocop (0.86.0)
53
+ rspec-support (~> 3.10.0)
54
+ rspec-support (3.10.3)
55
+ rubocop (1.24.1)
54
56
  parallel (~> 1.10)
55
- parser (>= 2.7.0.1)
57
+ parser (>= 3.0.0.0)
56
58
  rainbow (>= 2.2.2, < 4.0)
57
- regexp_parser (>= 1.7)
59
+ regexp_parser (>= 1.8, < 3.0)
58
60
  rexml
59
- rubocop-ast (>= 0.0.3, < 1.0)
61
+ rubocop-ast (>= 1.15.1, < 2.0)
60
62
  ruby-progressbar (~> 1.7)
61
- unicode-display_width (>= 1.4.0, < 2.0)
62
- rubocop-ast (0.1.0)
63
- parser (>= 2.7.0.1)
64
- rubocop-performance (1.6.1)
65
- rubocop (>= 0.71.0)
66
- ruby-progressbar (1.10.1)
67
- unicode-display_width (1.7.0)
63
+ unicode-display_width (>= 1.4.0, < 3.0)
64
+ rubocop-ast (1.15.1)
65
+ parser (>= 3.0.1.1)
66
+ rubocop-bsm (0.6.0)
67
+ rubocop (~> 1.0)
68
+ rubocop-performance
69
+ rubocop-rake
70
+ rubocop-rspec
71
+ rubocop-performance (1.13.1)
72
+ rubocop (>= 1.7.0, < 2.0)
73
+ rubocop-ast (>= 0.4.0)
74
+ rubocop-rake (0.6.0)
75
+ rubocop (~> 1.0)
76
+ rubocop-rspec (2.7.0)
77
+ rubocop (~> 1.19)
78
+ ruby-progressbar (1.11.0)
79
+ unicode-display_width (2.1.0)
68
80
 
69
81
  PLATFORMS
70
82
  ruby
@@ -72,13 +84,11 @@ PLATFORMS
72
84
  DEPENDENCIES
73
85
  bundler
74
86
  feedx!
75
- google-protobuf (>= 3.7.0.pre.rc2)
76
87
  pbio
77
88
  rake
78
- red-parquet
89
+ red-parquet (>= 3.0, < 4.0)
79
90
  rspec
80
- rubocop
81
- rubocop-performance
91
+ rubocop-bsm
82
92
 
83
93
  BUNDLED WITH
84
- 2.1.4
94
+ 2.2.27
data/Makefile CHANGED
@@ -1,12 +1,12 @@
1
- default: vet test
1
+ default: test
2
2
 
3
- test:
4
- go test ./...
3
+ .common.makefile:
4
+ curl -fsSL -o $@ https://gitlab.com/bsm/misc/raw/master/make/go/common.makefile
5
5
 
6
- vet:
7
- go vet ./...
6
+ include .common.makefile
8
7
 
9
8
  proto: internal/testdata/testdata.pb.go
10
9
 
11
10
  %.pb.go: %.proto
12
- protoc -I=. --gogo_out=paths=source_relative:. $<
11
+ # may need to `go install google.golang.org/protobuf/cmd/protoc-gen-go`
12
+ protoc -I=. --go_out=paths=source_relative:. $<
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Feedx
2
2
 
3
- [![Build Status](https://travis-ci.org/bsm/feedx.png?branch=master)](https://travis-ci.org/bsm/feedx)
3
+ [![Test](https://github.com/bsm/feedx/actions/workflows/test.yml/badge.svg)](https://github.com/bsm/feedx/actions/workflows/test.yml)
4
4
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
5
5
 
6
6
  Feed-based data exchange between services.
data/compression.go CHANGED
@@ -1,6 +1,7 @@
1
1
  package feedx
2
2
 
3
3
  import (
4
+ "compress/flate"
4
5
  "compress/gzip"
5
6
  "io"
6
7
  "path"
@@ -20,6 +21,8 @@ func DetectCompression(name string) Compression {
20
21
  ext := path.Ext(path.Base(name))
21
22
  if ext != "" && ext[0] == '.' && ext[len(ext)-1] == 'z' {
22
23
  return GZipCompression
24
+ } else if ext == ".flate" {
25
+ return FlateCompression
23
26
  }
24
27
  }
25
28
  return NoCompression
@@ -61,3 +64,18 @@ func (gzipCompression) NewReader(r io.Reader) (io.ReadCloser, error) {
61
64
  func (gzipCompression) NewWriter(w io.Writer) (io.WriteCloser, error) {
62
65
  return gzip.NewWriter(w), nil
63
66
  }
67
+
68
+ // --------------------------------------------------------------------
69
+
70
+ // FlateCompression supports flate compression format.
71
+ var FlateCompression = flateCompression{}
72
+
73
+ type flateCompression struct{}
74
+
75
+ func (flateCompression) NewReader(r io.Reader) (io.ReadCloser, error) {
76
+ return flate.NewReader(r), nil
77
+ }
78
+
79
+ func (flateCompression) NewWriter(w io.Writer) (io.WriteCloser, error) {
80
+ return flate.NewWriter(w, flate.BestSpeed)
81
+ }
data/compression_test.go CHANGED
@@ -4,8 +4,8 @@ import (
4
4
  "bytes"
5
5
 
6
6
  "github.com/bsm/feedx"
7
- . "github.com/onsi/ginkgo"
8
- . "github.com/onsi/gomega"
7
+ . "github.com/bsm/ginkgo"
8
+ . "github.com/bsm/gomega"
9
9
  )
10
10
 
11
11
  var _ = Describe("Compression", func() {
@@ -32,7 +32,7 @@ var _ = Describe("Compression", func() {
32
32
  Expect(r.Close()).To(Succeed())
33
33
  }
34
34
 
35
- It("should detect the format", func() {
35
+ It("detects the format", func() {
36
36
  Expect(feedx.DetectCompression("/path/to/file.json")).To(Equal(feedx.NoCompression))
37
37
  Expect(feedx.DetectCompression("/path/to/file.json.gz")).To(Equal(feedx.GZipCompression))
38
38
  Expect(feedx.DetectCompression("/path/to/file.jsonz")).To(Equal(feedx.GZipCompression))
@@ -41,6 +41,9 @@ var _ = Describe("Compression", func() {
41
41
  Expect(feedx.DetectCompression("/path/to/file.pb.gz")).To(Equal(feedx.GZipCompression))
42
42
  Expect(feedx.DetectCompression("/path/to/file.pbz")).To(Equal(feedx.GZipCompression))
43
43
 
44
+ Expect(feedx.DetectCompression("/path/to/file.flate")).To(Equal(feedx.FlateCompression))
45
+ Expect(feedx.DetectCompression("/path/to/file.whatever.flate")).To(Equal(feedx.FlateCompression))
46
+
44
47
  Expect(feedx.DetectCompression("")).To(Equal(feedx.NoCompression))
45
48
  Expect(feedx.DetectCompression("/path/to/file")).To(Equal(feedx.NoCompression))
46
49
  Expect(feedx.DetectCompression("/path/to/file.txt")).To(Equal(feedx.NoCompression))
@@ -50,7 +53,7 @@ var _ = Describe("Compression", func() {
50
53
  var subject = feedx.NoCompression
51
54
  var _ feedx.Compression = subject
52
55
 
53
- It("should write/read", func() {
56
+ It("writes/reads", func() {
54
57
  runSharedTest(subject)
55
58
  })
56
59
  })
@@ -59,7 +62,16 @@ var _ = Describe("Compression", func() {
59
62
  var subject = feedx.GZipCompression
60
63
  var _ feedx.Compression = subject
61
64
 
62
- It("should write/read", func() {
65
+ It("writes/reads", func() {
66
+ runSharedTest(subject)
67
+ })
68
+ })
69
+
70
+ Describe("FlateCompression", func() {
71
+ var subject = feedx.FlateCompression
72
+ var _ feedx.Compression = subject
73
+
74
+ It("writes/reads", func() {
63
75
  runSharedTest(subject)
64
76
  })
65
77
  })
data/consumer.go CHANGED
@@ -50,6 +50,8 @@ type Consumer interface {
50
50
  Data() interface{}
51
51
  // LastSync returns time of last sync attempt.
52
52
  LastSync() time.Time
53
+ // LastConsumed returns time of last feed consumption.
54
+ LastConsumed() time.Time
53
55
  // LastModified returns time at which the remote feed was last modified.
54
56
  LastModified() time.Time
55
57
  // NumRead returns the number of values consumed during the last sync.
@@ -114,7 +116,7 @@ type consumer struct {
114
116
  cfn ConsumeFunc
115
117
  data atomic.Value
116
118
 
117
- numRead, lastMod, lastSync int64
119
+ numRead, lastMod, lastSync, lastConsumed int64
118
120
  }
119
121
 
120
122
  // Data implements Consumer interface.
@@ -132,6 +134,11 @@ func (c *consumer) LastSync() time.Time {
132
134
  return timestamp(atomic.LoadInt64(&c.lastSync)).Time()
133
135
  }
134
136
 
137
+ // LastConsumed implements Consumer interface.
138
+ func (c *consumer) LastConsumed() time.Time {
139
+ return timestamp(atomic.LoadInt64(&c.lastConsumed)).Time()
140
+ }
141
+
135
142
  // LastModified implements Consumer interface.
136
143
  func (c *consumer) LastModified() time.Time {
137
144
  return timestamp(atomic.LoadInt64(&c.lastMod)).Time()
@@ -147,8 +154,9 @@ func (c *consumer) Close() error {
147
154
  }
148
155
 
149
156
  func (c *consumer) sync(force bool) (*ConsumerSync, error) {
157
+ syncTime := timestampFromTime(time.Now()).Millis()
150
158
  defer func() {
151
- atomic.StoreInt64(&c.lastSync, timestampFromTime(time.Now()).Millis())
159
+ atomic.StoreInt64(&c.lastSync, syncTime)
152
160
  }()
153
161
 
154
162
  // retrieve original last modified time
@@ -158,7 +166,7 @@ func (c *consumer) sync(force bool) (*ConsumerSync, error) {
158
166
  }
159
167
 
160
168
  // skip update if not forced or modified
161
- if lastMod.Millis() == atomic.LoadInt64(&c.lastMod) && !force {
169
+ if !force && lastMod > 0 && lastMod.Millis() == atomic.LoadInt64(&c.lastMod) {
162
170
  return &ConsumerSync{Consumer: c}, nil
163
171
  }
164
172
 
@@ -180,6 +188,7 @@ func (c *consumer) sync(force bool) (*ConsumerSync, error) {
180
188
  c.data.Store(data)
181
189
  atomic.StoreInt64(&c.numRead, int64(reader.NumRead()))
182
190
  atomic.StoreInt64(&c.lastMod, lastMod.Millis())
191
+ atomic.StoreInt64(&c.lastConsumed, syncTime)
183
192
  return &ConsumerSync{
184
193
  Consumer: c,
185
194
  Updated: true,
data/consumer_test.go CHANGED
@@ -8,8 +8,8 @@ import (
8
8
  "github.com/bsm/bfs"
9
9
  "github.com/bsm/feedx"
10
10
  "github.com/bsm/feedx/internal/testdata"
11
- . "github.com/onsi/ginkgo"
12
- . "github.com/onsi/gomega"
11
+ . "github.com/bsm/ginkgo"
12
+ . "github.com/bsm/gomega"
13
13
  )
14
14
 
15
15
  var _ = Describe("Consumer", func() {
@@ -17,25 +17,26 @@ var _ = Describe("Consumer", func() {
17
17
  var obj *bfs.Object
18
18
  var ctx = context.Background()
19
19
 
20
+ consume := func(r *feedx.Reader) (interface{}, error) {
21
+ var msgs []*testdata.MockMessage
22
+ for {
23
+ var msg testdata.MockMessage
24
+ if err := r.Decode(&msg); err == io.EOF {
25
+ break
26
+ } else if err != nil {
27
+ return nil, err
28
+ }
29
+ msgs = append(msgs, &msg)
30
+ }
31
+ return msgs, nil
32
+ }
33
+
20
34
  BeforeEach(func() {
21
35
  obj = bfs.NewInMemObject("path/to/file.jsonz")
22
- Expect(writeMulti(obj, 2)).To(Succeed())
36
+ Expect(writeMulti(obj, 2, mockTime)).To(Succeed())
23
37
 
24
38
  var err error
25
- subject, err = feedx.NewConsumerForRemote(ctx, obj, nil, func(r *feedx.Reader) (interface{}, error) {
26
- var msgs []*testdata.MockMessage
27
- for {
28
- var msg testdata.MockMessage
29
- if err := r.Decode(&msg); err == io.EOF {
30
- break
31
- }
32
- if err != nil {
33
- return nil, err
34
- }
35
- msgs = append(msgs, &msg)
36
- }
37
- return msgs, nil
38
- })
39
+ subject, err = feedx.NewConsumerForRemote(ctx, obj, nil, consume)
39
40
  Expect(err).NotTo(HaveOccurred())
40
41
  })
41
42
 
@@ -43,11 +44,41 @@ var _ = Describe("Consumer", func() {
43
44
  Expect(subject.Close()).To(Succeed())
44
45
  })
45
46
 
46
- It("should sync and retrieve feeds from remote", func() {
47
+ It("syncs/retrieves feeds from remote", func() {
47
48
  Expect(subject.LastSync()).To(BeTemporally("~", time.Now(), time.Second))
48
- Expect(subject.LastModified()).To(BeTemporally("~", time.Unix(1515151515, 0), time.Second))
49
+ Expect(subject.LastConsumed()).To(BeTemporally("==", subject.LastSync()))
50
+ Expect(subject.LastModified()).To(BeTemporally("==", mockTime.Truncate(time.Millisecond)))
49
51
  Expect(subject.NumRead()).To(Equal(2))
50
52
  Expect(subject.Data()).To(ConsistOf(seed(), seed()))
51
53
  Expect(subject.Close()).To(Succeed())
52
54
  })
55
+
56
+ It("consumes feeds only if necessary", func() {
57
+ prevSync := subject.LastSync()
58
+ time.Sleep(2 * time.Millisecond)
59
+
60
+ testable := subject.(interface{ TestSync() error })
61
+ Expect(testable.TestSync()).To(Succeed())
62
+ Expect(subject.LastSync()).To(BeTemporally(">", prevSync))
63
+ Expect(subject.LastConsumed()).To(BeTemporally("==", prevSync)) // skipped on last sync
64
+ Expect(subject.LastModified()).To(BeTemporally("==", mockTime.Truncate(time.Millisecond)))
65
+ Expect(subject.NumRead()).To(Equal(2))
66
+ })
67
+
68
+ It("always consumes if LastModified not set", func() {
69
+ noModTime := bfs.NewInMemObject("path/to/file.json")
70
+ Expect(writeMulti(noModTime, 2, time.Time{})).To(Succeed())
71
+
72
+ csmr, err := feedx.NewConsumerForRemote(ctx, noModTime, nil, consume)
73
+ Expect(err).NotTo(HaveOccurred())
74
+
75
+ prevSync := csmr.LastSync()
76
+ time.Sleep(2 * time.Millisecond)
77
+
78
+ testable := csmr.(interface{ TestSync() error })
79
+ Expect(testable.TestSync()).To(Succeed())
80
+ Expect(csmr.LastSync()).To(BeTemporally(">", prevSync))
81
+ Expect(csmr.LastConsumed()).To(BeTemporally("==", csmr.LastSync())) // consumed on last sync
82
+ Expect(csmr.LastModified()).To(BeTemporally("==", time.Unix(0, 0)))
83
+ })
53
84
  })
@@ -0,0 +1,59 @@
1
+ package parquet
2
+
3
+ import (
4
+ "io"
5
+
6
+ goparquet "github.com/fraugster/parquet-go"
7
+ "github.com/fraugster/parquet-go/floor"
8
+ )
9
+
10
+ type decoder struct {
11
+ pfr *goparquet.FileReader
12
+ ffr *floor.Reader
13
+ tmp *tempFile
14
+ }
15
+
16
+ func newDecoder(rs io.ReadSeeker) (*decoder, error) {
17
+ pfr, err := goparquet.NewFileReader(rs)
18
+ if err != nil {
19
+ return nil, err
20
+ }
21
+
22
+ ffr := floor.NewReader(pfr)
23
+
24
+ return &decoder{
25
+ pfr: pfr,
26
+ ffr: ffr,
27
+ },
28
+ nil
29
+ }
30
+
31
+ func (w *decoder) Decode(v interface{}) error {
32
+ // read the next value and scan
33
+ if w.ffr.Next() {
34
+ return w.ffr.Scan(v)
35
+ }
36
+
37
+ // check for errors
38
+ if err := w.ffr.Err(); err != nil {
39
+ return err
40
+ }
41
+
42
+ // end of file
43
+ return io.EOF
44
+ }
45
+
46
+ func (w *decoder) Close() (err error) {
47
+ // close the tmp file if present
48
+ if w.tmp != nil {
49
+ if e := w.tmp.Close(); e != nil {
50
+ err = e
51
+ }
52
+ }
53
+
54
+ // close the reader
55
+ if e := w.ffr.Close(); e != nil {
56
+ err = e
57
+ }
58
+ return
59
+ }