feedx 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 93c6d8da8f0485d4cde2b935d55d176bd85265ed2983d5fda8b86e229635c666
4
+ data.tar.gz: 0d32c1595c97c42937bbdb8765da4347d8041fc6e4db8c335a88a26c715eed02
5
+ SHA512:
6
+ metadata.gz: 689dd5925778d4895cd1dbfd35bf717070dbfdbe4fe3c831d01108ccef637fb1eaeb1166945b152393cff653f71426dbdd326de78633b6ed4aab36640e11693e
7
+ data.tar.gz: ea603ed38643a9484875d626ff9be7b42ae0a56f20e7a85ccdcbb0ca8101846084bfc4cdb4373793df3d4be36ca31eb92e0351186c85712937d88f1b46677e07
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
@@ -0,0 +1,2 @@
1
+ .rubocop-*
2
+ pkg/
@@ -0,0 +1,5 @@
1
+ inherit_from:
2
+ - https://storage.googleapis.com/bsm-misc/rubocop.yml?1541767352
3
+
4
+ AllCops:
5
+ TargetRubyVersion: "2.2"
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5
4
+ - 2.4
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,59 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ feedx (0.1.0)
5
+ bfs
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.0)
11
+ bfs (0.2.2)
12
+ diff-lcs (1.3)
13
+ google-protobuf (3.6.1)
14
+ jaro_winkler (1.5.1)
15
+ parallel (1.12.1)
16
+ parser (2.5.3.0)
17
+ ast (~> 2.4.0)
18
+ pbio (0.1.0)
19
+ google-protobuf
20
+ powerpack (0.1.2)
21
+ rainbow (3.0.0)
22
+ rake (12.3.1)
23
+ rspec (3.8.0)
24
+ rspec-core (~> 3.8.0)
25
+ rspec-expectations (~> 3.8.0)
26
+ rspec-mocks (~> 3.8.0)
27
+ rspec-core (3.8.0)
28
+ rspec-support (~> 3.8.0)
29
+ rspec-expectations (3.8.2)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.8.0)
32
+ rspec-mocks (3.8.0)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.8.0)
35
+ rspec-support (3.8.0)
36
+ rubocop (0.60.0)
37
+ jaro_winkler (~> 1.5.1)
38
+ parallel (~> 1.10)
39
+ parser (>= 2.5, != 2.5.1.1)
40
+ powerpack (~> 0.1)
41
+ rainbow (>= 2.2.2, < 4.0)
42
+ ruby-progressbar (~> 1.7)
43
+ unicode-display_width (~> 1.4.0)
44
+ ruby-progressbar (1.10.0)
45
+ unicode-display_width (1.4.0)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ bundler
52
+ feedx!
53
+ pbio
54
+ rake
55
+ rspec
56
+ rubocop
57
+
58
+ BUNDLED WITH
59
+ 1.16.4
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2018 Black Square Media Ltd
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,23 @@
1
+ # Feedx
2
+
3
+ [![Build Status](https://travis-ci.org/bsm/feedx.png?branch=master)](https://travis-ci.org/bsm/feedx)
4
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
5
+
6
+ Feed-based data exchange between services.
7
+
8
+ ## Usage
9
+
10
+ ```ruby
11
+ require 'bfs/s3'
12
+ require 'feedx'
13
+
14
+ # Init a new pusher with an S3 destination
15
+ relation = Post.includes(:author)
16
+ pusher = Feedx::Pusher.new relation, 's3://my-bucket/feeds/users.json.gz'
17
+
18
+ # Push a new feed every hour
19
+ loop do
20
+ pusher.perform
21
+ sleep(3600)
22
+ end
23
+ ```
@@ -0,0 +1,9 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+ require 'rubocop/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ RuboCop::RakeTask.new(:rubocop)
8
+
9
+ task default: %i[spec rubocop]
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'feedx'
3
+ s.version = '0.1.0'
4
+ s.authors = ['Black Square Media Ltd']
5
+ s.email = ['info@blacksquaremedia.com']
6
+ s.summary = %(Exchange data between components via feeds)
7
+ s.description = %(Use feeds to exchange data between (micro-)services.)
8
+ s.homepage = 'https://github.com/bsm/feedx'
9
+ s.license = 'Apache-2.0'
10
+
11
+ s.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^spec/}) }
12
+ s.test_files = `git ls-files -z -- spec/*`.split("\x0")
13
+ s.require_paths = ['lib']
14
+ s.required_ruby_version = '>= 2.2'
15
+
16
+ s.add_dependency 'bfs'
17
+
18
+ s.add_development_dependency 'bundler'
19
+ s.add_development_dependency 'pbio'
20
+ s.add_development_dependency 'rake'
21
+ s.add_development_dependency 'rspec'
22
+ s.add_development_dependency 'rubocop'
23
+ end
@@ -0,0 +1,5 @@
1
+ module Feedx
2
+ autoload :Compression, 'feedx/compression'
3
+ autoload :Format, 'feedx/format'
4
+ autoload :Pusher, 'feedx/pusher'
5
+ end
@@ -0,0 +1,28 @@
1
+ module Feedx
2
+ module Compression
3
+ autoload :Abstract, 'feedx/compression/abstract'
4
+ autoload :None, 'feedx/compression/none'
5
+ autoload :Gzip, 'feedx/compression/gzip'
6
+
7
+ class << self
8
+ def resolve(name)
9
+ case name.to_s
10
+ when 'gz', 'gzip'
11
+ Gzip
12
+ when ''
13
+ None
14
+ else
15
+ raise ArgumentError, "invalid compression #{name}"
16
+ end
17
+ end
18
+
19
+ def detect(path)
20
+ if File.extname(path)[-1] == 'z'
21
+ Gzip
22
+ else
23
+ None
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ class Feedx::Compression::Abstract
2
+ def self.wrap(_io, &_block)
3
+ raise 'Not implemented'
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require 'zlib'
2
+
3
+ class Feedx::Compression::Gzip < Feedx::Compression::Abstract
4
+ def self.wrap(io, &block)
5
+ Zlib::GzipWriter.wrap(io, &block)
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class Feedx::Compression::None < Feedx::Compression::Abstract
2
+ def self.wrap(io, &block)
3
+ block.call(io)
4
+ end
5
+ end
@@ -0,0 +1,37 @@
1
+ module Feedx
2
+ module Format
3
+ autoload :Abstract, 'feedx/format/abstract'
4
+ autoload :JSON, 'feedx/format/json'
5
+ autoload :Protobuf, 'feedx/format/protobuf'
6
+
7
+ class << self
8
+ def resolve(name)
9
+ _resolve(name) || raise(ArgumentError, "invalid format #{name}")
10
+ end
11
+
12
+ def detect(path)
13
+ base = File.basename(path)
14
+ loop do
15
+ ext = File.extname(base)
16
+ raise ArgumentError, 'unable to detect format' if ext.empty?
17
+
18
+ kind = _resolve(ext[1..-1]) || _resolve(ext[1..-2])
19
+ return kind if kind
20
+
21
+ base = base[0..-ext.size - 1]
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def _resolve(name)
28
+ case name.to_s
29
+ when 'pb', 'proto', 'protobuf'
30
+ Protobuf
31
+ when 'json'
32
+ JSON
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ class Feedx::Format::Abstract
2
+ def initialize(io)
3
+ @io = io
4
+ end
5
+
6
+ def write(_msg)
7
+ raise 'Not implemented'
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ require 'json'
2
+
3
+ class Feedx::Format::JSON < Feedx::Format::Abstract
4
+ def write(msg)
5
+ @io.write msg.to_json << "\n"
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'pbio'
2
+
3
+ class Feedx::Format::Protobuf < Feedx::Format::Abstract
4
+ def initialize(io)
5
+ super PBIO::Delimited.new(io)
6
+ end
7
+
8
+ def write(msg)
9
+ @io.write msg.to_pb
10
+ end
11
+ end
@@ -0,0 +1,62 @@
1
+ require 'uri'
2
+ require 'bfs'
3
+
4
+ module Feedx
5
+ # Pushes a relation as a protobuf encoded stream to an S3 location.
6
+ class Pusher
7
+ # @param [Enumerable,ActiveRecord::Relation] relation to stream.
8
+ # @param [String] url the destination URL.
9
+ # @param [Hash] opts options
10
+ # @option opts [Symbol,Class<Feedx::Format::Abstract>] :format custom formatter. Default: from file extension.
11
+ # @option opts [Symbol,Class<Feedx::Compression::Abstract>] :compress enable compression. Default: from file extension.
12
+ def initialize(relation, url, opts={})
13
+ @relation = relation
14
+ @blob = BFS::Blob.new(url)
15
+ @format = detect_format(opts[:format])
16
+ @compress = detect_compress(opts[:compress])
17
+ end
18
+
19
+ def perform
20
+ @blob.create do |io|
21
+ @compress.wrap(io) {|w| write_to(w) }
22
+ end
23
+ @blob.info.size
24
+ end
25
+
26
+ private
27
+
28
+ def detect_format(val)
29
+ case val
30
+ when nil
31
+ Feedx::Format.detect(@blob.path)
32
+ when Class
33
+ parent = Feedx::Format::Abstract
34
+ raise ArgumentError, "Class #{val} must extend #{parent}" unless val < parent
35
+
36
+ val
37
+ else
38
+ Feedx::Format.resolve(val)
39
+ end
40
+ end
41
+
42
+ def detect_compress(val)
43
+ case val
44
+ when nil
45
+ Feedx::Compression.detect(@blob.path)
46
+ when Class
47
+ parent = Feedx::Compression::Abstract
48
+ raise ArgumentError, "Class #{val} must extend #{parent}" unless val < parent
49
+
50
+ val
51
+ else
52
+ Feedx::Compression.resolve(val)
53
+ end
54
+ end
55
+
56
+ def write_to(io)
57
+ stream = @format.new(io)
58
+ enum = @relation.respond_to?(:find_each) ? :find_each : :each
59
+ @relation.send(enum) {|rec| stream.write(rec) }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Feedx::Compression::Gzip do
4
+ it 'should wrap' do
5
+ io = StringIO.new
6
+ described_class.wrap(io) {|w| w.write 'xyz' * 1000 }
7
+ expect(io.size).to be_within(20).of(40)
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Feedx::Compression::None do
4
+ it 'should wrap' do
5
+ io = StringIO.new
6
+ described_class.wrap(io) {|w| w.write 'xyz' * 1000 }
7
+ expect(io.size).to eq(3000)
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Feedx::Compression do
4
+ it 'should resolve' do
5
+ expect(described_class.resolve(:gzip)).to eq(described_class::Gzip)
6
+ expect(described_class.resolve(:gz)).to eq(described_class::Gzip)
7
+ expect(described_class.resolve(nil)).to eq(described_class::None)
8
+ expect { described_class.resolve(:txt) }.to raise_error(/invalid compression txt/)
9
+ end
10
+
11
+ it 'should detect' do
12
+ expect(described_class.detect('path/to/file.jsonz')).to eq(described_class::Gzip)
13
+ expect(described_class.detect('path/to/file.json.gz')).to eq(described_class::Gzip)
14
+ expect(described_class.detect('path/to/file.json')).to eq(described_class::None)
15
+ expect(described_class.detect('path/to/file.pbz')).to eq(described_class::Gzip)
16
+ expect(described_class.detect('path/to/file.pb.gz')).to eq(described_class::Gzip)
17
+ expect(described_class.detect('path/to/file.pb')).to eq(described_class::None)
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Feedx::Format::JSON do
4
+ subject { described_class.new(io) }
5
+ let(:io) { StringIO.new }
6
+
7
+ it 'should write' do
8
+ subject.write(a: 1, b: 2)
9
+ subject.write(c: ['x'], d: true)
10
+ expect(io.string).to eq %({"a":1,"b":2}\n{"c":["x"],"d":true}\n)
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Feedx::Format::Protobuf do
4
+ subject { described_class.new(io) }
5
+ let(:io) { StringIO.new }
6
+
7
+ let(:model) do
8
+ Class.new Struct.new(:title) do
9
+ def to_pb
10
+ Feedx::TestCase::Message.new title: title
11
+ end
12
+ end
13
+ end
14
+
15
+ it 'should write' do
16
+ subject.write(model.new('X'))
17
+ subject.write(model.new('Y'))
18
+ expect(io.string.bytes).to eq([3, 10, 1, 88] + [3, 10, 1, 89])
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Feedx::Format do
4
+ it 'should resolve' do
5
+ expect(described_class.resolve(:json)).to eq(described_class::JSON)
6
+ expect(described_class.resolve(:pb)).to eq(described_class::Protobuf)
7
+ expect { described_class.resolve(:txt) }.to raise_error(/invalid format txt/)
8
+ end
9
+
10
+ it 'should detect' do
11
+ expect(described_class.detect('path/to/file.json')).to eq(described_class::JSON)
12
+ expect(described_class.detect('path/to/file.jsonz')).to eq(described_class::JSON)
13
+ expect(described_class.detect('path/to/file.json.gz')).to eq(described_class::JSON)
14
+ expect(described_class.detect('path/to/file.pb')).to eq(described_class::Protobuf)
15
+ expect(described_class.detect('path/to/file.pbz')).to eq(described_class::Protobuf)
16
+ expect(described_class.detect('path/to/file.pb.z')).to eq(described_class::Protobuf)
17
+ expect do
18
+ described_class.detect('path/to/file.txt')
19
+ end.to raise_error(/unable to detect format/)
20
+ end
21
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Feedx::Pusher 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
+ let :relation do
17
+ %w[x y z].map {|_t| model.new('t') } * 100
18
+ end
19
+
20
+ let(:tempdir) { Dir.mktmpdir }
21
+ after { FileUtils.rm_rf tempdir }
22
+
23
+ it 'should reject invalid inputs' do
24
+ expect do
25
+ described_class.new relation, "file://#{tempdir}/file.txt"
26
+ end.to raise_error(/unable to detect format/)
27
+ end
28
+
29
+ it 'should push compressed JSON' do
30
+ pusher = described_class.new relation, "file://#{tempdir}/file.jsonz"
31
+ size = pusher.perform
32
+ expect(size).to be_within(20).of(140)
33
+ expect(File.size("#{tempdir}/file.jsonz")).to eq(size)
34
+ end
35
+
36
+ it 'should push plain JSON' do
37
+ pusher = described_class.new relation, "file://#{tempdir}/file.json"
38
+ size = pusher.perform
39
+ expect(size).to be_within(0).of(15900)
40
+ expect(File.size("#{tempdir}/file.json")).to eq(size)
41
+ end
42
+
43
+ it 'should push compressed PB' do
44
+ pusher = described_class.new relation, "file://#{tempdir}/file.pbz"
45
+ size = pusher.perform
46
+ expect(size).to be_within(20).of(41)
47
+ expect(File.size("#{tempdir}/file.pbz")).to eq(size)
48
+ end
49
+
50
+ it 'should push plain PB' do
51
+ pusher = described_class.new relation, "file://#{tempdir}/file.pb"
52
+ size = pusher.perform
53
+ expect(size).to be_within(0).of(1200)
54
+ expect(File.size("#{tempdir}/file.pb")).to eq(size)
55
+ end
56
+ end
@@ -0,0 +1,15 @@
1
+ require 'rspec'
2
+ require 'feedx'
3
+ require 'google/protobuf'
4
+
5
+ Google::Protobuf::DescriptorPool.generated_pool.build do
6
+ add_message 'com.blacksquaremedia.feedx.testcase.Message' do
7
+ optional :title, :string, 1
8
+ end
9
+ end
10
+
11
+ module Feedx
12
+ module TestCase
13
+ Message = Google::Protobuf::DescriptorPool.generated_pool.lookup('com.blacksquaremedia.feedx.testcase.Message').msgclass
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feedx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Black Square Media Ltd
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-11-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bfs
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pbio
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Use feeds to exchange data between (micro-)services.
98
+ email:
99
+ - info@blacksquaremedia.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".editorconfig"
105
+ - ".gitignore"
106
+ - ".rubocop.yml"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - Gemfile.lock
110
+ - LICENSE
111
+ - README.md
112
+ - Rakefile
113
+ - feedx.gemspec
114
+ - lib/feedx.rb
115
+ - lib/feedx/compression.rb
116
+ - lib/feedx/compression/abstract.rb
117
+ - lib/feedx/compression/gzip.rb
118
+ - lib/feedx/compression/none.rb
119
+ - lib/feedx/format.rb
120
+ - lib/feedx/format/abstract.rb
121
+ - lib/feedx/format/json.rb
122
+ - lib/feedx/format/protobuf.rb
123
+ - lib/feedx/pusher.rb
124
+ - spec/feedx/compression/gzip_spec.rb
125
+ - spec/feedx/compression/none_spec.rb
126
+ - spec/feedx/compression_spec.rb
127
+ - spec/feedx/format/json_spec.rb
128
+ - spec/feedx/format/protobuf_spec.rb
129
+ - spec/feedx/format_spec.rb
130
+ - spec/feedx/pusher_spec.rb
131
+ - spec/spec_helper.rb
132
+ homepage: https://github.com/bsm/feedx
133
+ licenses:
134
+ - Apache-2.0
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '2.2'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.7.7
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Exchange data between components via feeds
156
+ test_files:
157
+ - spec/feedx/compression/gzip_spec.rb
158
+ - spec/feedx/compression/none_spec.rb
159
+ - spec/feedx/compression_spec.rb
160
+ - spec/feedx/format/json_spec.rb
161
+ - spec/feedx/format/protobuf_spec.rb
162
+ - spec/feedx/format_spec.rb
163
+ - spec/feedx/pusher_spec.rb
164
+ - spec/spec_helper.rb