lagomorph 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +83 -0
- data/Rakefile +7 -0
- data/lagomorph.gemspec +47 -0
- data/lib/lagomorph.rb +32 -0
- data/lib/lagomorph/json_parser.rb +30 -0
- data/lib/lagomorph/metadata_adapter.rb +17 -0
- data/lib/lagomorph/queue_adapter.rb +22 -0
- data/lib/lagomorph/queue_builder.rb +15 -0
- data/lib/lagomorph/rpc_call.rb +84 -0
- data/lib/lagomorph/session.rb +55 -0
- data/lib/lagomorph/subscriber.rb +42 -0
- data/lib/lagomorph/supervisor.rb +22 -0
- data/lib/lagomorph/version.rb +3 -0
- data/spec/lagomorph_spec.rb +89 -0
- data/spec/rabbitmq.yml.example +3 -0
- data/spec/spec_helper.rb +2 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6cff4cf7c784a64394cd23dd70849e382667df5f
|
4
|
+
data.tar.gz: 21739a5e5f83a0be0ea5f4179f6f2dad7e4682d0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3057276adb863de048a1b80a54f059b2359a326fbfe9d01f64b05c14caeb2349968c2fbe8ad2e186fc0104be3c69e70e9647620c2858766929bcc1c77b9c44ee
|
7
|
+
data.tar.gz: 79fc5bf8130caa38e2343e0a9f235bb7d1e5ddea4a3a2d20e28b36684ac39e02b260f6b0758cdfabd839756fbf62ed98d4dcb549c923ec28e4438b05ed3fdb6e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.16
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Alessandro Berardi
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Lagomorph
|
2
|
+
|
3
|
+
RPC Messaging pattern using RabbitMQ
|
4
|
+
|
5
|
+
Lagomorph is a mammal of the order Lagomorpha, which comprises the hares, rabbits, and pikas.
|
6
|
+
|
7
|
+
It's also a gem that implements the RPC pattern over AMPQ using RabbitMQ.
|
8
|
+
In this case, it can work with either MRI (through the bunny gem) or jRuby
|
9
|
+
(via the march_hare gem).
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this to your application's Gemfile if you're on jruby:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'march_hare'
|
17
|
+
gem 'lagomorph'
|
18
|
+
```
|
19
|
+
|
20
|
+
...and if you're on MRI, or rubinius:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'bunny'
|
24
|
+
gem 'lagomorph'
|
25
|
+
```
|
26
|
+
|
27
|
+
And then execute:
|
28
|
+
|
29
|
+
$ bundle
|
30
|
+
|
31
|
+
Or install it yourself as:
|
32
|
+
|
33
|
+
$ gem install lagomorph
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
Lagomorph tries to maintain a healthy distance from your code. It wants
|
38
|
+
you to be in control.
|
39
|
+
|
40
|
+
Let's say you have a complicated bit of work, that looks like this:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class PongWorker
|
44
|
+
def ponger
|
45
|
+
'pong'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
Then, you could set it going in a super micro-service type of way with
|
51
|
+
this bootup:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
connection_params = {} # passed along to RabbitMQ connect
|
55
|
+
session = Lagomorph::Session.connect(connection_params)
|
56
|
+
supervisor = Lagomorph::Supervisor.new(session)
|
57
|
+
supervisor.route 'ping', PongWorker
|
58
|
+
|
59
|
+
trap("SIGINT") { puts "Bye!"; exit! }
|
60
|
+
|
61
|
+
# Let the supervisor run in the background,
|
62
|
+
# while the main thread does nothing
|
63
|
+
sleep
|
64
|
+
```
|
65
|
+
|
66
|
+
Now to utilise this amazing service, we would use the rpc call client:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
connection_params = {} # passed along to RabbitMQ connect
|
70
|
+
session = Lagomorph::Session.connect(connection_params)
|
71
|
+
|
72
|
+
rpc_call = Lagomorph::RpcCall.new(session)
|
73
|
+
result = rpc_call.dispatch(queue, 'ponger')
|
74
|
+
|
75
|
+
puts "The result should be 'pong'. Is it: #{result}?"
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
1. Fork it ( https://github.com/[my-github-username]/lagomorph/fork )
|
80
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
81
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
82
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
83
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lagomorph.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'lagomorph/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "lagomorph"
|
8
|
+
spec.version = Lagomorph::VERSION
|
9
|
+
spec.authors = ["Alessandro Berardi", "Adam Davies"]
|
10
|
+
spec.email = ["berardialessandro@gmail.com", "adzdavies@gmail.com"]
|
11
|
+
spec.summary = %q{RPC Messaging pattern using RabbitMQ}
|
12
|
+
spec.description = %q{
|
13
|
+
Lagomorph is a mammal of the order Lagomorpha, which comprises the hares, rabbits, and pikas.
|
14
|
+
|
15
|
+
It's also a gem that implements the RPC pattern over AMPQ using RabbitMQ.
|
16
|
+
In this case, it can work with either MRI (through the bunny gem) or jRuby
|
17
|
+
(via the march_hare gem).
|
18
|
+
}
|
19
|
+
spec.homepage = ""
|
20
|
+
spec.license = "MIT"
|
21
|
+
|
22
|
+
spec.files = `git ls-files -z`.split("\x0")
|
23
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
|
27
|
+
# You'll have to include one of these when you use it
|
28
|
+
# since gem-build will include one or both...
|
29
|
+
# if RUBY_PLATFORM == 'java' # jruby
|
30
|
+
# spec.add_dependency 'march_hare'
|
31
|
+
# else # mri
|
32
|
+
# spec.add_dependency 'bunny'
|
33
|
+
# end
|
34
|
+
spec.add_dependency 'json'
|
35
|
+
|
36
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
37
|
+
|
38
|
+
# Switch your platform when spec'ing...
|
39
|
+
if RUBY_PLATFORM == 'java' # jruby
|
40
|
+
spec.add_development_dependency 'march_hare'
|
41
|
+
else # mri
|
42
|
+
spec.add_development_dependency 'bunny'
|
43
|
+
end
|
44
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
45
|
+
spec.add_development_dependency "rspec"
|
46
|
+
|
47
|
+
end
|
data/lib/lagomorph.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'lagomorph/version'
|
2
|
+
|
3
|
+
module Lagomorph
|
4
|
+
|
5
|
+
def self.using_march_hare?
|
6
|
+
using? 'march_hare'
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def self.using_bunny?
|
11
|
+
using? 'bunny'
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.using?(gem_name)
|
18
|
+
!Gem.loaded_specs[gem_name].nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
if Lagomorph.using_bunny?
|
24
|
+
require 'bunny'
|
25
|
+
else
|
26
|
+
require 'march_hare'
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'lagomorph/session'
|
30
|
+
require 'lagomorph/subscriber'
|
31
|
+
require 'lagomorph/supervisor'
|
32
|
+
require 'lagomorph/rpc_call'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Lagomorph
|
4
|
+
class JsonParser
|
5
|
+
|
6
|
+
def parse_request(payload)
|
7
|
+
request_message = JSON.parse(payload)
|
8
|
+
method = request_message.fetch('method')
|
9
|
+
params = request_message.fetch('params', [])
|
10
|
+
return method, params
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_response(response)
|
14
|
+
JSON.parse(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_request(method, *params)
|
18
|
+
JSON.generate('method' => method, 'params' => params)
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_response(result)
|
22
|
+
JSON.generate('result' => result)
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_error(error)
|
26
|
+
JSON.generate('error' => error)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Lagomorph
|
2
|
+
MetadataAdapter = Struct.new(:delivery_info, :properties) do
|
3
|
+
|
4
|
+
def delivery_tag
|
5
|
+
delivery_info.delivery_tag
|
6
|
+
end
|
7
|
+
|
8
|
+
def reply_to
|
9
|
+
properties.reply_to
|
10
|
+
end
|
11
|
+
|
12
|
+
def correlation_id
|
13
|
+
properties.correlation_id
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'lagomorph/metadata_adapter'
|
2
|
+
|
3
|
+
module Lagomorph
|
4
|
+
QueueAdapter = Struct.new(:queue) do
|
5
|
+
|
6
|
+
def subscribe(options = {}, &block)
|
7
|
+
if Lagomorph.using_bunny?
|
8
|
+
queue.subscribe(options) do |delivery_info, properties, payload|
|
9
|
+
metadata = MetadataAdapter.new(delivery_info, properties)
|
10
|
+
block.call(metadata, payload)
|
11
|
+
end
|
12
|
+
else
|
13
|
+
queue.subscribe(options, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
queue.name
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'lagomorph/queue_adapter'
|
2
|
+
require 'lagomorph/metadata_adapter'
|
3
|
+
|
4
|
+
module Lagomorph
|
5
|
+
QueueBuilder = Struct.new(:channel) do
|
6
|
+
def queue(name, opts={})
|
7
|
+
QueueAdapter.new(channel.queue(name, opts))
|
8
|
+
end
|
9
|
+
|
10
|
+
# Build a reply queue
|
11
|
+
def reply_queue
|
12
|
+
queue('', exclusive: true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'lagomorph/queue_adapter'
|
2
|
+
require 'lagomorph/json_parser'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Lagomorph
|
6
|
+
class RpcCall
|
7
|
+
|
8
|
+
def initialize(session)
|
9
|
+
@session = session
|
10
|
+
@results = {}
|
11
|
+
@mutex = Monitor.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def dispatch(queue_name, method, *params)
|
15
|
+
@queue_name = queue_name
|
16
|
+
|
17
|
+
correlation_id = calculate_correlation_id
|
18
|
+
@mutex.synchronize do
|
19
|
+
@results[correlation_id] = ::Queue.new
|
20
|
+
end
|
21
|
+
|
22
|
+
prepare_channel
|
23
|
+
payload = prepare_payload(method, *params)
|
24
|
+
publish_rpc_call(payload, correlation_id)
|
25
|
+
response = block_till_receive_response(correlation_id)
|
26
|
+
|
27
|
+
response['result'] || (fail response.fetch('error'))
|
28
|
+
end
|
29
|
+
|
30
|
+
def close_channel
|
31
|
+
@channel.close
|
32
|
+
@prepared_channel = false
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def prepare_channel
|
38
|
+
return if @prepared_channel
|
39
|
+
|
40
|
+
@channel = @session.create_channel(1)
|
41
|
+
|
42
|
+
@exchange = @channel.default_exchange
|
43
|
+
@reply_queue = QueueBuilder.new(@channel).reply_queue
|
44
|
+
|
45
|
+
listen_for_responses
|
46
|
+
|
47
|
+
@prepared_channel = true
|
48
|
+
end
|
49
|
+
|
50
|
+
def publish_rpc_call(request, correlation_id)
|
51
|
+
@exchange.publish(request, routing_key: @queue_name,
|
52
|
+
correlation_id: correlation_id,
|
53
|
+
reply_to: @reply_queue.name
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def prepare_payload(method, *params)
|
58
|
+
JsonParser.new.build_request(method, *params)
|
59
|
+
end
|
60
|
+
|
61
|
+
def calculate_correlation_id
|
62
|
+
SecureRandom.uuid
|
63
|
+
end
|
64
|
+
|
65
|
+
def listen_for_responses
|
66
|
+
@reply_queue.subscribe(block: false) do |metadata, payload|
|
67
|
+
@results[metadata.correlation_id].push(payload)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def block_till_receive_response(correlation_id)
|
72
|
+
raw_response = @results[correlation_id].pop # blocks until can pop
|
73
|
+
response = parse_response(raw_response)
|
74
|
+
@results.delete(correlation_id)
|
75
|
+
|
76
|
+
response
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_response(response)
|
80
|
+
JsonParser.new.parse_response(response)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Lagomorph
|
2
|
+
class Session
|
3
|
+
|
4
|
+
CONNECTION_PARAM_KEYS = [
|
5
|
+
:host,
|
6
|
+
:heartbeat_interval,
|
7
|
+
:username,
|
8
|
+
:password,
|
9
|
+
:port
|
10
|
+
]
|
11
|
+
|
12
|
+
|
13
|
+
def self.connect(connection_params)
|
14
|
+
new(connection_params).tap(&:open_connection)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def initialize(connection_params)
|
19
|
+
@connection_params = connection_params.select { |key,_|
|
20
|
+
CONNECTION_PARAM_KEYS.include?(key)
|
21
|
+
}
|
22
|
+
@mutex = Monitor.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def open_connection
|
26
|
+
@mutex.synchronize do
|
27
|
+
@connection ||= if Lagomorph.using_bunny?
|
28
|
+
::Bunny.new(@connection_params).tap(&:start)
|
29
|
+
else
|
30
|
+
::MarchHare.connect(@connection_params)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def close_connection
|
36
|
+
return if @connection.nil? || @connection.closed?
|
37
|
+
@mutex.synchronize do
|
38
|
+
@connection.close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_channel(prefetch = nil)
|
43
|
+
@mutex.synchronize do
|
44
|
+
channel = @connection.create_channel
|
45
|
+
if Lagomorph.using_bunny?
|
46
|
+
channel.prefetch(prefetch)
|
47
|
+
else
|
48
|
+
channel.prefetch = prefetch
|
49
|
+
end
|
50
|
+
channel
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'lagomorph/json_parser'
|
2
|
+
|
3
|
+
module Lagomorph
|
4
|
+
class Subscriber
|
5
|
+
|
6
|
+
def initialize(worker_class)
|
7
|
+
@worker_class = worker_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def subscribe(queue, channel)
|
11
|
+
queue.subscribe(manual_ack: true, block: false) do |metadata, payload|
|
12
|
+
result = process_request(payload)
|
13
|
+
response = build_response(result)
|
14
|
+
channel.ack(metadata.delivery_tag)
|
15
|
+
publish_response(channel, metadata, response)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def process_request(request)
|
23
|
+
method, params = parse_request(request)
|
24
|
+
@worker_class.new.send(method, *params)
|
25
|
+
end
|
26
|
+
|
27
|
+
def publish_response(channel, metadata, payload)
|
28
|
+
channel.default_exchange.publish(payload,
|
29
|
+
routing_key: metadata.reply_to,
|
30
|
+
correlation_id: metadata.correlation_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_request(payload)
|
34
|
+
JsonParser.new.parse_request(payload)
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_response(result)
|
38
|
+
JsonParser.new.build_response(result)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'lagomorph/queue_builder'
|
2
|
+
require 'lagomorph/json_parser'
|
3
|
+
|
4
|
+
module Lagomorph
|
5
|
+
class Supervisor
|
6
|
+
|
7
|
+
def initialize(session)
|
8
|
+
@session = session
|
9
|
+
end
|
10
|
+
|
11
|
+
def route(queue_name, worker_class)
|
12
|
+
prefetch = 10
|
13
|
+
durable = false
|
14
|
+
|
15
|
+
channel = @session.create_channel(prefetch)
|
16
|
+
queue = QueueBuilder.new(channel).queue(queue_name, durable: durable)
|
17
|
+
|
18
|
+
Subscriber.new(worker_class).subscribe(queue, channel)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yaml'
|
3
|
+
require 'lagomorph/session'
|
4
|
+
require 'lagomorph/supervisor'
|
5
|
+
require 'lagomorph/subscriber'
|
6
|
+
require 'lagomorph/rpc_call'
|
7
|
+
|
8
|
+
describe 'a Lagomorph RPC process' do
|
9
|
+
|
10
|
+
let(:rabbitmq_config) {
|
11
|
+
YAML.load_file(
|
12
|
+
File.join(File.expand_path(File.dirname(__FILE__)),'rabbitmq.yml')
|
13
|
+
)
|
14
|
+
}
|
15
|
+
|
16
|
+
class PongWorker
|
17
|
+
def pong
|
18
|
+
'pong'
|
19
|
+
end
|
20
|
+
|
21
|
+
def echo(request)
|
22
|
+
request
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when supervising a PongWorker on the ping queue' do
|
27
|
+
let(:connection_params) {
|
28
|
+
{
|
29
|
+
host: rabbitmq_config.fetch('host'),
|
30
|
+
username: rabbitmq_config.fetch('username'),
|
31
|
+
password: rabbitmq_config.fetch('password')
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
let(:session) { Lagomorph::Session.connect(connection_params) }
|
36
|
+
|
37
|
+
let(:supervisor) { Lagomorph::Supervisor.new(session) }
|
38
|
+
|
39
|
+
let(:queue) { 'ping' }
|
40
|
+
|
41
|
+
before do
|
42
|
+
supervisor.route queue, PongWorker
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'and we have an rpc call manager thingy' do
|
46
|
+
let!(:rpc_call) { Lagomorph::RpcCall.new(session) }
|
47
|
+
|
48
|
+
context 'when a pong rpc call is made on the ping queue' do
|
49
|
+
let(:result) { rpc_call.dispatch(queue, 'pong') }
|
50
|
+
it { expect(result).to eq 'pong' }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when an echo rpc call is made on the ping queue' do
|
54
|
+
let(:result) { rpc_call.dispatch(queue, 'echo', 'test') }
|
55
|
+
it { expect(result).to eq 'test' }
|
56
|
+
end
|
57
|
+
after { rpc_call.close_channel }
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when using 8 threads, with one instance per thread' do
|
61
|
+
let(:number_of_threads) { 8 }
|
62
|
+
let(:total_calls) { 800 }
|
63
|
+
let(:calls_per_thread) { (total_calls / number_of_threads).ceil }
|
64
|
+
|
65
|
+
let(:rpc_calls) {
|
66
|
+
1.upto(number_of_threads).map { Lagomorph::RpcCall.new(session) }
|
67
|
+
}
|
68
|
+
|
69
|
+
specify 'each thread will get the expected response' do
|
70
|
+
rpc_calls.map do |rpc_call|
|
71
|
+
Thread.new do
|
72
|
+
thread_results = calls_per_thread.times.map { |call_index|
|
73
|
+
rpc_call.dispatch(queue, 'echo', call_index).tap { |result|
|
74
|
+
expect(result).to eq call_index
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end.each(&:join)
|
79
|
+
|
80
|
+
rpc_calls.each(&:close_channel)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
after do
|
85
|
+
session.close_connection
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lagomorph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alessandro Berardi
|
8
|
+
- Adam Davies
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-11-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
name: json
|
21
|
+
prerelease: false
|
22
|
+
type: :runtime
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
name: bundler
|
35
|
+
prerelease: false
|
36
|
+
type: :development
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.7'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
name: march_hare
|
49
|
+
prerelease: false
|
50
|
+
type: :development
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
name: rake
|
63
|
+
prerelease: false
|
64
|
+
type: :development
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '10.0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
name: rspec
|
77
|
+
prerelease: false
|
78
|
+
type: :development
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
description: "\n Lagomorph is a mammal of the order Lagomorpha, which comprises\
|
85
|
+
\ the hares, rabbits, and pikas.\n\n It's also a gem that implements the RPC\
|
86
|
+
\ pattern over AMPQ using RabbitMQ.\n In this case, it can work with either MRI\
|
87
|
+
\ (through the bunny gem) or jRuby \n (via the march_hare gem).\n "
|
88
|
+
email:
|
89
|
+
- berardialessandro@gmail.com
|
90
|
+
- adzdavies@gmail.com
|
91
|
+
executables: []
|
92
|
+
extensions: []
|
93
|
+
extra_rdoc_files: []
|
94
|
+
files:
|
95
|
+
- .gitignore
|
96
|
+
- .rspec
|
97
|
+
- .ruby-version
|
98
|
+
- .travis.yml
|
99
|
+
- Gemfile
|
100
|
+
- LICENSE.txt
|
101
|
+
- README.md
|
102
|
+
- Rakefile
|
103
|
+
- lagomorph.gemspec
|
104
|
+
- lib/lagomorph.rb
|
105
|
+
- lib/lagomorph/json_parser.rb
|
106
|
+
- lib/lagomorph/metadata_adapter.rb
|
107
|
+
- lib/lagomorph/queue_adapter.rb
|
108
|
+
- lib/lagomorph/queue_builder.rb
|
109
|
+
- lib/lagomorph/rpc_call.rb
|
110
|
+
- lib/lagomorph/session.rb
|
111
|
+
- lib/lagomorph/subscriber.rb
|
112
|
+
- lib/lagomorph/supervisor.rb
|
113
|
+
- lib/lagomorph/version.rb
|
114
|
+
- spec/lagomorph_spec.rb
|
115
|
+
- spec/rabbitmq.yml.example
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
homepage: ''
|
118
|
+
licenses:
|
119
|
+
- MIT
|
120
|
+
metadata: {}
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - '>='
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 2.1.9
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: RPC Messaging pattern using RabbitMQ
|
141
|
+
test_files:
|
142
|
+
- spec/lagomorph_spec.rb
|
143
|
+
- spec/rabbitmq.yml.example
|
144
|
+
- spec/spec_helper.rb
|