feedx 0.3.1 → 0.3.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c601b31c975bf931998f4c498b86f4e0308be42d0064b9d138d64dc58f2ddb4e
4
- data.tar.gz: ab223f06c6b9f9bf6c5b000f1268ded7a86e10e36715d43628bb19d08ac7809f
3
+ metadata.gz: 7d1c12376b32188b646923935967905bb97ab8c86dde28236a0341ee878eb631
4
+ data.tar.gz: 3c76d4356a0f6aafcad0e8ea37a2159f8170f5d1cea4072753e3c9947afd7613
5
5
  SHA512:
6
- metadata.gz: e5200ae2328cb6353f1262639aca392cc09f18e30f9023c8db9da5e81e7236cb00bb07b543b660497d7c98d5368dfe24f8dbc410ac5234ef82ce270d0cc253d9
7
- data.tar.gz: 940fe6bf7295e7b24dc71cea752fa2850ed1fc0dac736559f8289f93704690393ca81e216d222385eb33e5674709d06c82ff108ee10e8dad8b6bf6f57e97ed51
6
+ metadata.gz: d55b8ee3620301c3ffc5c95d2895d00f54ee562250c4aa8236ed93fd99e552cd67c126feab4a17b063415518e46b113ef918491b19e37043b5d36fa9e09c2808
7
+ data.tar.gz: b8eee064c4e12e462097087431cdd9b435720eead2d7524c9cc7d44cb4a7dde410756f443b71cd5b24261d3b06c95f1d3975ddca6b6ff69692e0e9a77a742446
data/.travis.yml CHANGED
@@ -1,4 +1,13 @@
1
- language: ruby
2
- rvm:
3
- - 2.5
4
- - 2.4
1
+ matrix:
2
+ include:
3
+ - language: ruby
4
+ rvm:
5
+ - 2.5
6
+ - language: ruby
7
+ rvm:
8
+ - 2.4
9
+ - language: go
10
+ go:
11
+ - 1.11.x
12
+ env:
13
+ - GO111MODULE=on
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- feedx (0.3.1)
4
+ feedx (0.3.2)
5
5
  bfs (>= 0.3.4)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  ast (2.4.0)
11
- bfs (0.3.4)
11
+ bfs (0.3.6)
12
12
  diff-lcs (1.3)
13
13
  google-protobuf (3.6.1)
14
14
  jaro_winkler (1.5.1)
@@ -19,7 +19,7 @@ GEM
19
19
  google-protobuf
20
20
  powerpack (0.1.2)
21
21
  rainbow (3.0.0)
22
- rake (12.3.1)
22
+ rake (12.3.2)
23
23
  rspec (3.8.0)
24
24
  rspec-core (~> 3.8.0)
25
25
  rspec-expectations (~> 3.8.0)
@@ -33,7 +33,7 @@ GEM
33
33
  diff-lcs (>= 1.2.0, < 2.0)
34
34
  rspec-support (~> 3.8.0)
35
35
  rspec-support (3.8.0)
36
- rubocop (0.60.0)
36
+ rubocop (0.61.1)
37
37
  jaro_winkler (~> 1.5.1)
38
38
  parallel (~> 1.10)
39
39
  parser (>= 2.5, != 2.5.1.1)
@@ -56,4 +56,4 @@ DEPENDENCIES
56
56
  rubocop
57
57
 
58
58
  BUNDLED WITH
59
- 1.16.4
59
+ 1.17.2
data/Makefile ADDED
@@ -0,0 +1,7 @@
1
+ default: vet test
2
+
3
+ test:
4
+ go test ./...
5
+
6
+ vet:
7
+ go vet ./...
data/compression.go ADDED
@@ -0,0 +1,63 @@
1
+ package feedx
2
+
3
+ import (
4
+ "compress/gzip"
5
+ "io"
6
+ "path"
7
+ )
8
+
9
+ // Compression represents the data compression.
10
+ type Compression interface {
11
+ // NewReader wraps a reader.
12
+ NewReader(io.Reader) (io.ReadCloser, error)
13
+ // NewWriter wraps a writer.
14
+ NewWriter(io.Writer) (io.WriteCloser, error)
15
+ }
16
+
17
+ // DetectCompression detects the compression type from a URL path or file name.
18
+ func DetectCompression(name string) Compression {
19
+ if name != "" {
20
+ ext := path.Ext(path.Base(name))
21
+ if ext != "" && ext[0] == '.' && ext[len(ext)-1] == 'z' {
22
+ return GZipCompression
23
+ }
24
+ }
25
+ return NoCompression
26
+ }
27
+
28
+ // --------------------------------------------------------------------
29
+
30
+ // NoCompression is just a pass-through without compression.
31
+ var NoCompression = noCompression{}
32
+
33
+ type noCompression struct{}
34
+
35
+ func (noCompression) NewReader(r io.Reader) (io.ReadCloser, error) {
36
+ return noCompressionWrapper{Reader: r}, nil
37
+ }
38
+
39
+ func (noCompression) NewWriter(w io.Writer) (io.WriteCloser, error) {
40
+ return noCompressionWrapper{Writer: w}, nil
41
+ }
42
+
43
+ type noCompressionWrapper struct {
44
+ io.Reader
45
+ io.Writer
46
+ }
47
+
48
+ func (noCompressionWrapper) Close() error { return nil }
49
+
50
+ // --------------------------------------------------------------------
51
+
52
+ // GZipCompression supports gzip compression format.
53
+ var GZipCompression = gzipCompression{}
54
+
55
+ type gzipCompression struct{}
56
+
57
+ func (gzipCompression) NewReader(r io.Reader) (io.ReadCloser, error) {
58
+ return gzip.NewReader(r)
59
+ }
60
+
61
+ func (gzipCompression) NewWriter(w io.Writer) (io.WriteCloser, error) {
62
+ return gzip.NewWriter(w), nil
63
+ }
@@ -0,0 +1,66 @@
1
+ package feedx_test
2
+
3
+ import (
4
+ "bytes"
5
+
6
+ "github.com/bsm/feedx"
7
+ . "github.com/onsi/ginkgo"
8
+ . "github.com/onsi/gomega"
9
+ )
10
+
11
+ var _ = Describe("Compression", func() {
12
+ var data = bytes.Repeat([]byte("wxyz"), 1024)
13
+
14
+ runSharedTest := func(subject feedx.Compression) {
15
+ buf := new(bytes.Buffer)
16
+
17
+ w, err := subject.NewWriter(buf)
18
+ Expect(err).NotTo(HaveOccurred())
19
+ defer w.Close()
20
+
21
+ Expect(w.Write(data)).To(Equal(4096))
22
+ Expect(w.Write(data)).To(Equal(4096))
23
+ Expect(w.Close()).To(Succeed())
24
+
25
+ r, err := subject.NewReader(buf)
26
+ Expect(err).NotTo(HaveOccurred())
27
+ defer r.Close()
28
+
29
+ p := make([]byte, 20)
30
+ Expect(r.Read(p)).To(Equal(20))
31
+ Expect(string(p)).To(Equal("wxyzwxyzwxyzwxyzwxyz"))
32
+ Expect(r.Close()).To(Succeed())
33
+ }
34
+
35
+ It("should detect the format", func() {
36
+ Expect(feedx.DetectCompression("/path/to/file.json")).To(Equal(feedx.NoCompression))
37
+ Expect(feedx.DetectCompression("/path/to/file.json.gz")).To(Equal(feedx.GZipCompression))
38
+ Expect(feedx.DetectCompression("/path/to/file.jsonz")).To(Equal(feedx.GZipCompression))
39
+
40
+ Expect(feedx.DetectCompression("/path/to/file.pb")).To(Equal(feedx.NoCompression))
41
+ Expect(feedx.DetectCompression("/path/to/file.pb.gz")).To(Equal(feedx.GZipCompression))
42
+ Expect(feedx.DetectCompression("/path/to/file.pbz")).To(Equal(feedx.GZipCompression))
43
+
44
+ Expect(feedx.DetectCompression("")).To(Equal(feedx.NoCompression))
45
+ Expect(feedx.DetectCompression("/path/to/file")).To(Equal(feedx.NoCompression))
46
+ Expect(feedx.DetectCompression("/path/to/file.txt")).To(Equal(feedx.NoCompression))
47
+ })
48
+
49
+ Describe("NoCompression", func() {
50
+ var subject = feedx.NoCompression
51
+ var _ feedx.Compression = subject
52
+
53
+ It("should write/read", func() {
54
+ runSharedTest(subject)
55
+ })
56
+ })
57
+
58
+ Describe("GZipCompression", func() {
59
+ var subject = feedx.GZipCompression
60
+ var _ feedx.Compression = subject
61
+
62
+ It("should write/read", func() {
63
+ runSharedTest(subject)
64
+ })
65
+ })
66
+ })
data/consumer.go ADDED
@@ -0,0 +1,206 @@
1
+ package feedx
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "strconv"
7
+ "sync/atomic"
8
+ "time"
9
+
10
+ "github.com/bsm/bfs"
11
+ )
12
+
13
+ // ConsumerOptions configure the Puller instance.
14
+ type ConsumerOptions struct {
15
+ // The interval used by Puller to check the remote changes.
16
+ // Default: 1m
17
+ Interval time.Duration
18
+
19
+ // Format specifies the format
20
+ // Default: auto-detected from URL path.
21
+ Format Format
22
+
23
+ // Compression specifies the compression type.
24
+ // Default: auto-detected from URL path.
25
+ Compression Compression
26
+
27
+ // AfterSync callbacks are triggered after each sync, receiving
28
+ // the updated status and error (if occurred).
29
+ AfterSync func(updated bool, err error)
30
+ }
31
+
32
+ func (o *ConsumerOptions) norm(name string) error {
33
+ if o.Interval <= 0 {
34
+ o.Interval = time.Minute
35
+ }
36
+ if o.Format == nil {
37
+ o.Format = DetectFormat(name)
38
+
39
+ if o.Format == nil {
40
+ return fmt.Errorf("feedx: unable to detect format from %q", name)
41
+ }
42
+ }
43
+ if o.Compression == nil {
44
+ o.Compression = DetectCompression(name)
45
+ }
46
+ return nil
47
+ }
48
+
49
+ // ParseFunc is a data parse function.
50
+ type ParseFunc func(FormatDecoder) (data interface{}, size int64, err error)
51
+
52
+ // Consumer manages data retrieval from a remote feed.
53
+ // It queries the feed in regular intervals, continuously retrieving new updates.
54
+ type Consumer interface {
55
+ // Data returns the data as returned by ParseFunc on last sync.
56
+ Data() interface{}
57
+ // LastCheck returns time of last sync attempt.
58
+ LastCheck() time.Time
59
+ // LastModified returns time at which the remote feed was last modified.
60
+ LastModified() time.Time
61
+ // Size returns the size as returned by ParseFunc on last sync.
62
+ Size() int64
63
+ // Close stops the underlying sync process.
64
+ Close() error
65
+ }
66
+
67
+ // NewConsumer starts a new feed consumer.
68
+ func NewConsumer(ctx context.Context, srcURL string, opt *ConsumerOptions, parse ParseFunc) (Consumer, error) {
69
+ src, err := bfs.NewObject(ctx, srcURL)
70
+ if err != nil {
71
+ return nil, err
72
+ }
73
+
74
+ var o ConsumerOptions
75
+ if opt != nil {
76
+ o = *opt
77
+ }
78
+ if err := o.norm(src.Name()); err != nil {
79
+ _ = src.Close()
80
+ return nil, err
81
+ }
82
+
83
+ ctx, stop := context.WithCancel(ctx)
84
+ f := &consumer{
85
+ src: src,
86
+ opt: o,
87
+ ctx: ctx,
88
+ stop: stop,
89
+ parse: parse,
90
+ }
91
+
92
+ // run initial sync
93
+ if _, err := f.sync(true); err != nil {
94
+ _ = f.Close()
95
+ return nil, err
96
+ }
97
+
98
+ // start continuous loop
99
+ go f.loop()
100
+
101
+ return f, nil
102
+ }
103
+
104
+ type consumer struct {
105
+ src *bfs.Object
106
+ opt ConsumerOptions
107
+ ctx context.Context
108
+ stop context.CancelFunc
109
+
110
+ parse ParseFunc
111
+
112
+ size, lastModMs int64
113
+ data, lastCheck atomic.Value
114
+ }
115
+
116
+ // Data implements Feed interface.
117
+ func (f *consumer) Data() interface{} {
118
+ return f.data.Load()
119
+ }
120
+
121
+ // Size implements Feed interface.
122
+ func (f *consumer) Size() int64 {
123
+ return atomic.LoadInt64(&f.size)
124
+ }
125
+
126
+ // LastCheck implements Feed interface.
127
+ func (f *consumer) LastCheck() time.Time {
128
+ return f.lastCheck.Load().(time.Time)
129
+ }
130
+
131
+ // LastModified implements Feed interface.
132
+ func (f *consumer) LastModified() time.Time {
133
+ msec := atomic.LoadInt64(&f.lastModMs)
134
+ return time.Unix(msec/1000, msec%1000*1e6)
135
+ }
136
+
137
+ // Close implements Feed interface.
138
+ func (f *consumer) Close() error {
139
+ f.stop()
140
+ return f.src.Close()
141
+ }
142
+
143
+ func (f *consumer) sync(force bool) (bool, error) {
144
+ f.lastCheck.Store(time.Now())
145
+
146
+ info, err := f.src.Head(f.ctx)
147
+ if err != nil {
148
+ return false, err
149
+ }
150
+
151
+ // calculate last modified time
152
+ msec, _ := strconv.ParseInt(info.Metadata[lastModifiedMetaKey], 10, 64)
153
+
154
+ // skip update if not forced or modified
155
+ if msec == atomic.LoadInt64(&f.lastModMs) && !force {
156
+ return false, nil
157
+ }
158
+
159
+ // open remote for reading
160
+ r, err := f.src.Open(f.ctx)
161
+ if err != nil {
162
+ return false, err
163
+ }
164
+ defer r.Close()
165
+
166
+ // wrap in compressed reader
167
+ c, err := f.opt.Compression.NewReader(r)
168
+ if err != nil {
169
+ return false, err
170
+ }
171
+ defer c.Close()
172
+
173
+ // open decoder
174
+ d, err := f.opt.Format.NewDecoder(c)
175
+ if err != nil {
176
+ return false, err
177
+ }
178
+ defer f.Close()
179
+
180
+ // parse feed
181
+ data, size, err := f.parse(d)
182
+ if err != nil {
183
+ return false, err
184
+ }
185
+
186
+ // update stores
187
+ f.data.Store(data)
188
+ atomic.StoreInt64(&f.size, size)
189
+ atomic.StoreInt64(&f.lastModMs, msec)
190
+ return true, nil
191
+ }
192
+
193
+ func (f *consumer) loop() {
194
+ ticker := time.NewTicker(f.opt.Interval)
195
+ defer ticker.Stop()
196
+
197
+ for {
198
+ select {
199
+ case <-f.ctx.Done():
200
+ return
201
+ case <-ticker.C:
202
+ updated, err := f.sync(false)
203
+ f.opt.AfterSync(updated, err)
204
+ }
205
+ }
206
+ }
data/consumer_test.go ADDED
@@ -0,0 +1,70 @@
1
+ package feedx_test
2
+
3
+ import (
4
+ "context"
5
+ "io"
6
+ "time"
7
+
8
+ "github.com/bsm/bfs"
9
+ "github.com/bsm/feedx"
10
+ tbp "github.com/golang/protobuf/proto/proto3_proto"
11
+ . "github.com/onsi/ginkgo"
12
+ . "github.com/onsi/gomega"
13
+ )
14
+
15
+ var _ = Describe("Consumer", func() {
16
+ ctx := context.Background()
17
+ msg := &tbp.Message{
18
+ Name: "Joe",
19
+ TrueScotsman: true,
20
+ Hilarity: tbp.Message_BILL_BAILEY,
21
+ }
22
+ pfn := func(dec feedx.FormatDecoder) (interface{}, int64, error) {
23
+ var msgs []*tbp.Message
24
+ for {
25
+ msg := new(tbp.Message)
26
+ if err := dec.Decode(msg); err == io.EOF {
27
+ break
28
+ } else if err != nil {
29
+ return nil, 0, err
30
+ }
31
+ msgs = append(msgs, msg)
32
+ }
33
+ return msgs, int64(len(msgs)), nil
34
+ }
35
+
36
+ BeforeEach(func() {
37
+ memStore = bfs.NewInMem()
38
+ w, err := memStore.Create(ctx, "path/to/file.jsonz", &bfs.WriteOptions{
39
+ Metadata: map[string]string{"x-feedx-pusher-last-modified": "1544477788899"},
40
+ })
41
+ Expect(err).NotTo(HaveOccurred())
42
+ defer w.Close()
43
+
44
+ c, err := feedx.GZipCompression.NewWriter(w)
45
+ Expect(err).NotTo(HaveOccurred())
46
+ defer c.Close()
47
+
48
+ f, err := feedx.JSONFormat.NewEncoder(c)
49
+ Expect(err).NotTo(HaveOccurred())
50
+ defer f.Close()
51
+
52
+ Expect(f.Encode(msg)).To(Succeed())
53
+ Expect(f.Encode(msg)).To(Succeed())
54
+ Expect(f.Close()).To(Succeed())
55
+ Expect(c.Close()).To(Succeed())
56
+ Expect(w.Close()).To(Succeed())
57
+ })
58
+
59
+ It("should sync and retrieve feeds from remote", func() {
60
+ subject, err := feedx.NewConsumer(ctx, "mem:///path/to/file.jsonz", nil, pfn)
61
+ Expect(err).NotTo(HaveOccurred())
62
+ defer subject.Close()
63
+
64
+ Expect(subject.LastCheck()).To(BeTemporally("~", time.Now(), time.Second))
65
+ Expect(subject.LastModified()).To(BeTemporally("~", time.Unix(1544477788, 0), time.Second))
66
+ Expect(subject.Size()).To(Equal(int64(2)))
67
+ Expect(subject.Data()).To(Equal([]*tbp.Message{msg, msg}))
68
+ Expect(subject.Close()).To(Succeed())
69
+ })
70
+ })
data/feedx.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'feedx'
3
- s.version = '0.3.1'
3
+ s.version = '0.3.2'
4
4
  s.authors = ['Black Square Media Ltd']
5
5
  s.email = ['info@blacksquaremedia.com']
6
6
  s.summary = %(Exchange data between components via feeds)
data/feedx.go ADDED
@@ -0,0 +1,3 @@
1
+ package feedx
2
+
3
+ const lastModifiedMetaKey = "x-feedx-pusher-last-modified"
data/feedx_test.go ADDED
@@ -0,0 +1,27 @@
1
+ package feedx_test
2
+
3
+ import (
4
+ "context"
5
+ "net/url"
6
+ "testing"
7
+
8
+ "github.com/bsm/bfs"
9
+ . "github.com/onsi/ginkgo"
10
+ . "github.com/onsi/gomega"
11
+ )
12
+
13
+ // ------------------------------------------------------------------------
14
+
15
+ var memStore *bfs.InMem
16
+
17
+ func init() {
18
+ memStore = bfs.NewInMem()
19
+ bfs.Register("mem", func(_ context.Context, u *url.URL) (bfs.Bucket, error) {
20
+ return memStore, nil
21
+ })
22
+ }
23
+
24
+ func TestSuite(t *testing.T) {
25
+ RegisterFailHandler(Fail)
26
+ RunSpecs(t, "feedx")
27
+ }
data/format.go ADDED
@@ -0,0 +1,122 @@
1
+ package feedx
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "io"
7
+ "path"
8
+
9
+ "github.com/golang/protobuf/proto"
10
+
11
+ pbio "github.com/gogo/protobuf/io"
12
+ )
13
+
14
+ // Format represents the data format.
15
+ type Format interface {
16
+ // NewDecoder wraps a decoder around a reader.
17
+ NewDecoder(io.Reader) (FormatDecoder, error)
18
+ // NewEncoder wraps an encoder around a writer.
19
+ NewEncoder(io.Writer) (FormatEncoder, error)
20
+ }
21
+
22
+ // DetectFormat detects the data format from a URL path or file name.
23
+ // May return nil.
24
+ func DetectFormat(name string) Format {
25
+ ext := path.Ext(path.Base(name))
26
+ switch ext {
27
+ case ".json":
28
+ return JSONFormat
29
+ case ".pb", ".proto", ".protobuf":
30
+ return ProtobufFormat
31
+ default:
32
+ if name != "" && ext != "" && ext[0] == '.' {
33
+ if ext[len(ext)-1] == 'z' {
34
+ return DetectFormat(name[0 : len(name)-1])
35
+ }
36
+ return DetectFormat(name[0 : len(name)-len(ext)])
37
+ }
38
+ }
39
+ return nil
40
+ }
41
+
42
+ // FormatDecoder methods
43
+ type FormatDecoder interface {
44
+ // Decode decodes the next message into an interface.
45
+ Decode(v interface{}) error
46
+
47
+ io.Closer
48
+ }
49
+
50
+ // FormatEncoder methods
51
+ type FormatEncoder interface {
52
+ // Encode encodes the value to the stream.
53
+ Encode(v interface{}) error
54
+
55
+ io.Closer
56
+ }
57
+
58
+ // --------------------------------------------------------------------
59
+
60
+ // JSONFormat provides a Format implemention for JSON.
61
+ var JSONFormat = jsonFormat{}
62
+
63
+ type jsonFormat struct{}
64
+
65
+ // NewDecoder implements Format.
66
+ func (jsonFormat) NewDecoder(r io.Reader) (FormatDecoder, error) {
67
+ return jsonDecoderWrapper{Decoder: json.NewDecoder(r)}, nil
68
+ }
69
+
70
+ // NewEncoder implements Format.
71
+ func (jsonFormat) NewEncoder(w io.Writer) (FormatEncoder, error) {
72
+ return jsonEncoderWrapper{Encoder: json.NewEncoder(w)}, nil
73
+ }
74
+
75
+ type jsonDecoderWrapper struct{ *json.Decoder }
76
+
77
+ func (jsonDecoderWrapper) Close() error { return nil }
78
+
79
+ type jsonEncoderWrapper struct{ *json.Encoder }
80
+
81
+ func (jsonEncoderWrapper) Close() error { return nil }
82
+
83
+ // --------------------------------------------------------------------
84
+
85
+ const protobufMaxMessageSize = 20 * 1024 * 1024 // 20MB
86
+
87
+ // ProtobufFormat provides a Format implemention for Protobuf.
88
+ var ProtobufFormat = protobufFormat{}
89
+
90
+ type protobufFormat struct{}
91
+
92
+ // NewDecoder implements Format.
93
+ func (protobufFormat) NewDecoder(r io.Reader) (FormatDecoder, error) {
94
+ rc := pbio.NewDelimitedReader(r, protobufMaxMessageSize)
95
+ return protobufDecoderWrapper{ReadCloser: rc}, nil
96
+ }
97
+
98
+ // NewEncoder implements Format.
99
+ func (protobufFormat) NewEncoder(w io.Writer) (FormatEncoder, error) {
100
+ wc := pbio.NewDelimitedWriter(w)
101
+ return protobufEncoderWrapper{WriteCloser: wc}, nil
102
+ }
103
+
104
+ type protobufDecoderWrapper struct{ pbio.ReadCloser }
105
+
106
+ func (w protobufDecoderWrapper) Decode(v interface{}) error {
107
+ msg, ok := v.(proto.Message)
108
+ if !ok {
109
+ return fmt.Errorf("feedx: value %v is not a proto.Message", v)
110
+ }
111
+ return w.ReadCloser.ReadMsg(msg)
112
+ }
113
+
114
+ type protobufEncoderWrapper struct{ pbio.WriteCloser }
115
+
116
+ func (w protobufEncoderWrapper) Encode(v interface{}) error {
117
+ msg, ok := v.(proto.Message)
118
+ if !ok {
119
+ return fmt.Errorf("feedx: value %v is not a proto.Message", v)
120
+ }
121
+ return w.WriteMsg(msg)
122
+ }
data/format_test.go ADDED
@@ -0,0 +1,80 @@
1
+ package feedx_test
2
+
3
+ import (
4
+ "bytes"
5
+ "io"
6
+
7
+ "github.com/bsm/feedx"
8
+ tbp "github.com/golang/protobuf/proto/proto3_proto"
9
+ . "github.com/onsi/ginkgo"
10
+ . "github.com/onsi/gomega"
11
+ )
12
+
13
+ var _ = Describe("Format", func() {
14
+ msg := &tbp.Message{
15
+ Name: "Joe",
16
+ TrueScotsman: true,
17
+ Hilarity: tbp.Message_BILL_BAILEY,
18
+ }
19
+
20
+ runSharedTest := func(subject feedx.Format) {
21
+ buf := new(bytes.Buffer)
22
+
23
+ enc, err := subject.NewEncoder(buf)
24
+ Expect(err).NotTo(HaveOccurred())
25
+ defer enc.Close()
26
+
27
+ Expect(enc.Encode(msg)).To(Succeed())
28
+ Expect(enc.Encode(msg)).To(Succeed())
29
+ Expect(enc.Close()).To(Succeed())
30
+
31
+ dec, err := subject.NewDecoder(buf)
32
+ Expect(err).NotTo(HaveOccurred())
33
+ defer dec.Close()
34
+
35
+ v1 := new(tbp.Message)
36
+ Expect(dec.Decode(v1)).To(Succeed())
37
+ Expect(v1.Name).To(Equal("Joe"))
38
+
39
+ v2 := new(tbp.Message)
40
+ Expect(dec.Decode(v2)).To(Succeed())
41
+ Expect(v2.Name).To(Equal("Joe"))
42
+
43
+ v3 := new(tbp.Message)
44
+ Expect(dec.Decode(v3)).To(MatchError(io.EOF))
45
+
46
+ Expect(dec.Close()).To(Succeed())
47
+ }
48
+
49
+ It("should detect the format", func() {
50
+ Expect(feedx.DetectFormat("/path/to/file.json")).To(Equal(feedx.JSONFormat))
51
+ Expect(feedx.DetectFormat("/path/to/file.json.gz")).To(Equal(feedx.JSONFormat))
52
+ Expect(feedx.DetectFormat("/path/to/file.jsonz")).To(Equal(feedx.JSONFormat))
53
+
54
+ Expect(feedx.DetectFormat("/path/to/file.pb")).To(Equal(feedx.ProtobufFormat))
55
+ Expect(feedx.DetectFormat("/path/to/file.pb.gz")).To(Equal(feedx.ProtobufFormat))
56
+ Expect(feedx.DetectFormat("/path/to/file.pbz")).To(Equal(feedx.ProtobufFormat))
57
+
58
+ Expect(feedx.DetectFormat("")).To(BeNil())
59
+ Expect(feedx.DetectFormat("/path/to/file")).To(BeNil())
60
+ Expect(feedx.DetectFormat("/path/to/file.txt")).To(BeNil())
61
+ })
62
+
63
+ Describe("JSONFormat", func() {
64
+ var subject = feedx.JSONFormat
65
+ var _ feedx.Format = subject
66
+
67
+ It("should encode/decode", func() {
68
+ runSharedTest(subject)
69
+ })
70
+ })
71
+
72
+ Describe("ProtobufFormat", func() {
73
+ var subject = feedx.ProtobufFormat
74
+ var _ feedx.Format = subject
75
+
76
+ It("should encode/decode", func() {
77
+ runSharedTest(subject)
78
+ })
79
+ })
80
+ })
data/go.mod ADDED
@@ -0,0 +1,13 @@
1
+ module github.com/bsm/feedx
2
+
3
+ require (
4
+ github.com/bsm/bfs v0.4.0
5
+ github.com/gogo/protobuf v1.2.0
6
+ github.com/golang/protobuf v1.2.0
7
+ github.com/kr/pretty v0.1.0 // indirect
8
+ github.com/onsi/ginkgo v1.7.0
9
+ github.com/onsi/gomega v1.4.3
10
+ golang.org/x/net v0.0.0-20181213202711-891ebc4b82d6 // indirect
11
+ golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 // indirect
12
+ gopkg.in/yaml.v2 v2.2.2 // indirect
13
+ )
data/go.sum ADDED
@@ -0,0 +1,111 @@
1
+ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2
+ cloud.google.com/go v0.32.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3
+ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
4
+ git.apache.org/thrift.git v0.0.0-20181106172052-f7d43ce0aa58/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
5
+ github.com/aws/aws-sdk-go v1.15.71/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
6
+ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
7
+ github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ=
8
+ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
9
+ github.com/bsm/bfs v0.4.0 h1:+fT6iDNt1ytYVbSA1PCJFQez8j+DuO53D38wy39ol1g=
10
+ github.com/bsm/bfs v0.4.0/go.mod h1:b96pNJ2nox+3JhdQL2oaVrzSsTpd1pEL+GgQuWjxIO0=
11
+ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
12
+ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
13
+ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
14
+ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
15
+ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
16
+ github.com/go-ini/ini v1.39.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
17
+ github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
18
+ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
19
+ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
20
+ github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
21
+ github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
22
+ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
23
+ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
24
+ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
25
+ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
26
+ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
27
+ github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
28
+ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
29
+ github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
30
+ github.com/grpc-ecosystem/grpc-gateway v1.5.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
31
+ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
32
+ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
33
+ github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
34
+ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
35
+ github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
36
+ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
37
+ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
38
+ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
39
+ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
40
+ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
41
+ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
42
+ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
43
+ github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
44
+ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
45
+ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
46
+ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
47
+ github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
48
+ github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
49
+ github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
50
+ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
51
+ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
52
+ github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
53
+ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
54
+ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
55
+ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
56
+ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
57
+ github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
58
+ github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
59
+ github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
60
+ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
61
+ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
62
+ github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
63
+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
64
+ go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
65
+ golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
66
+ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
67
+ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
68
+ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
69
+ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
70
+ golang.org/x/net v0.0.0-20181108082009-03003ca0c849 h1:FSqE2GGG7wzsYUsWiQ8MZrvEd1EOyU3NCF0AW3Wtltg=
71
+ golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
72
+ golang.org/x/net v0.0.0-20181213202711-891ebc4b82d6 h1:gT0Y6H7hbVPUtvtk0YGxMXPgN+p8fYlqWkgJeUCZcaQ=
73
+ golang.org/x/net v0.0.0-20181213202711-891ebc4b82d6/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
74
+ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
75
+ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
76
+ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
77
+ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
78
+ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
79
+ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
80
+ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
81
+ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8 h1:YoY1wS6JYVRpIfFngRf2HHo9R9dAne3xbkGOQ5rJXjU=
82
+ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
83
+ golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM=
84
+ golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
85
+ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
86
+ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
87
+ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
88
+ golang.org/x/tools v0.0.0-20181107225058-a28dfb48e06b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
89
+ google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
90
+ google.golang.org/api v0.0.0-20181108001712-cfbc873f6b93/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
91
+ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
92
+ google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
93
+ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
94
+ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
95
+ google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
96
+ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
97
+ google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
98
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
99
+ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
100
+ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
101
+ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
102
+ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
103
+ gopkg.in/ini.v1 v1.39.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
104
+ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
105
+ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
106
+ gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
107
+ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
108
+ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
109
+ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
110
+ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
111
+ honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feedx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Black Square Media Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-29 00:00:00.000000000 Z
11
+ date: 2018-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bfs
@@ -108,9 +108,20 @@ files:
108
108
  - Gemfile
109
109
  - Gemfile.lock
110
110
  - LICENSE
111
+ - Makefile
111
112
  - README.md
112
113
  - Rakefile
114
+ - compression.go
115
+ - compression_test.go
116
+ - consumer.go
117
+ - consumer_test.go
113
118
  - feedx.gemspec
119
+ - feedx.go
120
+ - feedx_test.go
121
+ - format.go
122
+ - format_test.go
123
+ - go.mod
124
+ - go.sum
114
125
  - lib/feedx.rb
115
126
  - lib/feedx/compression.rb
116
127
  - lib/feedx/compression/abstract.rb
@@ -149,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
160
  version: '0'
150
161
  requirements: []
151
162
  rubyforge_project:
152
- rubygems_version: 2.7.6
163
+ rubygems_version: 2.7.7
153
164
  signing_key:
154
165
  specification_version: 4
155
166
  summary: Exchange data between components via feeds