nasreddin 0.3.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +42 -0
- data/lib/nasreddin.rb +4 -0
- data/lib/nasreddin/api-server.rb +136 -0
- data/lib/nasreddin/remote_torquebox_adapter.rb +38 -0
- data/lib/nasreddin/resource.rb +175 -0
- data/lib/nasreddin/version.rb +3 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 14b6167ede8abe24650d6a757cf8559cc8e2855f
|
4
|
+
data.tar.gz: cb9108d4eb76c807370a626c736ece44260892ac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 83bc41838bb86061abecba46b6d64f101d9135ce620f1d6fe05b22abaf713a571bc4707b2896f82436c1d128c11044394ffbb18250d0699ee9e5f573ef3dcb8a
|
7
|
+
data.tar.gz: 18314db0937c622bb89f01cd41e2d86b19a5830e386450b52bc2b867e81e50d95dd5dca6d6d875a3ea7f371cf156d0e56630ffabe0c4db79ad4cff7e575d05f5
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
> "Nasreddin, your donkey has been lost."
|
2
|
+
|
3
|
+
> "Thank goodness I was not on the donkey at the time, or I would be lost too."
|
4
|
+
|
5
|
+
|
6
|
+
## What is Nasreddin?
|
7
|
+
|
8
|
+
Nasreddin Hoca was a wise scolar from 13th century Anatolia. This, however, is a library designed to enable know-nothing
|
9
|
+
distribution of data requests between varying services.
|
10
|
+
|
11
|
+
To connect two services, one needs to include the Nasreddin::APIServer middleware and indicate what resources it is offering:
|
12
|
+
```ruby
|
13
|
+
require 'nasreddin/api-server'
|
14
|
+
|
15
|
+
use Nasreddin::APIServer, resources: %w| users |
|
16
|
+
run MyApp
|
17
|
+
```
|
18
|
+
Then, the consumer can use the Nasreddin::Resource class to generate new classes that will talk to the APIServer when making
|
19
|
+
requests.
|
20
|
+
|
21
|
+
|
22
|
+
An example implementation of Nasreddin::Resource is provided below
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
require 'nasreddin/resource'
|
26
|
+
|
27
|
+
class User < Nasreddin::Resource('users') # name of endpoint for resource
|
28
|
+
end
|
29
|
+
|
30
|
+
```
|
31
|
+
|
32
|
+
|
33
|
+
## Caveats
|
34
|
+
|
35
|
+
Currently, Nasreddin relies on JRuby and TorqueBox/HornetQ.
|
36
|
+
|
37
|
+
|
38
|
+
## License
|
39
|
+
|
40
|
+
Copyright (C) 2012-2013 Burnside Digital
|
41
|
+
|
42
|
+
Licensed under the BSD 2-Clause License. See COPYING for license details.
|
data/lib/nasreddin.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Nasreddin
|
5
|
+
|
6
|
+
class APIServer
|
7
|
+
|
8
|
+
def initialize(app, options = {})
|
9
|
+
@app = app
|
10
|
+
@threads = {}
|
11
|
+
@resources = options[:resources]
|
12
|
+
@route_prefix = options[:route_prefix]
|
13
|
+
if @resources
|
14
|
+
@resources.each do |resource|
|
15
|
+
@threads[resource] = Thread.new { Nasreddin::APIServerResource.new(@route_prefix,resource,@app).run }
|
16
|
+
end
|
17
|
+
else
|
18
|
+
$stderr.puts "WARNING: Nasreddin::APIServer is being used without any resources specified!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
params = Rack::Request.new(env).params
|
24
|
+
|
25
|
+
if is_heartbeat?(params)
|
26
|
+
res = params['resource'] = params['resources'].pop
|
27
|
+
params["resources.#{res}"] = @resources
|
28
|
+
Nasreddin::Resource(@resource).remote_call(params)
|
29
|
+
else
|
30
|
+
@app.call(env)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def is_heartbeat?(params)
|
35
|
+
params.has_key? '__hearbeat__'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class APIServerResource
|
40
|
+
|
41
|
+
DEFAULT_ENV = {
|
42
|
+
'rack.errors' => $stderr,
|
43
|
+
'rack.input' => StringIO.new,
|
44
|
+
'rack.version' => [1, 1],
|
45
|
+
'rack.multithread' => true,
|
46
|
+
'rack.multiprocess' => true,
|
47
|
+
'rack.run_once' => false,
|
48
|
+
'HTTP_ACCEPT' => 'application/json',
|
49
|
+
'HTTP_USER_AGENT' => 'NasreddinAPI'
|
50
|
+
}
|
51
|
+
|
52
|
+
def initialize(route_prefix,resource,app)
|
53
|
+
@resource = resource
|
54
|
+
@route_prefix = route_prefix
|
55
|
+
@app = app
|
56
|
+
end
|
57
|
+
|
58
|
+
def queue
|
59
|
+
@queue ||= TorqueBox::Messaging::Queue.start("/queues/#{@resource}", durable: false)
|
60
|
+
end
|
61
|
+
|
62
|
+
def run
|
63
|
+
loop do
|
64
|
+
begin
|
65
|
+
queue.receive_and_publish &method(:process_incoming_message)
|
66
|
+
rescue Exception => err
|
67
|
+
$stderr.puts "Error processing request: #{err.class} - #{err.message}"
|
68
|
+
if err.kind_of?(Java::JavaxJms::JMSException)
|
69
|
+
break
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
queue.stop
|
74
|
+
end
|
75
|
+
|
76
|
+
def is_heartbeat?(msg)
|
77
|
+
msg[:params] && msg[:params].has_key?('__heartbeat__')
|
78
|
+
end
|
79
|
+
|
80
|
+
def call(env)
|
81
|
+
@app.call(env)
|
82
|
+
end
|
83
|
+
|
84
|
+
def process_incoming_message(msg)
|
85
|
+
return heartbeat_ok if is_heartbeat?(msg)
|
86
|
+
|
87
|
+
begin
|
88
|
+
status, headers, body = call(env(msg))
|
89
|
+
|
90
|
+
resp = ''
|
91
|
+
body.each { |d| resp += d.to_s }
|
92
|
+
body.close if body.respond_to?(:close)
|
93
|
+
|
94
|
+
rescue Exception => err
|
95
|
+
resp = "#{err.message}\n\n#{err.backtrace.join("\n")}"
|
96
|
+
end
|
97
|
+
|
98
|
+
[status, headers, resp]
|
99
|
+
end
|
100
|
+
|
101
|
+
def heartbeat_ok
|
102
|
+
[200, nil, "OK"]
|
103
|
+
end
|
104
|
+
|
105
|
+
def env(msg)
|
106
|
+
env = DEFAULT_ENV.clone
|
107
|
+
env['rack.url_scheme'] = (msg.delete(:secure) ? 'https' : 'http')
|
108
|
+
method = msg.delete(:method) || 'GET'
|
109
|
+
env['REQUEST_METHOD'] = method.to_s.upcase
|
110
|
+
env['QUERY_STRING'] = queryize(msg.delete(:params)) || ''
|
111
|
+
env['SCRIPT_NAME'] = "/#{@route_prefix}" || ''
|
112
|
+
env['PATH_INFO'] = "/#{@resource}"
|
113
|
+
if ((id = msg.delete(:id)))
|
114
|
+
env['PATH_INFO'] += "/#{id}"
|
115
|
+
end
|
116
|
+
if ((path = msg.delete(:path)))
|
117
|
+
env['PATH_INFO'] += "/#{path}"
|
118
|
+
end
|
119
|
+
env['REQUEST_URI'] = "#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
|
120
|
+
env.merge!(Hash[msg.map { |k, v| [k.to_s, v] }])
|
121
|
+
env
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def queryize(params = {})
|
127
|
+
params.map do |key, value|
|
128
|
+
if value.is_a? Array
|
129
|
+
value.map { |v| URI.encode("#{key}[]=#{v}") }
|
130
|
+
else
|
131
|
+
URI.encode("#{key}=#{value}")
|
132
|
+
end
|
133
|
+
end.join('&') unless params.nil?
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#require 'torquebox/messaging'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
module Nasreddin
|
5
|
+
class RemoteTorqueboxAdapter
|
6
|
+
attr_accessor :resource, :klass
|
7
|
+
|
8
|
+
def load_data(data,resource, as_objects = true)
|
9
|
+
resp = MultiJson.load(data)
|
10
|
+
resp = resp[@resource] if resp.respond_to?(:keys) && resp.keys.include?(@resource)
|
11
|
+
if resp.kind_of? Array
|
12
|
+
as_objects ? resp.map { |r| @klass.new(r) } : resp
|
13
|
+
else
|
14
|
+
as_objects ? @klass.new(resp) : resp
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def succeded?(status)
|
19
|
+
status != nil && status > 199 && status < 300
|
20
|
+
end
|
21
|
+
|
22
|
+
def queue
|
23
|
+
@queue ||= TorqueBox::Messaging::Queue.start("/queues/#{@resource}", durable: false)
|
24
|
+
end
|
25
|
+
|
26
|
+
def call(params, as_new_objects=false)
|
27
|
+
status, _, data = *(queue.publish_and_receive(params, persistant: false))
|
28
|
+
values = load_data(data,@resource,as_new_objects) if data && !data.empty?
|
29
|
+
[ succeded?(status), values ]
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(resource, klass)
|
33
|
+
@resource = resource
|
34
|
+
@klass = klass
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'torquebox/messaging'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
module Nasreddin
|
5
|
+
|
6
|
+
class SaveError < Exception ; end
|
7
|
+
|
8
|
+
# == Nasreddin Resource
|
9
|
+
# Provides a base class for implementing an API backed data object.
|
10
|
+
# A minimal implementation could be:
|
11
|
+
# class Car < Nasreddin::Resource('cars')
|
12
|
+
# end
|
13
|
+
class Resource
|
14
|
+
class<<self
|
15
|
+
attr_accessor :resource
|
16
|
+
|
17
|
+
def subclasses; (@subclasses ||= []); end
|
18
|
+
|
19
|
+
def inherited(sub)
|
20
|
+
subclasses << sub
|
21
|
+
sub.resource = @resource
|
22
|
+
end
|
23
|
+
|
24
|
+
def remote
|
25
|
+
@remote ||= Nasreddin::RemoteTorqueboxAdapter.new(@resource, self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remote_call(params = {}, as_objects=true)
|
29
|
+
success, values = remote.call(params, as_objects)
|
30
|
+
values
|
31
|
+
end
|
32
|
+
|
33
|
+
# Allows fetching of all entities without requiring filtering
|
34
|
+
# parameters.
|
35
|
+
def all
|
36
|
+
remote_call({ method: 'GET' }, true)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Allows searching for a specific entity or a collection of
|
40
|
+
# entities that match a certain criteria.
|
41
|
+
# example usage:
|
42
|
+
# Car.find(15)
|
43
|
+
# # => #<Car:0x5fafa486>
|
44
|
+
#
|
45
|
+
# Car.find(make: 'Ford')
|
46
|
+
# # => [ #<Car:0x5fafa486> ]
|
47
|
+
#
|
48
|
+
# Car.find(15, make: 'Ford')
|
49
|
+
# # => [ #<Car:0x5fafa486> ]
|
50
|
+
def find(*args)
|
51
|
+
params = args.last.kind_of?(Hash) ? args.pop : {}
|
52
|
+
id = args.shift
|
53
|
+
|
54
|
+
remote_call({ method: 'GET', id: id, params: params })
|
55
|
+
end
|
56
|
+
|
57
|
+
# Allows creating a new record in one shot
|
58
|
+
# returns true if the record was created
|
59
|
+
# example usage:
|
60
|
+
# Car.create make: 'Ford', model: 'Focus'
|
61
|
+
# # => true or false
|
62
|
+
def create(properties = {})
|
63
|
+
new(properties).save
|
64
|
+
end
|
65
|
+
|
66
|
+
# Allows destroying a resource without finding it
|
67
|
+
# example usage:
|
68
|
+
# Car.destroy(15)
|
69
|
+
# # => true or false
|
70
|
+
def destroy(id)
|
71
|
+
remote_call({ method: 'DELETE', id: id }, false).empty?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Custom to_json implementation
|
76
|
+
# passes through options to @data
|
77
|
+
def to_json(options={})
|
78
|
+
@data.to_json(options)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Custom as_json implementation
|
82
|
+
# passes through options to @data
|
83
|
+
def as_json(options={})
|
84
|
+
@data.as_json(options)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Checks if the current instance has
|
88
|
+
# already been deleted
|
89
|
+
def deleted?
|
90
|
+
@deleted
|
91
|
+
end
|
92
|
+
|
93
|
+
# Saves the current resource instance
|
94
|
+
# if the instance has an ID it sends a PUT request
|
95
|
+
# otherwise it sends a POST request
|
96
|
+
# will raise an error if the object has been deleted
|
97
|
+
# example usage:
|
98
|
+
# car = Car.find(15)
|
99
|
+
# car.miles += 1500
|
100
|
+
# car.save
|
101
|
+
# # => true or false
|
102
|
+
def save
|
103
|
+
raise SaveError.new("Cannot save a deleted resource") if deleted?
|
104
|
+
|
105
|
+
if @data['id'].to_s.empty?
|
106
|
+
remote_call({ method: 'POST', params: @data })
|
107
|
+
else
|
108
|
+
remote_call({ method: 'PUT', id: @data['id'], params: @data })
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def remote_call(params)
|
113
|
+
success, values = remote.call(params, false)
|
114
|
+
@data = values if values && !values.empty?
|
115
|
+
success
|
116
|
+
end
|
117
|
+
|
118
|
+
# Destroys the current resource instance
|
119
|
+
# example usage:
|
120
|
+
# car = Car.find(15)
|
121
|
+
# car.destroy
|
122
|
+
# # => true or false
|
123
|
+
def destroy
|
124
|
+
if !deleted?
|
125
|
+
@deleted = remote_call({ method: 'DELETE', id: @data['id'] })
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Initialize a new instance
|
130
|
+
# also defines setters for any values given
|
131
|
+
# example usage:
|
132
|
+
# car = Car.new make: 'Ford', model: 'Mustang'
|
133
|
+
# car.respond_to? :make=
|
134
|
+
# # => true
|
135
|
+
def initialize(data={})
|
136
|
+
@deleted = false
|
137
|
+
@data = data
|
138
|
+
@data.each do |key, value|
|
139
|
+
unless respond_to?("#{key.to_s}=")
|
140
|
+
self.class.send(:define_method, "#{key.to_s}=") do |other|
|
141
|
+
@data[key.to_s] = other
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def method_missing(mid, *args, &block)
|
148
|
+
if @data.keys.include?(mid.to_s)
|
149
|
+
@data[mid.to_s]
|
150
|
+
else
|
151
|
+
super
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def respond_to?(mid, include_private=false)
|
156
|
+
@data.keys.include?(mid.to_s) || super
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def remote
|
162
|
+
self.class.send(:remote)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.Resource(name)
|
168
|
+
klass = Resource.subclasses.find { |k| k.resource == name }
|
169
|
+
unless klass
|
170
|
+
klass = Class.new(Resource)
|
171
|
+
klass.resource = name
|
172
|
+
end
|
173
|
+
klass
|
174
|
+
end
|
175
|
+
end
|
metadata
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nasreddin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.10
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Ballanco
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: torquebox
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '2.0'
|
25
|
+
prerelease: false
|
26
|
+
type: :runtime
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.4.1
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 1.4.1
|
39
|
+
prerelease: false
|
40
|
+
type: :runtime
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: multi_json
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.5.0
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ~>
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.5.0
|
53
|
+
prerelease: false
|
54
|
+
type: :runtime
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack-test
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.6.1
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ~>
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 0.6.1
|
67
|
+
prerelease: false
|
68
|
+
type: :development
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: torquebox-server
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ~>
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '2.0'
|
81
|
+
prerelease: false
|
82
|
+
type: :development
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: kramdown
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.13.7
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ~>
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 0.13.7
|
95
|
+
prerelease: false
|
96
|
+
type: :development
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.8.2
|
104
|
+
requirement: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ~>
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: 0.8.2
|
109
|
+
prerelease: false
|
110
|
+
type: :development
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: bacon
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.1.0
|
118
|
+
requirement: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ~>
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 1.1.0
|
123
|
+
prerelease: false
|
124
|
+
type: :development
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: mocha-on-bacon
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirement: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
prerelease: false
|
138
|
+
type: :development
|
139
|
+
description: ' Nasreddin is a library to make distributed calls via HornetQ '
|
140
|
+
email:
|
141
|
+
- jballanc@gmail.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ./lib/nasreddin.rb
|
147
|
+
- ./lib/nasreddin/api-server.rb
|
148
|
+
- ./lib/nasreddin/remote_torquebox_adapter.rb
|
149
|
+
- ./lib/nasreddin/resource.rb
|
150
|
+
- ./lib/nasreddin/version.rb
|
151
|
+
- README.md
|
152
|
+
homepage: ''
|
153
|
+
licenses: []
|
154
|
+
metadata: {}
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - '>='
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - '>='
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
requirements: []
|
170
|
+
rubyforge_project: ''
|
171
|
+
rubygems_version: 2.1.10
|
172
|
+
signing_key:
|
173
|
+
specification_version: 4
|
174
|
+
summary: A library for making distributed calls via HornetQ
|
175
|
+
test_files: []
|
176
|
+
has_rdoc:
|