alephant-broker 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/alephant-broker.gemspec +1 -0
- data/lib/alephant/broker/call_environment.rb +12 -6
- data/lib/alephant/broker/models/request/post_request.rb +9 -5
- data/lib/alephant/broker/models/request_handler.rb +17 -7
- data/lib/alephant/broker/models/response.rb +0 -1
- data/lib/alephant/broker/models/response/asset_response.rb +58 -5
- data/lib/alephant/broker/models/response_factory.rb +8 -2
- data/lib/alephant/broker/version.rb +1 -1
- data/spec/asset_response_spec.rb +45 -43
- data/spec/batch_response_spec.rb +20 -18
- data/spec/rack_spec.rb +28 -17
- data/spec/spec_helper.rb +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b93740e5628e1841d3f296c5675304678cc0be7
|
4
|
+
data.tar.gz: 9412546f75f2f95a77c3b2cbfa3b53e98a296f9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34c3356dc0bab54c046bf2d8c269a07ea08c60ebfa9077d9f3eadd46a3df6fb48e1dd01d0bab710022607228629f98b6a4b91448153ed125cc38340e7a6e3626
|
7
|
+
data.tar.gz: 9414facbf42866a0eba6e903ec71ba365f0f342c183edb13a8710125c9fba411b08067a6493551bd1fb8223c64f3a5ef58722404fcf796c7faed88fae2ed56bd
|
data/alephant-broker.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module Alephant
|
2
4
|
module Broker
|
3
5
|
class CallEnvironment
|
@@ -9,23 +11,27 @@ module Alephant
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def method
|
12
|
-
|
14
|
+
settings['REQUEST_METHOD']
|
13
15
|
end
|
14
16
|
|
15
17
|
def post?
|
16
|
-
|
18
|
+
settings['REQUEST_METHOD'] == 'POST'
|
17
19
|
end
|
18
20
|
|
19
21
|
def get?
|
20
|
-
|
22
|
+
settings['REQUEST_METHOD'] == 'GET'
|
21
23
|
end
|
22
24
|
|
23
25
|
def query
|
24
|
-
|
26
|
+
settings['QUERY_STRING']
|
25
27
|
end
|
26
28
|
|
27
29
|
def path
|
28
|
-
|
30
|
+
settings['PATH_INFO']
|
31
|
+
end
|
32
|
+
|
33
|
+
def request_type
|
34
|
+
path.split('/')[1]
|
29
35
|
end
|
30
36
|
|
31
37
|
def data
|
@@ -35,7 +41,7 @@ module Alephant
|
|
35
41
|
private
|
36
42
|
|
37
43
|
def rack_input
|
38
|
-
(
|
44
|
+
(settings['rack.input'].read).tap { settings['rack.input'].rewind } # http://rack.rubyforge.org/doc/SPEC.html
|
39
45
|
end
|
40
46
|
|
41
47
|
def parse(json)
|
@@ -3,16 +3,16 @@ require 'alephant/broker/models/request'
|
|
3
3
|
module Alephant
|
4
4
|
module Broker
|
5
5
|
class PostRequest < Request
|
6
|
-
attr_reader :type, :component_id, :options, :content_type
|
6
|
+
attr_reader :type, :renderer_id, :component_id, :options, :content_type
|
7
7
|
|
8
8
|
def initialize
|
9
|
-
@
|
9
|
+
@renderer_id = batch_id
|
10
10
|
@content_type = 'application/json'
|
11
11
|
super(:batch)
|
12
12
|
end
|
13
13
|
|
14
14
|
def components
|
15
|
-
@requested_components ||= components_for
|
15
|
+
@requested_components ||= components_for env.path
|
16
16
|
end
|
17
17
|
|
18
18
|
def set_component(id, options)
|
@@ -22,6 +22,10 @@ module Alephant
|
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
|
+
def env
|
26
|
+
@env ||= RequestStore.store[:env]
|
27
|
+
end
|
28
|
+
|
25
29
|
def components_for(path)
|
26
30
|
request_parts = path.split('/')
|
27
31
|
|
@@ -33,11 +37,11 @@ module Alephant
|
|
33
37
|
end
|
34
38
|
|
35
39
|
def batch_id
|
36
|
-
|
40
|
+
env.data['batch_id']
|
37
41
|
end
|
38
42
|
|
39
43
|
def batched
|
40
|
-
|
44
|
+
env.data['components'].reduce({ :components => [] }) do |obj, component|
|
41
45
|
obj.tap { |o| o[:components].push(component) }
|
42
46
|
end
|
43
47
|
end
|
@@ -7,24 +7,30 @@ module Alephant
|
|
7
7
|
include Logger
|
8
8
|
|
9
9
|
def initialize(config)
|
10
|
-
@
|
11
|
-
@request = RequestFactory.process(request_type)
|
12
|
-
@response_factory = ResponseFactory.new(config)
|
10
|
+
@config = config
|
13
11
|
end
|
14
12
|
|
15
13
|
def process
|
16
14
|
begin
|
17
|
-
|
15
|
+
response_factory.response_from(request)
|
18
16
|
rescue Exception => e
|
19
17
|
logger.info("Broker.requestHandler.process: Exception raised (#{e.message})")
|
20
|
-
|
18
|
+
response_factory.response(500)
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
24
22
|
private
|
25
23
|
|
24
|
+
def request
|
25
|
+
@request ||= RequestFactory.process(request_type)
|
26
|
+
end
|
27
|
+
|
28
|
+
def response_factory
|
29
|
+
@response_factory ||= ResponseFactory.new(@config)
|
30
|
+
end
|
31
|
+
|
26
32
|
def request_type
|
27
|
-
case
|
33
|
+
case env.request_type
|
28
34
|
when 'components'
|
29
35
|
component_type
|
30
36
|
when 'status'
|
@@ -35,13 +41,17 @@ module Alephant
|
|
35
41
|
end
|
36
42
|
|
37
43
|
def component_type
|
38
|
-
case
|
44
|
+
case env.method
|
39
45
|
when 'POST'
|
40
46
|
:component_batch
|
41
47
|
when 'GET'
|
42
48
|
:component
|
43
49
|
end
|
44
50
|
end
|
51
|
+
|
52
|
+
def env
|
53
|
+
@env ||= RequestStore.store[:env]
|
54
|
+
end
|
45
55
|
end
|
46
56
|
end
|
47
57
|
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
require 'crimp'
|
1
2
|
require 'alephant/cache'
|
2
3
|
require 'alephant/lookup'
|
3
4
|
require 'alephant/broker/errors/invalid_cache_key'
|
5
|
+
require 'alephant/sequencer'
|
4
6
|
|
5
7
|
module Alephant
|
6
8
|
module Broker
|
@@ -11,15 +13,14 @@ module Alephant
|
|
11
13
|
|
12
14
|
def initialize(request, config)
|
13
15
|
@request = request
|
14
|
-
@
|
15
|
-
@cache = Cache.new(config[:bucket_id], config[:path])
|
16
|
+
@config = config
|
16
17
|
super()
|
17
18
|
end
|
18
19
|
|
19
20
|
def setup
|
20
21
|
begin
|
21
22
|
self.content_type = request.content_type
|
22
|
-
self.content =
|
23
|
+
self.content = cache.get(s3_path)
|
23
24
|
rescue AWS::S3::Errors::NoSuchKey, InvalidCacheKey => e
|
24
25
|
set_error_for(e, 404)
|
25
26
|
rescue Exception => e
|
@@ -29,14 +30,66 @@ module Alephant
|
|
29
30
|
|
30
31
|
private
|
31
32
|
|
33
|
+
def cache
|
34
|
+
@cache ||= Alephant::Cache.new(config[:bucket_id], config[:path])
|
35
|
+
end
|
36
|
+
|
32
37
|
def set_error_for(exception, status)
|
33
38
|
logger.info("Broker.assetResponse.set_error_for: #{status} exception raised (#{exception.message})")
|
34
39
|
self.status = status
|
35
40
|
self.content = exception.message
|
36
41
|
end
|
37
42
|
|
38
|
-
def
|
39
|
-
|
43
|
+
def s3_path
|
44
|
+
lookup.read(id, request.options, version).tap { |cache_id| raise InvalidCacheKey if cache_id.nil? }
|
45
|
+
end
|
46
|
+
|
47
|
+
def lookup
|
48
|
+
@lookup ||= Alephant::Lookup.create(config[:lookup_table_name])
|
49
|
+
end
|
50
|
+
|
51
|
+
def asset?
|
52
|
+
request.type == :asset
|
53
|
+
end
|
54
|
+
|
55
|
+
def key
|
56
|
+
asset? ? component_key : renderer_key
|
57
|
+
end
|
58
|
+
|
59
|
+
def component_key
|
60
|
+
"#{component_id}/#{opts_hash}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def renderer_key
|
64
|
+
"#{renderer_id}/#{opts_hash}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def id
|
68
|
+
asset? ? component_id : renderer_id
|
69
|
+
end
|
70
|
+
|
71
|
+
def component_id
|
72
|
+
request.component_id
|
73
|
+
end
|
74
|
+
|
75
|
+
def renderer_id
|
76
|
+
request.renderer_id
|
77
|
+
end
|
78
|
+
|
79
|
+
def opts_hash
|
80
|
+
@opts_hash ||= Crimp.signature(request.options)
|
81
|
+
end
|
82
|
+
|
83
|
+
def version
|
84
|
+
@version ||= sequencer.get_last_seen
|
85
|
+
end
|
86
|
+
|
87
|
+
def sequencer
|
88
|
+
@sequencer ||= Alephant::Sequencer.create(config[:sequencer_table_name], key)
|
89
|
+
end
|
90
|
+
|
91
|
+
def config
|
92
|
+
@config
|
40
93
|
end
|
41
94
|
end
|
42
95
|
end
|
@@ -13,9 +13,9 @@ module Alephant
|
|
13
13
|
def response_from(request)
|
14
14
|
case request.type
|
15
15
|
when :asset
|
16
|
-
AssetResponse.new(request,
|
16
|
+
AssetResponse.new(request, config)
|
17
17
|
when :batch
|
18
|
-
BatchResponse.new(request,
|
18
|
+
BatchResponse.new(request, config).process
|
19
19
|
when :status
|
20
20
|
response(200)
|
21
21
|
when :notfound
|
@@ -25,6 +25,12 @@ module Alephant
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
private
|
29
|
+
|
30
|
+
def config
|
31
|
+
@config
|
32
|
+
end
|
33
|
+
|
28
34
|
def response(status)
|
29
35
|
Response.new(status)
|
30
36
|
end
|
data/spec/asset_response_spec.rb
CHANGED
@@ -1,73 +1,75 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Alephant::Broker::AssetResponse do
|
4
|
+
subject { Alephant::Broker::AssetResponse }
|
5
|
+
|
4
6
|
describe "#initialize(request, config)" do
|
5
7
|
let(:location) { 'test_location' }
|
8
|
+
|
6
9
|
let(:config) {{
|
7
10
|
:lookup_table_name => 'test_table',
|
8
11
|
:bucket_id => 'test_bucket',
|
9
12
|
:path => 'test_path'
|
10
13
|
}}
|
14
|
+
|
11
15
|
let(:request) { double(
|
12
16
|
"Alephant::Broker::Request",
|
13
17
|
:component_id => 'test',
|
14
18
|
:content_type => 'text/html',
|
19
|
+
:type => :asset,
|
15
20
|
:options => { :variant => 'test_variant' }
|
16
21
|
)
|
17
22
|
}
|
18
23
|
|
19
24
|
before do
|
20
|
-
|
21
|
-
Alephant::Lookup.stub(:create).and_return(@lookup_table)
|
22
|
-
end
|
23
|
-
|
24
|
-
it "Should return the content from a successful cache lookup" do
|
25
|
-
allow(@lookup_table)
|
26
|
-
.to receive(:read)
|
27
|
-
.with(request.options)
|
28
|
-
.and_return(location)
|
29
|
-
|
30
|
-
Alephant::Cache
|
31
|
-
.any_instance
|
32
|
-
.stub(:initialize)
|
33
|
-
|
34
|
-
Alephant::Cache
|
25
|
+
subject
|
35
26
|
.any_instance
|
36
|
-
.stub(:
|
37
|
-
.
|
38
|
-
.and_return('Test cache content')
|
39
|
-
|
40
|
-
instance = Alephant::Broker::AssetResponse.new(request, config)
|
41
|
-
|
42
|
-
expect(instance.content).to eq('Test cache content')
|
43
|
-
expect(instance.status).to eq(200)
|
27
|
+
.stub(:s3_path)
|
28
|
+
.and_return(:foo)
|
44
29
|
end
|
45
30
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
31
|
+
context "successful" do
|
32
|
+
before(:each) do
|
33
|
+
subject
|
34
|
+
.any_instance
|
35
|
+
.stub(:cache)
|
36
|
+
.and_return(double(:get => 'Test'))
|
37
|
+
end
|
51
38
|
|
52
|
-
|
53
|
-
.
|
54
|
-
.stub(:initialize)
|
39
|
+
it "Should return the content from a successful cache lookup" do
|
40
|
+
instance = subject.new(request, config)
|
55
41
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
expect(instance.status).to eq(404)
|
42
|
+
expect(instance.content).to eq('Test')
|
43
|
+
expect(instance.status).to eq(200)
|
44
|
+
end
|
60
45
|
end
|
61
46
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
47
|
+
context "client failure" do
|
48
|
+
before(:each) do
|
49
|
+
subject
|
50
|
+
.any_instance
|
51
|
+
.stub(:cache)
|
52
|
+
.and_raise(Alephant::Broker::InvalidCacheKey)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should return a 404 if lookup can't find a valid location" do
|
56
|
+
instance = subject.new(request, config)
|
57
|
+
expect(instance.status).to eq(404)
|
58
|
+
end
|
59
|
+
end
|
69
60
|
|
70
|
-
|
61
|
+
context "server failure" do
|
62
|
+
before(:each) do
|
63
|
+
subject
|
64
|
+
.any_instance
|
65
|
+
.stub(:cache)
|
66
|
+
.and_raise(Exception)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should return a 500 for any other exceptions" do
|
70
|
+
instance = subject.new(request, config)
|
71
|
+
expect(instance.status).to eq(500)
|
72
|
+
end
|
71
73
|
end
|
72
74
|
end
|
73
75
|
end
|
data/spec/batch_response_spec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Alephant::Broker::BatchResponse do
|
4
|
+
subject { Alephant::Broker::BatchResponse }
|
5
|
+
|
4
6
|
let (:config) {{
|
5
7
|
:lookup_table_name => 'test_table',
|
6
8
|
:bucket_id => 'test_bucket',
|
@@ -15,6 +17,7 @@ describe Alephant::Broker::BatchResponse do
|
|
15
17
|
:content_type => 'application/json',
|
16
18
|
:set_component => nil,
|
17
19
|
:component_id => nil,
|
20
|
+
:renderer_id => nil,
|
18
21
|
:components => {
|
19
22
|
:batch_id => :baz,
|
20
23
|
:components => [
|
@@ -25,31 +28,20 @@ describe Alephant::Broker::BatchResponse do
|
|
25
28
|
}
|
26
29
|
|
27
30
|
before do
|
28
|
-
|
29
|
-
|
30
|
-
Alephant::Lookup
|
31
|
-
.stub(:create)
|
32
|
-
.and_return(@lookup_table)
|
33
|
-
|
34
|
-
Alephant::Cache
|
31
|
+
Alephant::Broker::AssetResponse
|
35
32
|
.any_instance
|
36
33
|
.stub(:initialize)
|
37
|
-
|
38
|
-
Alephant::Cache
|
39
|
-
.any_instance
|
40
|
-
.stub(:get)
|
41
|
-
.and_return('Test response')
|
42
34
|
end
|
43
35
|
|
44
36
|
describe "#process" do
|
45
37
|
context "if a component is unrecognised" do
|
46
38
|
before(:each) do
|
47
|
-
Alephant::
|
39
|
+
Alephant::Broker::AssetResponse
|
48
40
|
.any_instance
|
49
|
-
.stub(:
|
50
|
-
.
|
41
|
+
.stub(:status)
|
42
|
+
.and_return(404)
|
51
43
|
|
52
|
-
instance =
|
44
|
+
instance = subject.new(post_request, config)
|
53
45
|
json = JSON.parse(instance.process.content)
|
54
46
|
@bad_component = json.fetch('components')[1]
|
55
47
|
end
|
@@ -65,7 +57,17 @@ describe Alephant::Broker::BatchResponse do
|
|
65
57
|
|
66
58
|
context "if a component is recognised" do
|
67
59
|
before(:each) do
|
68
|
-
|
60
|
+
Alephant::Broker::AssetResponse
|
61
|
+
.any_instance
|
62
|
+
.stub(:status)
|
63
|
+
.and_return(200)
|
64
|
+
|
65
|
+
Alephant::Broker::AssetResponse
|
66
|
+
.any_instance
|
67
|
+
.stub(:content)
|
68
|
+
.and_return('Test')
|
69
|
+
|
70
|
+
@instance = subject.new(post_request, config)
|
69
71
|
@content = @instance.process.content
|
70
72
|
@json = JSON.parse(@content)
|
71
73
|
end
|
@@ -77,7 +79,7 @@ describe Alephant::Broker::BatchResponse do
|
|
77
79
|
end
|
78
80
|
|
79
81
|
it "set @content to be JSON string containing retrieved components" do
|
80
|
-
compiled_json = '{"batch_id":"baz","components":[{"component":"foo1","options":{"variant":"bar1"},"status":200,"body":"Test
|
82
|
+
compiled_json = '{"batch_id":"baz","components":[{"component":"foo1","options":{"variant":"bar1"},"status":200,"body":"Test"},{"component":"foo2","options":{"variant":"bar2"},"status":200,"body":"Test"}]}'
|
81
83
|
expect(@content).to eq(compiled_json)
|
82
84
|
end
|
83
85
|
end
|
data/spec/rack_spec.rb
CHANGED
@@ -12,11 +12,19 @@ describe 'Broker Rack Application' do
|
|
12
12
|
before do
|
13
13
|
RequestStore.store[:env] = nil
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
Alephant::
|
15
|
+
Alephant::Broker::AssetResponse
|
16
|
+
.any_instance
|
17
|
+
.stub(:initialize)
|
18
|
+
|
19
|
+
Alephant::Broker::AssetResponse
|
20
|
+
.any_instance
|
21
|
+
.stub(:status)
|
22
|
+
.and_return(200)
|
23
|
+
|
24
|
+
Alephant::Broker::AssetResponse
|
25
|
+
.any_instance
|
26
|
+
.stub(:content)
|
27
|
+
.and_return('Test')
|
20
28
|
end
|
21
29
|
|
22
30
|
def app
|
@@ -40,41 +48,44 @@ describe 'Broker Rack Application' do
|
|
40
48
|
end
|
41
49
|
|
42
50
|
it "Test asset data is returned" do
|
43
|
-
allow(@lookup_table).to receive(:read).and_return('some_location')
|
44
|
-
|
45
51
|
get '/components/test_component'
|
52
|
+
|
46
53
|
expect(last_response).to be_ok
|
47
|
-
expect(last_response.body).to eq('Test
|
54
|
+
expect(last_response.body).to eq('Test')
|
48
55
|
end
|
49
56
|
|
50
57
|
it "Tests query string parameters are passed correctly to lookup" do
|
51
|
-
variant = {:variant => 'test_variant'}
|
52
|
-
allow(@lookup_table).to receive(:read).with(variant).and_return('some_location')
|
53
|
-
|
54
58
|
get '/components/test_component?variant=test_variant'
|
59
|
+
|
55
60
|
expect(last_response).to be_ok
|
56
|
-
expect(last_response.body).to eq('Test
|
61
|
+
expect(last_response.body).to eq('Test')
|
57
62
|
end
|
58
63
|
|
59
64
|
it "Tests 404 when lookup doesn't return a valid location" do
|
60
|
-
|
65
|
+
Alephant::Broker::AssetResponse
|
66
|
+
.any_instance
|
67
|
+
.stub(:status)
|
68
|
+
.and_return(404)
|
61
69
|
|
62
70
|
get '/components/test_component'
|
71
|
+
|
63
72
|
expect(last_response.status).to eq(404)
|
64
73
|
end
|
65
74
|
|
66
75
|
it "Tests 500 when exception is raised in application" do
|
67
|
-
|
76
|
+
Alephant::Broker::AssetResponse
|
77
|
+
.any_instance
|
78
|
+
.stub(:status)
|
79
|
+
.and_return(500)
|
68
80
|
|
69
81
|
get '/components/test_component'
|
82
|
+
|
70
83
|
expect(last_response.status).to eq(500)
|
71
84
|
end
|
72
85
|
|
73
86
|
it "Test batch asset data is returned" do
|
74
|
-
allow(@lookup_table).to receive(:read).and_return('some_location')
|
75
|
-
|
76
87
|
json = '{"batch_id":"baz","components":[{"component":"ni_council_results_table"},{"component":"ni_council_results_table"}]}'
|
77
|
-
compiled_json = '{"batch_id":"baz","components":[{"component":"ni_council_results_table","status":200,"body":"Test
|
88
|
+
compiled_json = '{"batch_id":"baz","components":[{"component":"ni_council_results_table","status":200,"body":"Test"},{"component":"ni_council_results_table","status":200,"body":"Test"}]}'
|
78
89
|
|
79
90
|
post '/components/batch', json, "CONTENT_TYPE" => "application/json"
|
80
91
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alephant-broker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Jack
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -206,6 +206,20 @@ dependencies:
|
|
206
206
|
version: '0'
|
207
207
|
prerelease: false
|
208
208
|
type: :runtime
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: alephant-sequencer
|
211
|
+
version_requirements: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - '>='
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
requirement: !ruby/object:Gem::Requirement
|
217
|
+
requirements:
|
218
|
+
- - '>='
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '0'
|
221
|
+
prerelease: false
|
222
|
+
type: :runtime
|
209
223
|
description: Brokers requests for alephant components
|
210
224
|
email:
|
211
225
|
- stevenmajack@gmail.com
|