feedx 0.1.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.
@@ -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