babs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in babs.gemspec
4
+ gemspec
@@ -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.
@@ -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
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -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
@@ -0,0 +1,5 @@
1
+ require "babs/version"
2
+
3
+ module Babs
4
+ # Your code goes here...
5
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Babs
2
+ VERSION = "0.0.1"
3
+ end
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: []