babs 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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +110 -0
- data/Rakefile +1 -0
- data/babs.gemspec +25 -0
- data/lib/babs.rb +5 -0
- data/lib/babs/client.rb +81 -0
- data/lib/babs/version.rb +3 -0
- metadata +119 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jay OConnor
|
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,110 @@
|
|
1
|
+
# Babs
|
2
|
+
|
3
|
+
Do you like RabbitMQ, but want to use it for more than asynchronous messaging? This will help. Babs is a Rabbit client using the Bunny gem that goes through the motions of setting up a listener, sending a message and waiting for a response on the listener queue.
|
4
|
+
|
5
|
+
Most of this code came from the Bunny tutorials. The client wrapper and opinions were added on for this gem.
|
6
|
+
|
7
|
+
This is demonstration quality code (debug output is left in). If you like it, let me know and we'll knock it out and set up better automated tests.
|
8
|
+
|
9
|
+
Why would someone want to do this?? Isn't this better served with DCell? Yes, but no. RabbitMQ will act as your broker, elminating the need for a directory server that remembers where all of your nodes are.
|
10
|
+
|
11
|
+
tl;dr - See the usage section
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'babs'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install babs
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'babs/client'
|
31
|
+
|
32
|
+
client = Babs::Client.new(server_queue: 'rpc_queue')
|
33
|
+
|
34
|
+
(1..1000).each do |c|
|
35
|
+
response = client.request(method: :fib, params: { number: 20 })
|
36
|
+
# response is a Hashie::Mash of the response (the resonse is expected to be a json string)
|
37
|
+
puts "i got this #{response.data.value}"
|
38
|
+
end
|
39
|
+
|
40
|
+
client.close_connection
|
41
|
+
```
|
42
|
+
|
43
|
+
What's a consumer (where you send the request) look like? -- it return a json string. This is important.
|
44
|
+
```ruby
|
45
|
+
#!/usr/bin/env ruby
|
46
|
+
# encoding: utf-8
|
47
|
+
|
48
|
+
require "bunny"
|
49
|
+
require 'hashie'
|
50
|
+
require 'json'
|
51
|
+
|
52
|
+
conn = Bunny.new(:automatically_recover => false)
|
53
|
+
conn.start
|
54
|
+
|
55
|
+
ch = conn.create_channel
|
56
|
+
|
57
|
+
class FibonacciServer
|
58
|
+
|
59
|
+
def initialize(ch)
|
60
|
+
@ch = ch
|
61
|
+
end
|
62
|
+
|
63
|
+
def start(queue_name)
|
64
|
+
@q = @ch.queue(queue_name, durable: true)
|
65
|
+
@x = @ch.default_exchange
|
66
|
+
|
67
|
+
@q.subscribe(:block => true, ack: true) do |delivery_info, properties, payload|
|
68
|
+
req = Hashie::Mash.new(JSON.parse(payload))
|
69
|
+
if req[:method] == 'fib'
|
70
|
+
n = req.params.number.to_i
|
71
|
+
r = self.class.fib(n)
|
72
|
+
puts " [.] fib(#{n})"
|
73
|
+
data_string = {data: { value: r } }.to_json
|
74
|
+
@x.publish(data_string, :routing_key => properties.reply_to, :correlation_id => properties.correlation_id)
|
75
|
+
@ch.acknowledge(delivery_info.delivery_tag, false)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def self.fib(n)
|
82
|
+
case n
|
83
|
+
when 0 then 0
|
84
|
+
when 1 then 1
|
85
|
+
else
|
86
|
+
fib(n - 1) + fib(n - 2)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
begin
|
92
|
+
server = FibonacciServer.new(ch)
|
93
|
+
" [x] Awaiting RPC requests"
|
94
|
+
server.start("rpc_queue")
|
95
|
+
rescue Interrupt => _
|
96
|
+
ch.close
|
97
|
+
conn.close
|
98
|
+
|
99
|
+
exit(0)
|
100
|
+
end
|
101
|
+
|
102
|
+
```
|
103
|
+
|
104
|
+
## Contributing
|
105
|
+
|
106
|
+
1. Fork it ( http://github.com/<my-github-username>/babs/fork )
|
107
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
108
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
109
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
110
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/babs.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'babs/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "babs"
|
8
|
+
spec.version = Babs::VERSION
|
9
|
+
spec.authors = ["Jay OConnor"]
|
10
|
+
spec.email = ["jaydoconnor@gmail.com"]
|
11
|
+
spec.summary = %q{ Client for a request response (synchronous) pattern using RabbitMQ. }
|
12
|
+
spec.description = %q{ }
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_dependency "bunny"
|
24
|
+
spec.add_dependency "hashie"
|
25
|
+
end
|
data/lib/babs.rb
ADDED
data/lib/babs/client.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module Babs
|
2
|
+
class Client
|
3
|
+
require "bunny"
|
4
|
+
require "thread"
|
5
|
+
require "hashie"
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
attr_accessor :route
|
9
|
+
attr_accessor :response, :call_id
|
10
|
+
attr_reader :lock, :condition
|
11
|
+
attr_reader :reply_queue
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
# make this connection part a singleton and a LOT of time is saved, as well as reusing the same connection
|
15
|
+
@conn = Bunny.new(:automatically_recover => false)
|
16
|
+
@conn.start
|
17
|
+
@ch = @conn.create_channel
|
18
|
+
|
19
|
+
@defaults = Hashie::Mash.new({
|
20
|
+
server_queue: nil,
|
21
|
+
exchange: @ch.default_exchange
|
22
|
+
}.merge(options)
|
23
|
+
)
|
24
|
+
|
25
|
+
@lock = Mutex.new
|
26
|
+
@condition = ConditionVariable.new
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def close_connection
|
31
|
+
@ch.close
|
32
|
+
@conn.close
|
33
|
+
end
|
34
|
+
|
35
|
+
def listen_for_response
|
36
|
+
# listen on a new queue for this response
|
37
|
+
@reply_queue = @ch.queue("", :exclusive => true)
|
38
|
+
@reply_queue.subscribe do |delivery_info, properties, payload|
|
39
|
+
puts "response_id #{properties[:correlation_id]}"
|
40
|
+
puts properties[:correlation_id] == self.call_id ? "correct id" : "BAD id"
|
41
|
+
if properties[:correlation_id] == self.call_id
|
42
|
+
self.response = payload
|
43
|
+
self.lock.synchronize{self.condition.signal}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def send_request(routing_options, method, params)
|
49
|
+
self.call_id = SecureRandom.uuid
|
50
|
+
|
51
|
+
data_string = {method: method, params: params}.to_json
|
52
|
+
|
53
|
+
routing_options.exchange.publish(
|
54
|
+
data_string,
|
55
|
+
routing_key: routing_options.server_queue,
|
56
|
+
correlation_id: call_id,
|
57
|
+
reply_to: @reply_queue.name)
|
58
|
+
puts "call id #{call_id}"
|
59
|
+
self.response = nil
|
60
|
+
# params to synchronize are mutex, timeout_in_seconds
|
61
|
+
lock.synchronize{condition.wait(lock, 5)}
|
62
|
+
response
|
63
|
+
end
|
64
|
+
|
65
|
+
def request(options = {})
|
66
|
+
options = Hashie::Mash.new(options)
|
67
|
+
# grab out the expected data
|
68
|
+
method = options.delete(:method)
|
69
|
+
params = options.delete(:params)
|
70
|
+
|
71
|
+
# merge the connection options with the defaults
|
72
|
+
routing_options = @defaults.merge(options)
|
73
|
+
|
74
|
+
response = listen_for_response
|
75
|
+
response = send_request(routing_options, method, params)
|
76
|
+
# parse and return response
|
77
|
+
Hashie::Mash.new(JSON.parse(response))
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/lib/babs/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: babs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jay OConnor
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-03-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.5'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.5'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bunny
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: hashie
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: ! ' '
|
79
|
+
email:
|
80
|
+
- jaydoconnor@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- Gemfile
|
87
|
+
- LICENSE.txt
|
88
|
+
- README.md
|
89
|
+
- Rakefile
|
90
|
+
- babs.gemspec
|
91
|
+
- lib/babs.rb
|
92
|
+
- lib/babs/client.rb
|
93
|
+
- lib/babs/version.rb
|
94
|
+
homepage: ''
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.24
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Client for a request response (synchronous) pattern using RabbitMQ.
|
119
|
+
test_files: []
|