feedx 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/.travis.yml +5 -0
- data/Gemfile.lock +8 -8
- data/feedx.gemspec +3 -3
- data/lib/feedx.rb +2 -0
- data/lib/feedx/cache.rb +7 -0
- data/lib/feedx/cache/abstract.rb +35 -0
- data/lib/feedx/cache/memory.rb +30 -0
- data/lib/feedx/cache/value.rb +25 -0
- data/lib/feedx/consumer.rb +47 -0
- data/lib/feedx/format/abstract.rb +2 -2
- data/lib/feedx/format/json.rb +2 -2
- data/lib/feedx/producer.rb +12 -10
- data/spec/feedx/cache/memory_spec.rb +23 -0
- data/spec/feedx/cache/value_spec.rb +19 -0
- data/spec/feedx/consumer_spec.rb +45 -0
- data/spec/spec_helper.rb +1 -1
- metadata +16 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c294d0811dec65d1fce8a44fe2e18dbe08ac0bffee8d56c6b1d96bdac63e9366
|
4
|
+
data.tar.gz: e6172cbf1f728569624a928dcf2a22c3a9a87824ed6cfd8fddaf834d1e526d8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64f69d35aa2cdca873e223b0f959a404dacebe03b07a8ca77796a10d5d6dfc06b4f7b9e7ee3bbfcfa03c23656399038324088a19d8e643636d0d3f3408af7b4b
|
7
|
+
data.tar.gz: fb2e106e250693c5756edecf6d98df7115f8e8b33f4fca01ade0c3c568eb756215e6efa967d36099fdb055e4f77a26f68aebc207d9f62bb82c4748fb193d8ec7
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
feedx (0.7.
|
5
|
-
bfs (>= 0.
|
4
|
+
feedx (0.7.2)
|
5
|
+
bfs (>= 0.4.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
ast (2.4.0)
|
11
|
-
bfs (0.
|
11
|
+
bfs (0.4.0)
|
12
12
|
diff-lcs (1.3)
|
13
13
|
google-protobuf (3.8.0)
|
14
|
-
jaro_winkler (1.5.
|
14
|
+
jaro_winkler (1.5.3)
|
15
15
|
parallel (1.17.0)
|
16
16
|
parser (2.6.3.0)
|
17
17
|
ast (~> 2.4.0)
|
@@ -23,12 +23,12 @@ GEM
|
|
23
23
|
rspec-core (~> 3.8.0)
|
24
24
|
rspec-expectations (~> 3.8.0)
|
25
25
|
rspec-mocks (~> 3.8.0)
|
26
|
-
rspec-core (3.8.
|
26
|
+
rspec-core (3.8.1)
|
27
27
|
rspec-support (~> 3.8.0)
|
28
28
|
rspec-expectations (3.8.4)
|
29
29
|
diff-lcs (>= 1.2.0, < 2.0)
|
30
30
|
rspec-support (~> 3.8.0)
|
31
|
-
rspec-mocks (3.8.
|
31
|
+
rspec-mocks (3.8.1)
|
32
32
|
diff-lcs (>= 1.2.0, < 2.0)
|
33
33
|
rspec-support (~> 3.8.0)
|
34
34
|
rspec-support (3.8.2)
|
@@ -39,8 +39,8 @@ GEM
|
|
39
39
|
rainbow (>= 2.2.2, < 4.0)
|
40
40
|
ruby-progressbar (~> 1.7)
|
41
41
|
unicode-display_width (>= 1.4.0, < 1.7)
|
42
|
-
rubocop-performance (1.
|
43
|
-
rubocop (>= 0.
|
42
|
+
rubocop-performance (1.4.0)
|
43
|
+
rubocop (>= 0.71.0)
|
44
44
|
ruby-progressbar (1.10.1)
|
45
45
|
unicode-display_width (1.6.0)
|
46
46
|
|
data/feedx.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'feedx'
|
3
|
-
s.version = '0.7.
|
3
|
+
s.version = '0.7.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)
|
@@ -11,9 +11,9 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^spec/}) }
|
12
12
|
s.test_files = `git ls-files -z -- spec/*`.split("\x0")
|
13
13
|
s.require_paths = ['lib']
|
14
|
-
s.required_ruby_version = '>= 2.
|
14
|
+
s.required_ruby_version = '>= 2.4'
|
15
15
|
|
16
|
-
s.add_dependency 'bfs', '>= 0.
|
16
|
+
s.add_dependency 'bfs', '>= 0.4.0'
|
17
17
|
|
18
18
|
s.add_development_dependency 'bundler'
|
19
19
|
s.add_development_dependency 'pbio'
|
data/lib/feedx.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module Feedx
|
2
2
|
META_LAST_MODIFIED = 'x-feedx-last-modified'.freeze
|
3
3
|
|
4
|
+
autoload :Cache, 'feedx/cache'
|
4
5
|
autoload :Compression, 'feedx/compression'
|
6
|
+
autoload :Consumer, 'feedx/consumer'
|
5
7
|
autoload :Format, 'feedx/format'
|
6
8
|
autoload :Stream, 'feedx/stream'
|
7
9
|
autoload :Producer, 'feedx/producer'
|
data/lib/feedx/cache.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class Feedx::Cache::Abstract
|
2
|
+
# Clears cache.
|
3
|
+
def clear
|
4
|
+
raise 'Not implemented'
|
5
|
+
end
|
6
|
+
|
7
|
+
# Read reads a key.
|
8
|
+
def read(_key, **_opts)
|
9
|
+
raise 'Not implemented'
|
10
|
+
end
|
11
|
+
|
12
|
+
# Write writes a key/value pair.
|
13
|
+
def write(_key, _value, **_opts)
|
14
|
+
raise 'Not implemented'
|
15
|
+
end
|
16
|
+
|
17
|
+
# Fetches data from the cache, using the given key.
|
18
|
+
# The optional block will be evaluated and the result stored in the cache
|
19
|
+
# in the event of a cache miss.
|
20
|
+
def fetch(key, **opts)
|
21
|
+
value = read(key, **opts)
|
22
|
+
|
23
|
+
if block_given?
|
24
|
+
value ||= yield
|
25
|
+
write(key, value, **opts) if value
|
26
|
+
end
|
27
|
+
|
28
|
+
value
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Feedx::Abstract::Value] returns a wrapper around a single value.
|
32
|
+
def value(key)
|
33
|
+
Feedx::Cache::Value.new(self, key)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'monitor'
|
2
|
+
|
3
|
+
# Thread-safe in-memory cache. Use for testing only.
|
4
|
+
class Feedx::Cache::Memory < Feedx::Cache::Abstract
|
5
|
+
def initialize
|
6
|
+
@monitor = Monitor.new
|
7
|
+
@entries = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
# Clear empties cache.
|
11
|
+
def clear
|
12
|
+
@monitor.synchronize do
|
13
|
+
@entries.clear
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Read reads a key.
|
18
|
+
def read(key, **)
|
19
|
+
@monitor.synchronize do
|
20
|
+
@entries[key]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Write writes a key.
|
25
|
+
def write(key, value, **)
|
26
|
+
@monitor.synchronize do
|
27
|
+
@entries[key] = value.to_s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# A single value inside a cache.
|
2
|
+
class Feedx::Cache::Value
|
3
|
+
attr_reader :key
|
4
|
+
|
5
|
+
def initialize(cache, key)
|
6
|
+
@cache = cache
|
7
|
+
@key = key
|
8
|
+
end
|
9
|
+
|
10
|
+
# Read the key.
|
11
|
+
def read(**opts)
|
12
|
+
@cache.read(@key, **opts)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Write a value.
|
16
|
+
def write(value, **opts)
|
17
|
+
@cache.write(@key, value, **opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Fetches data. The optional block will be evaluated and the
|
21
|
+
# result stored in the cache under the key in the event of a cache miss.
|
22
|
+
def fetch(**opts, &block)
|
23
|
+
@cache.fetch(@key, **opts, &block)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'bfs'
|
3
|
+
require 'feedx'
|
4
|
+
|
5
|
+
module Feedx
|
6
|
+
# Consumes an enumerates over a feed.
|
7
|
+
class Consumer
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
# See constructor.
|
11
|
+
def self.each(url, klass, opts={}, &block)
|
12
|
+
new(url, klass, opts).each(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [String] url the destination URL.
|
16
|
+
# @param [Class] klass the record class.
|
17
|
+
# @param [Hash] opts options
|
18
|
+
# @option opts [Symbol,Class<Feedx::Format::Abstract>] :format custom formatter. Default: from file extension.
|
19
|
+
# @option opts [Hash] :format_options format decode options. Default: {}.
|
20
|
+
# @option opts [Symbol,Class<Feedx::Compression::Abstract>] :compress enable compression. Default: from file extension.
|
21
|
+
# @option opts [Feedx::Cache::Value] :cache cache value to store remote last modified time and consume conditionally.
|
22
|
+
def initialize(url, klass, opts={})
|
23
|
+
@klass = klass
|
24
|
+
@stream = Feedx::Stream.new(url, opts)
|
25
|
+
@fmt_opts = opts[:format_options] || {}
|
26
|
+
@cache = opts[:cache]
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] returns true if performed.
|
30
|
+
def each(&block)
|
31
|
+
remote_rev = nil
|
32
|
+
|
33
|
+
if @cache
|
34
|
+
local_rev = @cache.read.to_i
|
35
|
+
remote_rev = @stream.blob.info.metadata[META_LAST_MODIFIED].to_i
|
36
|
+
return false if remote_rev.positive? && remote_rev <= local_rev
|
37
|
+
end
|
38
|
+
|
39
|
+
@stream.open do |fmt|
|
40
|
+
fmt.decode_each(@klass, **@fmt_opts, &block)
|
41
|
+
end
|
42
|
+
@cache.write(remote_rev) if @cache && remote_rev
|
43
|
+
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -9,9 +9,9 @@ class Feedx::Format::Abstract
|
|
9
9
|
|
10
10
|
def decode_each(klass, **opts)
|
11
11
|
if block_given?
|
12
|
-
yield decode(klass, opts) until eof?
|
12
|
+
yield decode(klass, **opts) until eof?
|
13
13
|
else
|
14
|
-
Enumerator.new {|y| y << decode(klass, opts) until eof? }
|
14
|
+
Enumerator.new {|y| y << decode(klass, **opts) until eof? }
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
data/lib/feedx/format/json.rb
CHANGED
data/lib/feedx/producer.rb
CHANGED
@@ -3,7 +3,7 @@ require 'bfs'
|
|
3
3
|
require 'feedx'
|
4
4
|
|
5
5
|
module Feedx
|
6
|
-
# Produces a relation as
|
6
|
+
# Produces a relation as an encoded feed to a remote location.
|
7
7
|
class Producer
|
8
8
|
# See constructor.
|
9
9
|
def self.perform(url, opts={}, &block)
|
@@ -14,6 +14,7 @@ module Feedx
|
|
14
14
|
# @param [Hash] opts options
|
15
15
|
# @option opts [Enumerable,ActiveRecord::Relation] :enum relation or enumerator to stream.
|
16
16
|
# @option opts [Symbol,Class<Feedx::Format::Abstract>] :format custom formatter. Default: from file extension.
|
17
|
+
# @option opts [Hash] :format_options format encode options. Default: {}.
|
17
18
|
# @option opts [Symbol,Class<Feedx::Compression::Abstract>] :compress enable compression. Default: from file extension.
|
18
19
|
# @option opts [Time,Proc] :last_modified the last modified time, used to determine if a push is necessary.
|
19
20
|
# @yield A block factory to generate the relation or enumerator.
|
@@ -22,24 +23,25 @@ module Feedx
|
|
22
23
|
@enum = opts[:enum] || block
|
23
24
|
raise ArgumentError, "#{self.class.name}.new expects an :enum option or a block factory" unless @enum
|
24
25
|
|
25
|
-
@stream
|
26
|
+
@stream = Feedx::Stream.new(url, opts)
|
26
27
|
@last_mod = opts[:last_modified]
|
28
|
+
@fmt_opts = opts[:format_options] || {}
|
27
29
|
end
|
28
30
|
|
29
31
|
def perform
|
30
|
-
enum
|
31
|
-
last_mod
|
32
|
-
|
32
|
+
enum = @enum.is_a?(Proc) ? @enum.call : @enum
|
33
|
+
last_mod = @last_mod.is_a?(Proc) ? @last_mod.call(enum) : @last_mod
|
34
|
+
local_rev = last_mod.is_a?(Integer) ? last_mod : (last_mod.to_f * 1000).floor
|
33
35
|
|
34
36
|
begin
|
35
|
-
|
36
|
-
return -1 unless
|
37
|
+
remote_rev = @stream.blob.info.metadata[META_LAST_MODIFIED].to_i
|
38
|
+
return -1 unless local_rev > remote_rev
|
37
39
|
rescue BFS::FileNotFound # rubocop:disable Lint/HandleExceptions
|
38
|
-
end if
|
40
|
+
end if local_rev.positive?
|
39
41
|
|
40
|
-
@stream.create metadata: { META_LAST_MODIFIED =>
|
42
|
+
@stream.create metadata: { META_LAST_MODIFIED => local_rev.to_s } do |fmt|
|
41
43
|
iter = enum.respond_to?(:find_each) ? :find_each : :each
|
42
|
-
enum.send(iter) {|rec| fmt.encode(rec) }
|
44
|
+
enum.send(iter) {|rec| fmt.encode(rec, **@fmt_opts) }
|
43
45
|
end
|
44
46
|
@stream.blob.info.size
|
45
47
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Feedx::Cache::Memory do
|
4
|
+
it 'should read/write' do
|
5
|
+
expect(subject.fetch('key')).to be_nil
|
6
|
+
expect(subject.fetch('key') { 'value' }).to eq('value')
|
7
|
+
expect(subject.fetch('key')).to eq('value')
|
8
|
+
expect(subject.fetch('key') { 'other' }).to eq('value')
|
9
|
+
expect(subject.fetch('key')).to eq('value')
|
10
|
+
|
11
|
+
subject.write('key', 'new-value')
|
12
|
+
expect(subject.read('key')).to eq('new-value')
|
13
|
+
expect(subject.fetch('key')).to eq('new-value')
|
14
|
+
|
15
|
+
subject.clear
|
16
|
+
expect(subject.fetch('key')).to be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should write strings' do
|
20
|
+
subject.write('key', 5)
|
21
|
+
expect(subject.read('key')).to eq('5')
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Feedx::Cache::Value do
|
4
|
+
subject do
|
5
|
+
described_class.new(Feedx::Cache::Memory.new, 'key')
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should read/write' do
|
9
|
+
expect(subject.fetch).to be_nil
|
10
|
+
expect(subject.fetch { 'value' }).to eq('value')
|
11
|
+
expect(subject.fetch).to eq('value')
|
12
|
+
expect(subject.fetch { 'other' }).to eq('value')
|
13
|
+
expect(subject.fetch).to eq('value')
|
14
|
+
|
15
|
+
subject.write('new-value')
|
16
|
+
expect(subject.read).to eq('new-value')
|
17
|
+
expect(subject.fetch).to eq('new-value')
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Feedx::Consumer do
|
4
|
+
let(:bucket) { BFS::Bucket::InMem.new }
|
5
|
+
let(:klass) { Feedx::TestCase::Model }
|
6
|
+
let(:cache) { Feedx::Cache::Memory.new.value('my-consumer') }
|
7
|
+
before { allow(BFS).to receive(:resolve).and_return(bucket) }
|
8
|
+
|
9
|
+
it 'should reject invalid inputs' do
|
10
|
+
expect do
|
11
|
+
described_class.each('mock:///dir/file.txt', klass) {}
|
12
|
+
end.to raise_error(/unable to detect format/)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should consume feeds' do
|
16
|
+
url = mock_produce!
|
17
|
+
csm = described_class.new(url, klass)
|
18
|
+
expect(csm).to be_a(Enumerable)
|
19
|
+
|
20
|
+
cnt = csm.count do |rec|
|
21
|
+
expect(rec).to be_instance_of(klass)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
expect(cnt).to eq(300)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should perform conditionally' do
|
28
|
+
url = mock_produce! last_modified: Time.at(1515151515)
|
29
|
+
expect(described_class.new(url, klass, cache: cache).count).to eq(300)
|
30
|
+
expect(described_class.new(url, klass, cache: cache).count).to eq(0)
|
31
|
+
|
32
|
+
url = mock_produce!
|
33
|
+
expect(described_class.new(url, klass, cache: cache).count).to eq(300)
|
34
|
+
expect(described_class.new(url, klass, cache: cache).count).to eq(300)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def mock_produce!(opts={})
|
40
|
+
url = 'mock:///dir/file.json'
|
41
|
+
opts[:enum] ||= %w[x y z].map {|t| Feedx::TestCase::Model.new(t) } * 100
|
42
|
+
Feedx::Producer.perform url, opts
|
43
|
+
url
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
CHANGED
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.7.
|
4
|
+
version: 0.7.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: 2019-06-
|
11
|
+
date: 2019-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bfs
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.4.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.4.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,10 +137,15 @@ files:
|
|
137
137
|
- go.mod
|
138
138
|
- go.sum
|
139
139
|
- lib/feedx.rb
|
140
|
+
- lib/feedx/cache.rb
|
141
|
+
- lib/feedx/cache/abstract.rb
|
142
|
+
- lib/feedx/cache/memory.rb
|
143
|
+
- lib/feedx/cache/value.rb
|
140
144
|
- lib/feedx/compression.rb
|
141
145
|
- lib/feedx/compression/abstract.rb
|
142
146
|
- lib/feedx/compression/gzip.rb
|
143
147
|
- lib/feedx/compression/none.rb
|
148
|
+
- lib/feedx/consumer.rb
|
144
149
|
- lib/feedx/format.rb
|
145
150
|
- lib/feedx/format/abstract.rb
|
146
151
|
- lib/feedx/format/json.rb
|
@@ -152,9 +157,12 @@ files:
|
|
152
157
|
- producer_test.go
|
153
158
|
- reader.go
|
154
159
|
- reader_test.go
|
160
|
+
- spec/feedx/cache/memory_spec.rb
|
161
|
+
- spec/feedx/cache/value_spec.rb
|
155
162
|
- spec/feedx/compression/gzip_spec.rb
|
156
163
|
- spec/feedx/compression/none_spec.rb
|
157
164
|
- spec/feedx/compression_spec.rb
|
165
|
+
- spec/feedx/consumer_spec.rb
|
158
166
|
- spec/feedx/format/abstract_spec.rb
|
159
167
|
- spec/feedx/format/json_spec.rb
|
160
168
|
- spec/feedx/format/protobuf_spec.rb
|
@@ -176,7 +184,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
176
184
|
requirements:
|
177
185
|
- - ">="
|
178
186
|
- !ruby/object:Gem::Version
|
179
|
-
version: '2.
|
187
|
+
version: '2.4'
|
180
188
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
189
|
requirements:
|
182
190
|
- - ">="
|
@@ -188,9 +196,12 @@ signing_key:
|
|
188
196
|
specification_version: 4
|
189
197
|
summary: Exchange data between components via feeds
|
190
198
|
test_files:
|
199
|
+
- spec/feedx/cache/memory_spec.rb
|
200
|
+
- spec/feedx/cache/value_spec.rb
|
191
201
|
- spec/feedx/compression/gzip_spec.rb
|
192
202
|
- spec/feedx/compression/none_spec.rb
|
193
203
|
- spec/feedx/compression_spec.rb
|
204
|
+
- spec/feedx/consumer_spec.rb
|
194
205
|
- spec/feedx/format/abstract_spec.rb
|
195
206
|
- spec/feedx/format/json_spec.rb
|
196
207
|
- spec/feedx/format/protobuf_spec.rb
|