feedx 0.12.2 → 0.12.7

Sign up to get free protection for your applications and to get access to all the features.
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
+ }