feedx 0.6.3 → 0.7.0
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 +4 -4
- data/Gemfile.lock +2 -2
- data/feedx.gemspec +1 -1
- data/lib/feedx.rb +1 -0
- data/lib/feedx/compression/abstract.rb +5 -1
- data/lib/feedx/compression/gzip.rb +5 -1
- data/lib/feedx/compression/none.rb +5 -1
- data/lib/feedx/format/abstract.rb +9 -1
- data/lib/feedx/format/json.rb +10 -1
- data/lib/feedx/format/protobuf.rb +7 -2
- data/lib/feedx/producer.rb +6 -43
- data/lib/feedx/stream.rb +75 -0
- data/spec/feedx/compression/gzip_spec.rb +10 -4
- data/spec/feedx/compression/none_spec.rb +10 -4
- data/spec/feedx/format/json_spec.rb +20 -6
- data/spec/feedx/format/protobuf_spec.rb +15 -13
- data/spec/feedx/producer_spec.rb +1 -13
- data/spec/feedx/stream_spec.rb +72 -0
- data/spec/spec_helper.rb +26 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b306ae446c9cae375b6ad538cc41d88a621bbb5cb59d9adf5aa114326567b2ca
|
4
|
+
data.tar.gz: 218b0aabdf6d1dda76e520b226f7d211a27ba14421db4ed8e34a9f59451e1e20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 246d39971aa5d6fe96267b95fba832f90d24ae8c8a16c4ea949fb3bca3f8777fd036e311f8d5f97b7cfed301b3782c2035e4e4ba1b62b7373e1f1e7d090ab00d
|
7
|
+
data.tar.gz: a151e2c9cde8820505912ceca55afa58f3a44cb25581eda9b66f830e057fe1254093c93ad5be77848fa8e3b0033d14885cb4a412bb4308bf0109b2bb57a47771
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
feedx (0.
|
4
|
+
feedx (0.7.0)
|
5
5
|
bfs (>= 0.3.4)
|
6
6
|
|
7
7
|
GEM
|
@@ -32,7 +32,7 @@ GEM
|
|
32
32
|
diff-lcs (>= 1.2.0, < 2.0)
|
33
33
|
rspec-support (~> 3.8.0)
|
34
34
|
rspec-support (3.8.0)
|
35
|
-
rubocop (0.
|
35
|
+
rubocop (0.70.0)
|
36
36
|
jaro_winkler (~> 1.5.1)
|
37
37
|
parallel (~> 1.10)
|
38
38
|
parser (>= 2.6)
|
data/feedx.gemspec
CHANGED
data/lib/feedx.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'zlib'
|
2
2
|
|
3
3
|
class Feedx::Compression::Gzip < Feedx::Compression::Abstract
|
4
|
-
def self.
|
4
|
+
def self.reader(io, &block)
|
5
|
+
Zlib::GzipReader.wrap(io, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.writer(io, &block)
|
5
9
|
Zlib::GzipWriter.wrap(io, &block)
|
6
10
|
end
|
7
11
|
end
|
data/lib/feedx/format/json.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
3
|
class Feedx::Format::JSON < Feedx::Format::Abstract
|
4
|
-
def
|
4
|
+
def decode(obj)
|
5
|
+
line = @io.gets
|
6
|
+
return unless line
|
7
|
+
|
8
|
+
obj = obj.allocate if obj.is_a?(Class)
|
9
|
+
obj.from_json(line)
|
10
|
+
obj
|
11
|
+
end
|
12
|
+
|
13
|
+
def encode(msg)
|
5
14
|
@io.write msg.to_json << "\n"
|
6
15
|
end
|
7
16
|
end
|
@@ -5,7 +5,12 @@ class Feedx::Format::Protobuf < Feedx::Format::Abstract
|
|
5
5
|
super PBIO::Delimited.new(io)
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
@io.
|
8
|
+
def decode(klass)
|
9
|
+
@io.read(klass)
|
10
|
+
end
|
11
|
+
|
12
|
+
def encode(msg)
|
13
|
+
msg = msg.to_pb if msg.respond_to?(:to_pb)
|
14
|
+
@io.write msg
|
10
15
|
end
|
11
16
|
end
|
data/lib/feedx/producer.rb
CHANGED
@@ -22,9 +22,7 @@ module Feedx
|
|
22
22
|
@enum = opts[:enum] || block
|
23
23
|
raise ArgumentError, "#{self.class.name}.new expects an :enum option or a block factory" unless @enum
|
24
24
|
|
25
|
-
@
|
26
|
-
@format = detect_format(opts[:format])
|
27
|
-
@compress = detect_compress(opts[:compress])
|
25
|
+
@stream = Feedx::Stream.new(url, opts)
|
28
26
|
@last_mod = opts[:last_modified]
|
29
27
|
end
|
30
28
|
|
@@ -34,51 +32,16 @@ module Feedx
|
|
34
32
|
current = (last_mod.to_f * 1000).floor
|
35
33
|
|
36
34
|
begin
|
37
|
-
previous = @blob.info.metadata[META_LAST_MODIFIED].to_i
|
35
|
+
previous = @stream.blob.info.metadata[META_LAST_MODIFIED].to_i
|
38
36
|
return -1 unless current > previous
|
39
37
|
rescue BFS::FileNotFound # rubocop:disable Lint/HandleExceptions
|
40
38
|
end if current.positive?
|
41
39
|
|
42
|
-
@
|
43
|
-
|
40
|
+
@stream.create metadata: { META_LAST_MODIFIED => current.to_s } do |fmt|
|
41
|
+
iter = enum.respond_to?(:find_each) ? :find_each : :each
|
42
|
+
enum.send(iter) {|rec| fmt.encode(rec) }
|
44
43
|
end
|
45
|
-
@blob.info.size
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def detect_format(val)
|
51
|
-
case val
|
52
|
-
when nil
|
53
|
-
Feedx::Format.detect(@blob.path)
|
54
|
-
when Class
|
55
|
-
parent = Feedx::Format::Abstract
|
56
|
-
raise ArgumentError, "Class #{val} must extend #{parent}" unless val < parent
|
57
|
-
|
58
|
-
val
|
59
|
-
else
|
60
|
-
Feedx::Format.resolve(val)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def detect_compress(val)
|
65
|
-
case val
|
66
|
-
when nil
|
67
|
-
Feedx::Compression.detect(@blob.path)
|
68
|
-
when Class
|
69
|
-
parent = Feedx::Compression::Abstract
|
70
|
-
raise ArgumentError, "Class #{val} must extend #{parent}" unless val < parent
|
71
|
-
|
72
|
-
val
|
73
|
-
else
|
74
|
-
Feedx::Compression.resolve(val)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def write_all(enum, io)
|
79
|
-
stream = @format.new(io)
|
80
|
-
iterator = enum.respond_to?(:find_each) ? :find_each : :each
|
81
|
-
enum.send(iterator) {|rec| stream.write(rec) }
|
44
|
+
@stream.blob.info.size
|
82
45
|
end
|
83
46
|
end
|
84
47
|
end
|
data/lib/feedx/stream.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'bfs'
|
2
|
+
require 'feedx'
|
3
|
+
|
4
|
+
module Feedx
|
5
|
+
# Abstract stream handler around a remote blob.
|
6
|
+
class Stream
|
7
|
+
attr_reader :blob
|
8
|
+
|
9
|
+
# @param [String] url the blob URL.
|
10
|
+
# @param [Hash] opts options
|
11
|
+
# @option opts [Symbol,Class<Feedx::Format::Abstract>] :format custom formatter. Default: from file extension.
|
12
|
+
# @option opts [Symbol,Class<Feedx::Compression::Abstract>] :compress enable compression. Default: from file extension.
|
13
|
+
def initialize(url, opts={})
|
14
|
+
@blob = BFS::Blob.new(url)
|
15
|
+
@format = detect_format(opts[:format])
|
16
|
+
@compress = detect_compress(opts[:compress])
|
17
|
+
end
|
18
|
+
|
19
|
+
# Opens the remote for reading.
|
20
|
+
# @param [Hash] opts BFS::Blob#open options
|
21
|
+
# @yield A block over a formatted stream.
|
22
|
+
# @yieldparam [Feedx::Format::Abstract] formatted input stream.
|
23
|
+
def open(opts={})
|
24
|
+
@blob.open(opts) do |io|
|
25
|
+
@compress.reader(io) do |cio|
|
26
|
+
fmt = @format.new(cio)
|
27
|
+
yield fmt
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Opens the remote for writing.
|
33
|
+
# @param [Hash] opts BFS::Blob#create options
|
34
|
+
# @yield A block over a formatted stream.
|
35
|
+
# @yieldparam [Feedx::Format::Abstract] formatted output stream.
|
36
|
+
def create(opts={})
|
37
|
+
@blob.create(opts) do |io|
|
38
|
+
@compress.writer(io) do |cio|
|
39
|
+
fmt = @format.new(cio)
|
40
|
+
yield fmt
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def detect_format(val)
|
48
|
+
case val
|
49
|
+
when nil
|
50
|
+
Feedx::Format.detect(@blob.path)
|
51
|
+
when Class
|
52
|
+
parent = Feedx::Format::Abstract
|
53
|
+
raise ArgumentError, "Class #{val} must extend #{parent}" unless val < parent
|
54
|
+
|
55
|
+
val
|
56
|
+
else
|
57
|
+
Feedx::Format.resolve(val)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def detect_compress(val)
|
62
|
+
case val
|
63
|
+
when nil
|
64
|
+
Feedx::Compression.detect(@blob.path)
|
65
|
+
when Class
|
66
|
+
parent = Feedx::Compression::Abstract
|
67
|
+
raise ArgumentError, "Class #{val} must extend #{parent}" unless val < parent
|
68
|
+
|
69
|
+
val
|
70
|
+
else
|
71
|
+
Feedx::Compression.resolve(val)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,9 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Feedx::Compression::Gzip do
|
4
|
-
it 'should wrap' do
|
5
|
-
|
6
|
-
described_class.
|
7
|
-
expect(
|
4
|
+
it 'should wrap readers/writers' do
|
5
|
+
wio = StringIO.new
|
6
|
+
described_class.writer(wio) {|w| w.write 'xyz' * 1000 }
|
7
|
+
expect(wio.size).to be_within(20).of(40)
|
8
|
+
|
9
|
+
data = ''
|
10
|
+
StringIO.open(wio.string) do |rio|
|
11
|
+
described_class.reader(rio) {|z| data = z.read }
|
12
|
+
end
|
13
|
+
expect(data.size).to eq(3000)
|
8
14
|
end
|
9
15
|
end
|
@@ -1,9 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Feedx::Compression::None do
|
4
|
-
it 'should wrap' do
|
5
|
-
|
6
|
-
described_class.
|
7
|
-
expect(
|
4
|
+
it 'should wrap readers/writers' do
|
5
|
+
wio = StringIO.new
|
6
|
+
described_class.writer(wio) {|w| w.write 'xyz' * 1000 }
|
7
|
+
expect(wio.size).to eq(3000)
|
8
|
+
|
9
|
+
data = ''
|
10
|
+
StringIO.open(wio.string) do |rio|
|
11
|
+
described_class.reader(rio) {|z| data = z.read }
|
12
|
+
end
|
13
|
+
expect(data.size).to eq(3000)
|
8
14
|
end
|
9
15
|
end
|
@@ -1,12 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Feedx::Format::JSON do
|
4
|
-
subject
|
5
|
-
let(:
|
4
|
+
subject { described_class.new(wio) }
|
5
|
+
let(:wio) { StringIO.new }
|
6
6
|
|
7
|
-
it 'should
|
8
|
-
subject.
|
9
|
-
subject.
|
10
|
-
|
7
|
+
it 'should encode/decode' do
|
8
|
+
subject.encode(Feedx::TestCase::Model.new('X'))
|
9
|
+
subject.encode(Feedx::TestCase::Model.new('Y'))
|
10
|
+
subject.encode(Feedx::TestCase::Message.new(title: 'Z'))
|
11
|
+
expect(wio.string.lines).to eq [
|
12
|
+
%({"title":"X","updated_at":"2018-01-05 11:25:15 UTC"}\n),
|
13
|
+
%({"title":"Y","updated_at":"2018-01-05 11:25:15 UTC"}\n),
|
14
|
+
%({"title":"Z"}\n),
|
15
|
+
]
|
16
|
+
|
17
|
+
StringIO.open(wio.string) do |rio|
|
18
|
+
fmt = described_class.new(rio)
|
19
|
+
expect(fmt.decode(Feedx::TestCase::Model)).to eq(Feedx::TestCase::Model.new('X'))
|
20
|
+
expect(fmt.decode(Feedx::TestCase::Model.new('O'))).to eq(Feedx::TestCase::Model.new('Y'))
|
21
|
+
expect(fmt.decode(Feedx::TestCase::Model)).to eq(Feedx::TestCase::Model.new('Z'))
|
22
|
+
expect(fmt.decode(Feedx::TestCase::Model)).to be_nil
|
23
|
+
expect(fmt).to be_eof
|
24
|
+
end
|
11
25
|
end
|
12
26
|
end
|
@@ -1,20 +1,22 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Feedx::Format::Protobuf do
|
4
|
-
subject
|
5
|
-
let(:
|
4
|
+
subject { described_class.new(wio) }
|
5
|
+
let(:wio) { StringIO.new }
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
7
|
+
it 'should encode/decode' do
|
8
|
+
subject.encode(Feedx::TestCase::Model.new('X'))
|
9
|
+
subject.encode(Feedx::TestCase::Model.new('Y'))
|
10
|
+
subject.encode(Feedx::TestCase::Message.new(title: 'Z'))
|
11
|
+
expect(wio.string.bytes).to eq([3, 10, 1, 88] + [3, 10, 1, 89] + [3, 10, 1, 90])
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
StringIO.open(wio.string) do |rio|
|
14
|
+
fmt = described_class.new(rio)
|
15
|
+
expect(fmt.decode(Feedx::TestCase::Message)).to eq(Feedx::TestCase::Message.new(title: 'X'))
|
16
|
+
expect(fmt.decode(Feedx::TestCase::Message)).to eq(Feedx::TestCase::Message.new(title: 'Y'))
|
17
|
+
expect(fmt.decode(Feedx::TestCase::Message)).to eq(Feedx::TestCase::Message.new(title: 'Z'))
|
18
|
+
expect(fmt.decode(Feedx::TestCase::Message)).to be_nil
|
19
|
+
expect(fmt).to be_eof
|
20
|
+
end
|
19
21
|
end
|
20
22
|
end
|
data/spec/feedx/producer_spec.rb
CHANGED
@@ -1,20 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Feedx::Producer do
|
4
|
-
let :model do
|
5
|
-
Class.new Struct.new(:title) do
|
6
|
-
def to_pb
|
7
|
-
Feedx::TestCase::Message.new title: title
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_json(*)
|
11
|
-
::JSON.dump(title: title, updated_at: Time.at(1515151515).utc)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
4
|
let :enumerable do
|
17
|
-
%w[x y z].map {|t|
|
5
|
+
%w[x y z].map {|t| Feedx::TestCase::Model.new(t) } * 100
|
18
6
|
end
|
19
7
|
|
20
8
|
let(:bucket) { BFS::Bucket::InMem.new }
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Feedx::Stream do
|
4
|
+
let(:bucket) { BFS::Bucket::InMem.new }
|
5
|
+
before { allow(BFS).to receive(:resolve).and_return(bucket) }
|
6
|
+
|
7
|
+
subject { described_class.new('mock:///dir/file.json') }
|
8
|
+
let(:compressed) { described_class.new('mock:///dir/file.json.gz') }
|
9
|
+
|
10
|
+
it 'should reject invalid inputs' do
|
11
|
+
expect do
|
12
|
+
described_class.new('mock:///dir/file.txt')
|
13
|
+
end.to raise_error(/unable to detect format/)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should encode' do
|
17
|
+
subject.create do |s|
|
18
|
+
s.encode(Feedx::TestCase::Model.new('X'))
|
19
|
+
s.encode(Feedx::TestCase::Model.new('Y'))
|
20
|
+
end
|
21
|
+
|
22
|
+
expect(bucket.open('dir/file.json').read).to eq(
|
23
|
+
%({"title":"X","updated_at":"2018-01-05 11:25:15 UTC"}\n) +
|
24
|
+
%({"title":"Y","updated_at":"2018-01-05 11:25:15 UTC"}\n),
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should encode compressed' do
|
29
|
+
compressed.create do |s|
|
30
|
+
100.times do
|
31
|
+
s.encode(Feedx::TestCase::Model.new('X'))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(bucket.info('dir/file.json.gz').size).to be_within(10).of(108)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should encode with create options' do
|
39
|
+
subject.create metadata: { 'x' => '5' } do |s|
|
40
|
+
s.encode(Feedx::TestCase::Model.new('X'))
|
41
|
+
end
|
42
|
+
expect(bucket.info('dir/file.json').metadata).to eq('x' => '5')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should decode' do
|
46
|
+
subject.create do |s|
|
47
|
+
s.encode(Feedx::TestCase::Model.new('X'))
|
48
|
+
s.encode(Feedx::TestCase::Model.new('Y'))
|
49
|
+
end
|
50
|
+
|
51
|
+
subject.open do |s|
|
52
|
+
expect(s.decode(Feedx::TestCase::Model)).to eq(Feedx::TestCase::Model.new('X'))
|
53
|
+
expect(s.decode(Feedx::TestCase::Model)).to eq(Feedx::TestCase::Model.new('Y'))
|
54
|
+
expect(s.decode(Feedx::TestCase::Model)).to be_nil
|
55
|
+
expect(s).to be_eof
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should decode compressed' do
|
60
|
+
compressed.create do |s|
|
61
|
+
s.encode(Feedx::TestCase::Model.new('X'))
|
62
|
+
s.encode(Feedx::TestCase::Model.new('Y'))
|
63
|
+
end
|
64
|
+
|
65
|
+
compressed.open do |s|
|
66
|
+
expect(s.decode(Feedx::TestCase::Model)).to eq(Feedx::TestCase::Model.new('X'))
|
67
|
+
expect(s.decode(Feedx::TestCase::Model)).to eq(Feedx::TestCase::Model.new('Y'))
|
68
|
+
expect(s.decode(Feedx::TestCase::Model)).to be_nil
|
69
|
+
expect(s).to be_eof
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,5 +11,31 @@ end
|
|
11
11
|
module Feedx
|
12
12
|
module TestCase
|
13
13
|
Message = Google::Protobuf::DescriptorPool.generated_pool.lookup('com.blacksquaremedia.feedx.testcase.Message').msgclass
|
14
|
+
|
15
|
+
class Model
|
16
|
+
attr_reader :title
|
17
|
+
|
18
|
+
def initialize(title)
|
19
|
+
@title = title
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_pb
|
23
|
+
Feedx::TestCase::Message.new title: @title
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
title == other.title
|
28
|
+
end
|
29
|
+
alias eql? ==
|
30
|
+
|
31
|
+
def from_json(data, *)
|
32
|
+
hash = ::JSON.parse(data)
|
33
|
+
@title = hash['title'] if hash.is_a?(Hash)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_json(*)
|
37
|
+
::JSON.dump(title: @title, updated_at: Time.at(1515151515).utc)
|
38
|
+
end
|
39
|
+
end
|
14
40
|
end
|
15
41
|
end
|
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.
|
4
|
+
version: 0.7.0
|
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: 2019-05-
|
11
|
+
date: 2019-05-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bfs
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- lib/feedx/format/protobuf.rb
|
148
148
|
- lib/feedx/producer.rb
|
149
149
|
- lib/feedx/pusher.rb
|
150
|
+
- lib/feedx/stream.rb
|
150
151
|
- producer.go
|
151
152
|
- producer_test.go
|
152
153
|
- reader.go
|
@@ -158,6 +159,7 @@ files:
|
|
158
159
|
- spec/feedx/format/protobuf_spec.rb
|
159
160
|
- spec/feedx/format_spec.rb
|
160
161
|
- spec/feedx/producer_spec.rb
|
162
|
+
- spec/feedx/stream_spec.rb
|
161
163
|
- spec/spec_helper.rb
|
162
164
|
- writer.go
|
163
165
|
- writer_test.go
|
@@ -192,4 +194,5 @@ test_files:
|
|
192
194
|
- spec/feedx/format/protobuf_spec.rb
|
193
195
|
- spec/feedx/format_spec.rb
|
194
196
|
- spec/feedx/producer_spec.rb
|
197
|
+
- spec/feedx/stream_spec.rb
|
195
198
|
- spec/spec_helper.rb
|