easy_bunny_rpc 0.1.0.alpha

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2c7753440ae7622d08014667722d7d607b5e2ff7
4
+ data.tar.gz: d6f58efefd2f73e5f2cc0dea692acae2dc14852d
5
+ SHA512:
6
+ metadata.gz: 605aba5b4e0aa19e103e7e83c7c5cff7483d66fe104fabdf420f1bdab4489d464391e5f057c60376ee1648fbeefecd24a0df92efd1e43b2fed415a259027fa4c
7
+ data.tar.gz: 2c53c15a9dd254a7a6a6032faca6e3b13ee23f00a72a24e00adf6620308e02ddc2e3aa3b36e8304a5a565c3b6816fcfb683165d4982818774b7d1ad44ca524ce
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ Gemfile.lock
2
+ pkg/
3
+ vendor/cache/*.gem
4
+ .idea
5
+ private
data/ChangeLog.rdoc ADDED
@@ -0,0 +1,4 @@
1
+ === 0.1.0 / 2015-06-19
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Tom van Leeuwen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,103 @@
1
+ easy\_bunny\_rpc
2
+ =================
3
+ [![Gem Version](https://badge.fury.io/rb/easy_bunny_rpc.svg)](https://rubygems.org/gems/easy_bunny_rpc)
4
+
5
+ Generic RPC client/worker library handling data serialization built on top of bunny.
6
+
7
+ Example usage
8
+ -----
9
+
10
+ Create an EchoWorker class which simply echoes whatever is received:
11
+
12
+ ``` ruby
13
+ class EchoWorker < EasyBunnyRPC::Worker
14
+ def mediate
15
+ subscribe do |payload|
16
+ publish_success(payload) # Send a success message to the client
17
+ # publish_failure(payload) # Send a failure message to the client
18
+ end
19
+ rescue Interrupt => _
20
+ # close
21
+ end
22
+ end
23
+ ```
24
+
25
+ Now you can initialize it. Please note the :bunny key Hash is directly fed to Bunny.new:
26
+
27
+ ``` ruby
28
+ options = {
29
+ queue: 'echo',
30
+ bunny: {
31
+ user: 'user',
32
+ password: 'secret',
33
+ host: 'localhost'
34
+ }
35
+ }
36
+
37
+ echo_worker = EchoWorker.new options
38
+ echo_worker.mediate
39
+ ```
40
+
41
+
42
+ Create an EchoClient class:
43
+
44
+ ``` ruby
45
+ class EchoClient < EasyBunnyRPC::Client
46
+ def perform
47
+ set_timeout(2) # timeout in seconds, default is 5
48
+
49
+ # The first argument is the payload, the send argument is the correlation_id
50
+ # The correlation_id is always send back as-is by the worker
51
+ # This way you can correlate the replies with your requests
52
+ publish('hi there', 'call 1')
53
+ publish('hi there again', 'call 2')
54
+
55
+ # Pop will raise a ::Timeout::Error when a timeout occurs and no message is received
56
+ p pop
57
+ p pop
58
+ end
59
+ end
60
+ ```
61
+
62
+ Now you can initialize it:
63
+
64
+ ``` ruby
65
+ options = {
66
+ queue: 'echo',
67
+ bunny: {
68
+ user: 'user',
69
+ password: 'secret',
70
+ host: 'localhost'
71
+ }
72
+ }
73
+
74
+ echo_client = EchoClient.new(options)
75
+ echo_client.perform
76
+ ```
77
+
78
+ Output:
79
+ ``` text
80
+ {"success"=>true, "payload"=>"hi there", "correlation_id"=>"call 1"}
81
+ {"success"=>true, "payload"=>"hi there again", "correlation_id"=>"call 2"}
82
+ ```
83
+
84
+
85
+ Notes
86
+ _____
87
+
88
+ - Tested with RabbitMQ
89
+ - Uses the expiration feature of RabbitMQ to expire messages sent by the client
90
+
91
+
92
+ Install
93
+ -------
94
+
95
+ ```
96
+ $ gem install easy_bunny_rpc
97
+ ```
98
+
99
+
100
+ Author
101
+ ------
102
+
103
+ Tom van Leeuwen, [@tvl2386](https://twitter.com/tvl2386)
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler'
7
+ rescue LoadError => e
8
+ warn e.message
9
+ warn "Run `gem install bundler` to install Bundler."
10
+ exit -1
11
+ end
12
+
13
+ begin
14
+ Bundler.setup(:development)
15
+ rescue Bundler::BundlerError => e
16
+ warn e.message
17
+ warn "Run `bundle install` to install missing gems."
18
+ exit e.status_code
19
+ end
20
+
21
+ require 'rake'
22
+
23
+ require 'rubygems/tasks'
24
+ Gem::Tasks.new
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/easy_bunny_rpc/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'easy_bunny_rpc'
7
+ gem.version = EasyBunnyRPC::VERSION
8
+ gem.summary = %q{A simple rabbitmq rpc server/client library built on bunny with message serialization using json}
9
+ gem.description = gem.summary
10
+ gem.license = 'MIT'
11
+ gem.authors = ['Tom van Leeuwen']
12
+ gem.email = 'tom@vleeuwen.eu'
13
+ gem.homepage = 'https://rubygems.org/gems/easy_bunny_rpc'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_dependency 'bunny', '~> 1.7'
21
+
22
+ gem.add_development_dependency 'bundler', '~> 1.10'
23
+ gem.add_development_dependency 'rake', '~> 10.4'
24
+ gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
25
+ end
@@ -0,0 +1,5 @@
1
+
2
+ require 'easy_bunny_rpc/version'
3
+ require 'easy_bunny_rpc/timed_queue'
4
+ require 'easy_bunny_rpc/client'
5
+ require 'easy_bunny_rpc/worker'
@@ -0,0 +1,72 @@
1
+ require 'bunny'
2
+ require 'json'
3
+ require 'securerandom'
4
+
5
+ module EasyBunnyRPC
6
+ class Client
7
+ def initialize options={}
8
+ @options = options
9
+ @subscribed = false
10
+
11
+ # The timed queue which will collect incoming messages
12
+ @timed_queue = EasyBunnyRPC::TimedQueue.new
13
+
14
+ # Need to set a default timeout. How about 5 seconds?
15
+ set_timeout(5)
16
+ end
17
+
18
+ private
19
+
20
+ def pop
21
+ correlation_id, payload = @timed_queue.pop_with_timeout(@timeout)
22
+
23
+ JSON.parse(payload).merge!({ 'correlation_id' => correlation_id })
24
+ end
25
+
26
+ def set_timeout(value)
27
+ @timeout = value
28
+ end
29
+
30
+ def start_subscription
31
+ queue.subscribe(block: false) do |delivery_info, properties, payload|
32
+ @timed_queue.push([properties.correlation_id, payload])
33
+ end
34
+
35
+ @subscribed = true
36
+ end
37
+
38
+ def publish(payload, correlation_id=default_correlation_id)
39
+ start_subscription unless @subscribed
40
+ exchange.publish([payload].to_json, routing_key: @options[:queue], correlation_id: correlation_id, reply_to: queue.name, expiration: (@timeout*1000).to_i)
41
+ end
42
+
43
+ def connection
44
+ return @connection if defined?(@connection)
45
+
46
+ @connection = Bunny.new(@options[:bunny])
47
+ @connection.start
48
+ @connection
49
+ end
50
+
51
+ def channel
52
+ @channel ||= connection.create_channel
53
+ end
54
+
55
+ # The exclusive no-name queue that is created for communicating back to me
56
+ def queue
57
+ @queue ||= channel.queue(generate_queue_name, exclusive: true)
58
+ end
59
+
60
+ def generate_queue_name
61
+ [@options[:queue], '-client_', SecureRandom.hex].join
62
+ end
63
+
64
+ def exchange
65
+ @exchange ||= channel.default_exchange
66
+ end
67
+
68
+ def default_correlation_id
69
+ ''
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,37 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+
4
+ module EasyBunnyRPC
5
+ class TimedQueue
6
+ def initialize
7
+ @array = Array.new
8
+ @mutex = Mutex.new
9
+ @cv = ConditionVariable.new
10
+ end
11
+
12
+ def push(item)
13
+ @mutex.synchronize do
14
+ @array.push(item)
15
+ @cv.signal
16
+ end
17
+ end
18
+
19
+ def pop_with_timeout(timeout=0.5)
20
+ timeout_at = Time.now + timeout
21
+
22
+ @mutex.synchronize do
23
+ loop do
24
+ if @array.empty?
25
+ remaining = timeout_at - Time.now
26
+
27
+ raise(Timeout::Error, "Waited #{timeout} seconds to pop") if(remaining <= 0)
28
+
29
+ @cv.wait(@mutex, remaining)
30
+ else
31
+ return @array.pop
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,4 @@
1
+ module EasyBunnyRPC
2
+ # easy_bunny_rpc version
3
+ VERSION = '0.1.0.alpha'
4
+ end
@@ -0,0 +1,58 @@
1
+ require 'bunny'
2
+ require 'json'
3
+
4
+ module EasyBunnyRPC
5
+ class Worker
6
+ def initialize(options={})
7
+ @options = options
8
+ end
9
+
10
+ private
11
+
12
+ def subscribe
13
+ queue.subscribe(block: true) do |delivery_info, properties, payload|
14
+ @delivery_info, @properties, @payload = delivery_info, properties, payload
15
+
16
+ yield JSON.parse(payload).first
17
+ end
18
+ end
19
+
20
+ def publish(success, payload)
21
+ obj = { 'success' => success, 'payload' => payload }.to_json
22
+
23
+ exchange.publish(obj, routing_key: @properties.reply_to, correlation_id: @properties.correlation_id)
24
+ end
25
+
26
+ def publish_success(payload)
27
+ publish true, payload
28
+ end
29
+
30
+ def publish_failure(payload)
31
+ publish false, payload
32
+ end
33
+
34
+ def connection
35
+ return @connection if defined?(@connection)
36
+
37
+ @connection = Bunny.new(@options[:bunny])
38
+ @connection.start
39
+ @connection
40
+ end
41
+
42
+ def channel
43
+ return @channel if defined?(@channel)
44
+
45
+ @channel = connection.create_channel
46
+ @channel.prefetch(1)
47
+ @channel
48
+ end
49
+
50
+ def queue
51
+ @queue ||= channel.queue(@options[:queue])
52
+ end
53
+
54
+ def exchange
55
+ @exchange ||= channel.default_exchange
56
+ end
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: easy_bunny_rpc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.alpha
5
+ platform: ruby
6
+ authors:
7
+ - Tom van Leeuwen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bunny
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubygems-tasks
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.2'
69
+ description: A simple rabbitmq rpc server/client library built on bunny with message
70
+ serialization using json
71
+ email: tom@vleeuwen.eu
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ChangeLog.rdoc
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - easy_bunny_rpc.gemspec
83
+ - lib/easy_bunny_rpc.rb
84
+ - lib/easy_bunny_rpc/client.rb
85
+ - lib/easy_bunny_rpc/timed_queue.rb
86
+ - lib/easy_bunny_rpc/version.rb
87
+ - lib/easy_bunny_rpc/worker.rb
88
+ homepage: https://rubygems.org/gems/easy_bunny_rpc
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">"
104
+ - !ruby/object:Gem::Version
105
+ version: 1.3.1
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.2.0
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: A simple rabbitmq rpc server/client library built on bunny with message serialization
112
+ using json
113
+ test_files: []