rabbit_rpc 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +119 -0
- data/Rakefile +1 -0
- data/lib/rabbit_rpc.rb +17 -0
- data/lib/rabbit_rpc/client.rb +5 -0
- data/lib/rabbit_rpc/config.rb +87 -0
- data/lib/rabbit_rpc/connection.rb +80 -0
- data/lib/rabbit_rpc/logging.rb +32 -0
- data/lib/rabbit_rpc/message.rb +43 -0
- data/lib/rabbit_rpc/message_parser.rb +36 -0
- data/lib/rabbit_rpc/request_handler.rb +37 -0
- data/lib/rabbit_rpc/synchronous_connection.rb +81 -0
- data/lib/rabbit_rpc/version.rb +3 -0
- data/rabbit_rpc.gemspec +31 -0
- data/spec/config_spec.rb +49 -0
- data/spec/connection_spec.rb +71 -0
- data/spec/logging_spec.rb +14 -0
- data/spec/message_parser_spec.rb +50 -0
- data/spec/message_spec.rb +36 -0
- data/spec/request_handler_spec.rb +45 -0
- data/spec/support/rpc.yaml +5 -0
- data/spec/support/rpc_invalid_structure.yaml +2 -0
- data/spec/support/rpc_no_address.yaml +4 -0
- data/spec/synchronous_connection_spec.rb +41 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dfc96f46c3426bcc0d99bc1bb4e5915b21fbfe9b
|
4
|
+
data.tar.gz: a79352d3a4ddc718970493cc715d24e13c891ad0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9cb7b84171dd10688b417d5d28fc1bc8899785949b51f17b5961668f48268f1b7e309b2b33bf721f72ae1cadbb7b5848ef0eb0582b55bd4707804da6db4a05b1
|
7
|
+
data.tar.gz: 5a2e9cc981bbaef8ee781b205941afd721f2f682527229903118218a7de345b7e434a8a1cb59e81f1248ced3529c4c4163c411e72561f21184ca96fbed7a2ccd
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Sohaib Bhatti
|
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,119 @@
|
|
1
|
+
# RabbitRPC
|
2
|
+
|
3
|
+
RabbitRPC helps in the rapid development of ruby services and their RPC
|
4
|
+
invocation over RabbitMQ in a service-oriented architecture.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'rabbit_rpc'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install rabbit_rpc
|
19
|
+
|
20
|
+
## Getting Started
|
21
|
+
|
22
|
+
Rabbit-RPC can be broadly divided into two parts. EventMachine servers that
|
23
|
+
consume messages over RabbitMQ and an RPC invocation interface used for
|
24
|
+
producing messages.
|
25
|
+
|
26
|
+
### Implementing Services(RabbitMQ Consumers)
|
27
|
+
|
28
|
+
Services can easily be defined via Rabbit-RPC.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class AuthorizationService
|
32
|
+
class << self
|
33
|
+
|
34
|
+
def auth(email, pass)
|
35
|
+
{
|
36
|
+
ok: true,
|
37
|
+
email: email,
|
38
|
+
pass: pass
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
Once the services have been implemented and are loaded into loaded into
|
46
|
+
the global namespace, the service can then establish a connection with
|
47
|
+
RabbitMQ and start consuming messages.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
RabbitRPC::Connection.new(QUEUE_NAME, QUEUE_ADDRESS).listen!
|
51
|
+
```
|
52
|
+
|
53
|
+
Methods expected to send a response back to the RPC invocator,
|
54
|
+
will encode the response message and produce them on a callback queue.
|
55
|
+
|
56
|
+
Methods defined with the "one_way" prefix will not send a response back
|
57
|
+
to the RPC invocator.
|
58
|
+
|
59
|
+
### RPC Invocation
|
60
|
+
|
61
|
+
RPC invocations, whether they're for services to communicate with one
|
62
|
+
another or for the user facing web server(Rails or Sinatra) to
|
63
|
+
communicate with the various services can be defined on an individual
|
64
|
+
basis. The reasoning behind this is that each service does not
|
65
|
+
necessarily need access to all other services and their corresponding
|
66
|
+
methods
|
67
|
+
|
68
|
+
By default, Rabbit-RPC expects a rabbit_rpc.yml file to present in the config
|
69
|
+
folder of the ruby app.This YAML file contains a definition of the names
|
70
|
+
of the services(queue names), their corresponding RabbitMQ URLs and
|
71
|
+
which method calls should exist.
|
72
|
+
|
73
|
+
```yml
|
74
|
+
UserService:
|
75
|
+
address: amqp://localhost:5672
|
76
|
+
methods:
|
77
|
+
User: create, read, delete
|
78
|
+
Authorization: auth
|
79
|
+
|
80
|
+
EntertainmentService
|
81
|
+
address: amqp://localhost:5672
|
82
|
+
methods:
|
83
|
+
Movie: likes
|
84
|
+
Music: likes
|
85
|
+
```
|
86
|
+
|
87
|
+
Rabbit-RPC relies on some conventions for it to work.
|
88
|
+
- The names of the services and their RabbitMQ queue names are the same.
|
89
|
+
- A service might be responsible for multiple functionaly. For the YAML
|
90
|
+
example above, the UserService is responsible for both the CRUD and
|
91
|
+
authentication of users. The service object is they key, and its
|
92
|
+
methods are provided by comma serperated values.
|
93
|
+
- Unless a method is defined with a "one_way" prefix, the RPC client
|
94
|
+
will wait for a a response in a synchronous fashion.
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
RabbitRPC::Config.initialize!
|
98
|
+
RabbitRPC::Client::UserService::Authorization.auth 'username', 'password'
|
99
|
+
=> {"ok"=>true, "email"=>"username", "password"=>"password"}
|
100
|
+
|
101
|
+
RabbitRPC::Client::UserService::User.one_way_send_mail
|
102
|
+
=> nil
|
103
|
+
```
|
104
|
+
|
105
|
+
## Example
|
106
|
+
|
107
|
+
A sample implementation of a service and RPC invocation can be seen [here.](https://github.com/sohaibbhatti/rabbit_rpc_example)
|
108
|
+
|
109
|
+
## Contributing
|
110
|
+
|
111
|
+
As with all gems in their infancy. Rabbit-RPC is in a skeletal state. Some
|
112
|
+
of the code especially related to the Synchronous connection can be
|
113
|
+
optimized.
|
114
|
+
|
115
|
+
1. Fork it
|
116
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
117
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
118
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
119
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/rabbit_rpc.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "rabbit_rpc/version"
|
2
|
+
require "rabbit_rpc/logging"
|
3
|
+
require "rabbit_rpc/message"
|
4
|
+
require "rabbit_rpc/message_parser"
|
5
|
+
require "rabbit_rpc/config"
|
6
|
+
require "rabbit_rpc/client"
|
7
|
+
require "rabbit_rpc/synchronous_connection"
|
8
|
+
require "rabbit_rpc/request_handler"
|
9
|
+
require "rabbit_rpc/connection"
|
10
|
+
|
11
|
+
module RabbitRPC
|
12
|
+
|
13
|
+
def self.logger
|
14
|
+
RabbitRPC::Logging.logger
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
|
4
|
+
module RabbitRPC
|
5
|
+
class InvalidFormatError < StandardError; end
|
6
|
+
|
7
|
+
class Config
|
8
|
+
@@service_address = {}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def client_rpc_path
|
13
|
+
@@rpc_path ||= File.join 'config', 'rabbit_rpc.yml'
|
14
|
+
end
|
15
|
+
|
16
|
+
def client_rpc_path=(path)
|
17
|
+
@@rpc_path = path
|
18
|
+
end
|
19
|
+
|
20
|
+
# Traverses through the RPC YAML file and creates RPC invocations
|
21
|
+
# under the RabbitRPC::Client namespace.
|
22
|
+
#
|
23
|
+
# UserService:
|
24
|
+
# Authorization: auth
|
25
|
+
# Friend: list, delete
|
26
|
+
#
|
27
|
+
# RabbitRPC::Client::UserService::Friends.list
|
28
|
+
# TODO: Abstract this logic into a seperate class
|
29
|
+
def initialize!
|
30
|
+
YAML.load_file(client_rpc_path).each do |service_name, class_definitions|
|
31
|
+
unless class_definitions.is_a?(Hash) && class_definitions.keys.sort == %w[address methods]
|
32
|
+
raise InvalidFormatError, "Error parsing the structure of the RPC YAML"
|
33
|
+
end
|
34
|
+
|
35
|
+
mdule = Module.new
|
36
|
+
service_name = "#{service_name}".classify
|
37
|
+
|
38
|
+
::RabbitRPC::Client.const_set service_name, mdule
|
39
|
+
class_definitions.each do |param, value|
|
40
|
+
if is_address?(param)
|
41
|
+
@@service_address ||= {}
|
42
|
+
@@service_address[service_name] = value
|
43
|
+
elsif is_method_declaration?(param)
|
44
|
+
populate_rpc(service_name, value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def is_address?(key)
|
53
|
+
key == 'address'
|
54
|
+
end
|
55
|
+
|
56
|
+
def is_method_declaration?(key)
|
57
|
+
key == 'methods'
|
58
|
+
end
|
59
|
+
|
60
|
+
def populate_rpc(service_name, class_and_methods)
|
61
|
+
if @@service_address[service_name].nil?
|
62
|
+
raise InvalidFormatError, "The address declaration in the YAML file appears to be missing for #{service_name}"
|
63
|
+
end
|
64
|
+
|
65
|
+
class_and_methods.each do |klass_name, methods|
|
66
|
+
klass_name = klass_name.classify
|
67
|
+
|
68
|
+
klass = Class.new do
|
69
|
+
methods.gsub(/\s+/, "").split(',').each do |method_name|
|
70
|
+
define_singleton_method(method_name) do |*args|
|
71
|
+
|
72
|
+
RabbitRPC::SynchronousConnection.new(
|
73
|
+
service_name,
|
74
|
+
"#{service_name}.callback",
|
75
|
+
@@service_address[service_name]).publish!(RabbitRPC::Message.new("#{klass_name}Service.#{method_name}", *args))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
"RabbitRPC::Client::#{service_name}".constantize.const_set(klass_name, klass)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'amqp'
|
2
|
+
|
3
|
+
#TODO: RabbitMQ connection options
|
4
|
+
module RabbitRPC
|
5
|
+
class Connection
|
6
|
+
PREFETCH_DEFAULT = 5
|
7
|
+
|
8
|
+
include Logging
|
9
|
+
|
10
|
+
attr_reader :queue_name, :uri, :opts, :prefetch
|
11
|
+
|
12
|
+
def initialize(queue_name, uri, prefetch, opts = {})
|
13
|
+
@queue_name = queue_name
|
14
|
+
@uri = uri
|
15
|
+
@opts = opts
|
16
|
+
@prefetch = prefetch || PREFETCH_DEFAULT
|
17
|
+
end
|
18
|
+
|
19
|
+
def listen!
|
20
|
+
EventMachine.run do
|
21
|
+
close_connection_on_interrupt
|
22
|
+
subscribe_to_queue
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def subscribe_to_queue
|
29
|
+
queue.subscribe do |metadata, payload|
|
30
|
+
|
31
|
+
EM.defer do
|
32
|
+
request_handler = RequestHandler.new(payload)
|
33
|
+
response_message = request_handler.execute
|
34
|
+
|
35
|
+
|
36
|
+
unless request_handler.one_way
|
37
|
+
|
38
|
+
channel.default_exchange.publish(
|
39
|
+
response_message.to_msgpack,
|
40
|
+
routing_key: metadata.reply_to,
|
41
|
+
correlation_id: metadata.message_id,
|
42
|
+
mandatory: true
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def connect!
|
51
|
+
connection_params = ::AMQP::Client.parse_connection_uri @uri
|
52
|
+
connection_params.merge! @opts
|
53
|
+
logger.info 'Connecting to RabbitMQ'
|
54
|
+
@connection ||= ::AMQP.connect connection_params
|
55
|
+
end
|
56
|
+
|
57
|
+
def channel
|
58
|
+
logger.info 'Establishng connection with channel'
|
59
|
+
@channel ||= ::AMQP::Channel.new connect!, prefetch: @prefetch
|
60
|
+
end
|
61
|
+
|
62
|
+
# Private - Establish connection with a RabbitMQ queue.
|
63
|
+
# TODO: Queue options need to be provided
|
64
|
+
def queue
|
65
|
+
logger.info 'Connecting to queue'
|
66
|
+
@queue ||= channel.queue @queue_name
|
67
|
+
end
|
68
|
+
|
69
|
+
def close_connection_on_interrupt
|
70
|
+
%w[INT TERM].each do |interrupt_type|
|
71
|
+
Signal.trap(interrupt_type) do
|
72
|
+
logger.info 'Exiting'
|
73
|
+
@connection.close do
|
74
|
+
EventMachine.stop { exit }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module RabbitRPC
|
4
|
+
|
5
|
+
module Logging
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.send :include, Methods
|
9
|
+
base.extend Methods
|
10
|
+
end
|
11
|
+
|
12
|
+
module Methods
|
13
|
+
def logger
|
14
|
+
RabbitRPC::Logging.logger
|
15
|
+
end
|
16
|
+
|
17
|
+
# TODO: logger options
|
18
|
+
def log_exception(ex)
|
19
|
+
RabbitRPC::Logging.log_exception(ex)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.logger(target = $stdout)
|
24
|
+
@logger ||= Logger.new(target)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.log_exception(ex)
|
28
|
+
logger.error ('Message: ' + ex.message)
|
29
|
+
logger.error (['backtrace:'] + ex.backtrace).join("\n")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
|
3
|
+
module RabbitRPC
|
4
|
+
class Message
|
5
|
+
|
6
|
+
attr_reader :method_name, :args
|
7
|
+
|
8
|
+
def initialize(method_name, *args)
|
9
|
+
@method_name = method_name
|
10
|
+
@args = args
|
11
|
+
end
|
12
|
+
|
13
|
+
# Squeezes and serializes the RPC method name and arguments
|
14
|
+
#
|
15
|
+
# Returns the packed and serialized string
|
16
|
+
def pack
|
17
|
+
serialize(method: @method_name, args: @args)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Unpacks a serialized message to a hash containing the method and
|
21
|
+
# its args. This method needs to be modified if a serializer other
|
22
|
+
# than MessagePack is to be used.
|
23
|
+
#
|
24
|
+
# Returns a Hash
|
25
|
+
def self.unpack(message)
|
26
|
+
MessagePack.unpack message
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.generate_id
|
30
|
+
SecureRandom.uuid
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Private: Serialize the message. Currently using message pack. The
|
36
|
+
# implementation can be changed in order to use some other serializer.
|
37
|
+
#
|
38
|
+
# Returns the serialized String
|
39
|
+
def serialize(message)
|
40
|
+
message.to_msgpack
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RabbitRPC
|
2
|
+
class MessageParserException; end
|
3
|
+
|
4
|
+
class MessageParser
|
5
|
+
attr_reader :service_name, :method_name
|
6
|
+
|
7
|
+
# methods with the following prefix will not wait
|
8
|
+
# for a response
|
9
|
+
ONE_WAY_PREFIX = 'one_way'
|
10
|
+
|
11
|
+
def initialize(message)
|
12
|
+
@message = message
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: Extracts the Service name and method name
|
16
|
+
#
|
17
|
+
# Examples
|
18
|
+
#
|
19
|
+
# "UserService.create"
|
20
|
+
# # => "UserService", "create"
|
21
|
+
#
|
22
|
+
# Returns nothing
|
23
|
+
def parse
|
24
|
+
method = @message.is_a?(RabbitRPC::Message) ? @message.method_name : @message['method']
|
25
|
+
@service_name, @method_name = method.split('.')
|
26
|
+
end
|
27
|
+
|
28
|
+
# Public: Identifies whether a wait for a response is expected
|
29
|
+
#
|
30
|
+
# Returns a Boolean
|
31
|
+
def one_way?
|
32
|
+
parse if @method_name.nil?
|
33
|
+
@method_name.start_with?(ONE_WAY_PREFIX)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RabbitRPC
|
2
|
+
class RequestHandler
|
3
|
+
include Logging
|
4
|
+
|
5
|
+
attr_reader :one_way
|
6
|
+
|
7
|
+
def initialize(serialized_message)
|
8
|
+
@message = Message.unpack serialized_message
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
parser = MessageParser.new(@message)
|
13
|
+
parser.parse
|
14
|
+
|
15
|
+
@one_way = parser.one_way?
|
16
|
+
|
17
|
+
logger.info "Received message #{@message}"
|
18
|
+
|
19
|
+
Kernel.const_get(parser.service_name).send(parser.method_name, *@message['args'] )
|
20
|
+
rescue ArgumentError => e
|
21
|
+
log_exception(e)
|
22
|
+
{ ok: false, message: e.message }
|
23
|
+
rescue Exception => e
|
24
|
+
log_exception(e)
|
25
|
+
exception_response
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# We do not want to return the actual exceptions back
|
31
|
+
# TODO: Perhaps have this user defined?
|
32
|
+
def exception_response
|
33
|
+
{ ok: false, message: 'Error processing request' }
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'bunny'
|
2
|
+
require 'active_support/core_ext/object/try'
|
3
|
+
|
4
|
+
#TODO: Implement callback queue timeout
|
5
|
+
#TODO: RabbitMQ connection options
|
6
|
+
module RabbitRPC
|
7
|
+
# Connects to RabbitMQ for blocking RPC calls.
|
8
|
+
# Wait for a response when querying other services
|
9
|
+
class SynchronousConnection
|
10
|
+
DEFAULT_HEARTBEAT = 30
|
11
|
+
|
12
|
+
include Logging
|
13
|
+
|
14
|
+
# RabbitMQ related info
|
15
|
+
attr_reader :queue_name, :callback_queue_name, :rabbit_mq_url, :heartbeat
|
16
|
+
|
17
|
+
# Message unique identifier and resposne
|
18
|
+
attr_reader :message_id, :response
|
19
|
+
|
20
|
+
def initialize(queue_name, callback_queue_name, rabbit_mq_url, heart_beat = nil)
|
21
|
+
@queue_name = queue_name
|
22
|
+
@callback_queue_name = callback_queue_name
|
23
|
+
@rabbit_mq_url = rabbit_mq_url
|
24
|
+
@heartbeat = heart_beat || DEFAULT_HEARTBEAT
|
25
|
+
|
26
|
+
@message_id = Message.generate_id
|
27
|
+
end
|
28
|
+
|
29
|
+
def publish!(unpacked_message)
|
30
|
+
connect!
|
31
|
+
|
32
|
+
send_request(unpacked_message.pack)
|
33
|
+
|
34
|
+
if wait_for_response?(unpacked_message)
|
35
|
+
callback_queue.subscribe(block: true, ack: true) do |delivery_info, properties, payload|
|
36
|
+
if message_id == properties.try(:[],:correlation_id)
|
37
|
+
@channel.acknowledge(delivery_info.delivery_tag, false)
|
38
|
+
@response = Message.unpack payload
|
39
|
+
|
40
|
+
logger.info "Received message #{@response}"
|
41
|
+
delivery_info.consumer.cancel
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
return @response
|
46
|
+
end
|
47
|
+
|
48
|
+
return response
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def connect!
|
54
|
+
@connection = Bunny.new(@rabbit_mq_url, heartbeat: @heartbeat).start
|
55
|
+
@channel = @connection.create_channel
|
56
|
+
end
|
57
|
+
|
58
|
+
def callback_queue
|
59
|
+
@channel.queue(@callback_queue_name, auto_delete: false)
|
60
|
+
end
|
61
|
+
|
62
|
+
def exchange
|
63
|
+
@exchange ||= @channel.default_exchange
|
64
|
+
end
|
65
|
+
|
66
|
+
def send_request(message)
|
67
|
+
exchange.publish(
|
68
|
+
message,
|
69
|
+
routing_key: @queue_name,
|
70
|
+
message_id: @message_id,
|
71
|
+
reply_to: @callback_queue_name,
|
72
|
+
auto_delete: false
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def wait_for_response?(message)
|
77
|
+
!MessageParser.new(message).one_way?
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/rabbit_rpc.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rabbit_rpc/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rabbit_rpc"
|
8
|
+
spec.version = RabbitRPC::VERSION
|
9
|
+
spec.authors = ["Sohaib Bhatti"]
|
10
|
+
spec.email = ["sohaibbbhatti@gmail.com"]
|
11
|
+
spec.description = %q{Framework for developing services and workers using RabbitMQ}
|
12
|
+
spec.summary = %q{Ruby RabbitMQ framework}
|
13
|
+
spec.homepage = "https://github.com/sohaibbhatti/rabbit_rpc"
|
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.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
|
24
|
+
spec.add_dependency "msgpack", "~> 0.5.8"
|
25
|
+
spec.add_dependency "active_support", "~> 3.0.0"
|
26
|
+
spec.add_dependency "bunny", "~> 0.10.8"
|
27
|
+
spec.add_dependency "amqp", "~> 1.0.4"
|
28
|
+
|
29
|
+
spec.add_development_dependency "rspec", "~> 2.14.1"
|
30
|
+
spec.add_development_dependency "evented-spec"
|
31
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rabbit_rpc'
|
2
|
+
|
3
|
+
describe RabbitRPC::Config do
|
4
|
+
describe '.client_rpc_path' do
|
5
|
+
it 'assumes the file to be present in the config folder by default' do
|
6
|
+
RabbitRPC::Config.client_rpc_path.should == 'config/rabbit_rpc.yml'
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'returns the configured value' do RabbitRPC::Config.client_rpc_path = 'foo/bar.yml'
|
10
|
+
RabbitRPC::Config.client_rpc_path.should == 'foo/bar.yml'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.initialize!' do
|
15
|
+
context 'with a valid YAML file' do
|
16
|
+
before do
|
17
|
+
RabbitRPC::Config.client_rpc_path = 'spec/support/rpc.yaml'
|
18
|
+
RabbitRPC::Config.initialize!
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'reads the client rpc file and initializes classes under the Client namespace' do
|
22
|
+
%w[create read delete].each do |meth|
|
23
|
+
RabbitRPC::Client::UserService::User.should respond_to meth
|
24
|
+
end
|
25
|
+
|
26
|
+
RabbitRPC::Client::UserService::Auth.should respond_to 'authorize'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'successfully encodes the messages with the proper arguments' do
|
30
|
+
# Do not wait for response
|
31
|
+
RabbitRPC::SynchronousConnection.any_instance.stub(:publish!).and_return(true)
|
32
|
+
|
33
|
+
RabbitRPC::Message.should_receive(:new).with('AuthService.authorize', 'omg', 'this', 'works'). \
|
34
|
+
and_call_original
|
35
|
+
RabbitRPC::Client::UserService::Auth.authorize 'omg', 'this', 'works'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'raises an exception if the address of the service is missing in the yml file' do
|
40
|
+
RabbitRPC::Config.client_rpc_path = 'spec/support/rpc_no_address.yaml'
|
41
|
+
expect { RabbitRPC::Config.initialize! }.to raise_error(RabbitRPC::InvalidFormatError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'raises and exception if method definition is not decipherable' do
|
45
|
+
RabbitRPC::Config.client_rpc_path = 'spec/support/rpc_invalid_structure.yaml'
|
46
|
+
expect { RabbitRPC::Config.initialize! }.to raise_error(RabbitRPC::InvalidFormatError)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rabbit_rpc'
|
2
|
+
require 'evented-spec'
|
3
|
+
|
4
|
+
describe RabbitRPC::Connection do
|
5
|
+
include EventedSpec::EMSpec
|
6
|
+
let(:connection) { RabbitRPC::Connection.new('UserService', 'amqp://localhost:5672', 5) }
|
7
|
+
let(:sync_connection) { RabbitRPC::SynchronousConnection.new('UserService', 'UserService.callback', 'amqp://localhost:5672') }
|
8
|
+
let(:exchange) { double 'exchange', publish: true }
|
9
|
+
|
10
|
+
it 'connects to the the specified rabbitMQ queue' do
|
11
|
+
em do
|
12
|
+
::AMQP::Channel.any_instance.should_receive(:queue).with('UserService').and_call_original
|
13
|
+
connection.listen!
|
14
|
+
done(0.5)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'attempts to execute received messages' do
|
19
|
+
RabbitRPC::SynchronousConnection.new('UserService', 'UserService.callback', 'amqp://localhost:5672').publish!(RabbitRPC::Message.new('User.one_way_create'))
|
20
|
+
|
21
|
+
em do
|
22
|
+
RabbitRPC::RequestHandler.should_receive(:new).at_least(1).times.with(RabbitRPC::Message.new('User.one_way_create').pack).and_call_original
|
23
|
+
connection.listen!
|
24
|
+
done(0.5)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when the client expects a response' do
|
29
|
+
|
30
|
+
it 'sends a response to the callback queue' do
|
31
|
+
RabbitRPC::Message.stub(:generate_id).and_return 'omg'
|
32
|
+
::AMQP::Channel.any_instance.stub(:default_exchange).and_return exchange
|
33
|
+
|
34
|
+
send_message_expecting_response
|
35
|
+
|
36
|
+
em do
|
37
|
+
exchange.should_receive(:publish).with(anything(), routing_key: 'UserService.callback', correlation_id: 'omg', mandatory: true)
|
38
|
+
connection.listen!
|
39
|
+
done(0.5)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when the client does not expect a response' do
|
45
|
+
it 'does not send a response to the callback queue' do
|
46
|
+
RabbitRPC::Message.stub(:generate_id).and_return 'omg'
|
47
|
+
::AMQP::Channel.any_instance.stub(:default_exchange).and_return exchange
|
48
|
+
|
49
|
+
send_one_way_message
|
50
|
+
|
51
|
+
em do
|
52
|
+
exchange.should_not_receive(:publish).with(anything(), routing_key: 'UserService.callback', correlation_id: 'omg', mandatory: true)
|
53
|
+
connection.listen!
|
54
|
+
done(0.5)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Disables the client blocking wait for receiving the message
|
60
|
+
# on the callback queue
|
61
|
+
def send_message_expecting_response
|
62
|
+
message = RabbitRPC::Message.new('User.create')
|
63
|
+
sync_connection.stub(:wait_for_response?).and_return(false)
|
64
|
+
sync_connection.publish! message
|
65
|
+
end
|
66
|
+
|
67
|
+
def send_one_way_message
|
68
|
+
message = RabbitRPC::Message.new('User.one_way_create')
|
69
|
+
sync_connection.publish! message
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rabbit_rpc'
|
2
|
+
|
3
|
+
describe RabbitRPC::Logging do
|
4
|
+
let(:random_class) do
|
5
|
+
class RandomClass
|
6
|
+
include RabbitRPC::Logging
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'grants the class and its objects the ability to log' do
|
11
|
+
random_class.logger.should be_instance_of Logger
|
12
|
+
random_class.new.logger.should be_instance_of Logger
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rabbit_rpc'
|
2
|
+
|
3
|
+
describe RabbitRPC::MessageParser do
|
4
|
+
let(:message) { {'method' => 'UserService.create', 'args' => [1, 2, 3] } }
|
5
|
+
let(:one_way) { {'method' => 'AuthService.one_way_delete', 'args' => [1, 2, 3] } }
|
6
|
+
|
7
|
+
describe '#one_way?' do
|
8
|
+
it 'determines if the method begins with the one_way prefix' do
|
9
|
+
example_one = RabbitRPC::MessageParser.new(message)
|
10
|
+
example_one.parse
|
11
|
+
example_one.one_way?.should be_false
|
12
|
+
|
13
|
+
example_two = RabbitRPC::MessageParser.new(one_way)
|
14
|
+
example_two.parse
|
15
|
+
example_two.one_way?.should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'parser has not executed' do
|
19
|
+
it 'determines if the method begins with the one_way prefix' do
|
20
|
+
RabbitRPC::MessageParser.new(message).one_way?.should be_false
|
21
|
+
RabbitRPC::MessageParser.new(one_way).one_way?.should be_true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#parse' do
|
27
|
+
it 'correctly identifies the name of the Service' do
|
28
|
+
example_one = RabbitRPC::MessageParser.new(message)
|
29
|
+
example_one.parse
|
30
|
+
example_one.service_name.should == 'UserService'
|
31
|
+
|
32
|
+
example_two = RabbitRPC::MessageParser.new(one_way)
|
33
|
+
example_two.parse
|
34
|
+
example_two.service_name.should == 'AuthService'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'correctly identifies the name of the method' do
|
38
|
+
example_one = RabbitRPC::MessageParser.new(message)
|
39
|
+
example_one.parse
|
40
|
+
example_one.method_name.should == 'create'
|
41
|
+
|
42
|
+
example_two = RabbitRPC::MessageParser.new(one_way)
|
43
|
+
example_two.parse
|
44
|
+
example_two.method_name.should == 'one_way_delete'
|
45
|
+
end
|
46
|
+
|
47
|
+
# Test case for message class and hash
|
48
|
+
# Invalid format?
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rabbit_rpc'
|
2
|
+
|
3
|
+
describe RabbitRPC::Message do
|
4
|
+
|
5
|
+
describe '#pack' do
|
6
|
+
it 'succesfully shifts the RPC method and its arguments to a single datastructure' do
|
7
|
+
message = RabbitRPC::Message.new 'hello', 'argument_one', { optional: 'arguments' }
|
8
|
+
message.should_receive(:serialize).with(method: 'hello', args: ['argument_one', { optional: 'arguments' }])
|
9
|
+
message.pack
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'succesfully handles the case of no arguments present' do
|
13
|
+
message = RabbitRPC::Message.new 'hello'
|
14
|
+
message.should_receive(:serialize).with(method: 'hello', args: [])
|
15
|
+
message.pack
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.unpack' do
|
20
|
+
it 'successfully converts the serialized message into a readable datastructure' do
|
21
|
+
message = RabbitRPC::Message.new 'hello', 'argument_one', { optional: 'arguments' }
|
22
|
+
RabbitRPC::Message.unpack(message.pack).should == {
|
23
|
+
'method' => 'hello',
|
24
|
+
'args' => ['argument_one', { 'optional' => 'arguments' }]
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '.generate_id' do
|
30
|
+
it 'generates random values' do
|
31
|
+
RabbitRPC::Message.generate_id.should_not be_nil
|
32
|
+
RabbitRPC::Message.generate_id.should_not == RabbitRPC::Message.generate_id
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rabbit_rpc'
|
2
|
+
|
3
|
+
describe RabbitRPC::RequestHandler do
|
4
|
+
let!(:sample_service) do
|
5
|
+
class UserService
|
6
|
+
def self.create
|
7
|
+
'woot'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
let(:message) { RabbitRPC::Message.new 'UserService.create' }
|
12
|
+
let(:one_way_message) { RabbitRPC::Message.new 'UserService.one_way_create' }
|
13
|
+
let(:invalid_message) { RabbitRPC::Message.new 'Usvice.create' }
|
14
|
+
let(:invalid_args_message) { RabbitRPC::Message.new 'UserService.create', 'args' }
|
15
|
+
|
16
|
+
describe '#execute' do
|
17
|
+
it 'succesfully executes the request method' do
|
18
|
+
RabbitRPC::RequestHandler.new(message.pack).execute.should == 'woot'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'identifies whether the method expects a response' do
|
22
|
+
handler = RabbitRPC::RequestHandler.new(message.pack)
|
23
|
+
handler.execute
|
24
|
+
handler.one_way.should be_false
|
25
|
+
|
26
|
+
handler = RabbitRPC::RequestHandler.new(one_way_message.pack)
|
27
|
+
handler.execute
|
28
|
+
handler.one_way.should be_true
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns an exception message in the event of errors' do
|
32
|
+
RabbitRPC::RequestHandler.new(invalid_message.pack).execute.should == {
|
33
|
+
ok: false,
|
34
|
+
message: 'Error processing request'
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns an error notifying an Argument error' do
|
39
|
+
RabbitRPC::RequestHandler.new(invalid_args_message.pack).execute.should == {
|
40
|
+
ok: false,
|
41
|
+
message: 'wrong number of arguments (1 for 0)'
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rabbit_rpc'
|
2
|
+
|
3
|
+
describe RabbitRPC::SynchronousConnection do
|
4
|
+
describe '#publish!' do
|
5
|
+
let(:connection) { RabbitRPC::SynchronousConnection.new('foo', 'bar', 'amqp:://localhost:5672') }
|
6
|
+
let(:exchange) { double 'exchange', publish: true }
|
7
|
+
let(:message) { RabbitRPC::Message.new 'User.create', 'bar' }
|
8
|
+
let(:one_way_message) { RabbitRPC::Message.new 'User.one_way_create', 'bar' }
|
9
|
+
let(:queue) { double 'queue', subscribe: true }
|
10
|
+
|
11
|
+
before do
|
12
|
+
connection.stub(:exchange).and_return exchange
|
13
|
+
connection.stub(:callback_queue).and_return queue
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'sends a message to the relevant queue' do
|
17
|
+
exchange.should_receive(:publish).with(
|
18
|
+
message.pack,
|
19
|
+
routing_key: 'foo',
|
20
|
+
message_id: anything(),
|
21
|
+
reply_to: 'bar',
|
22
|
+
auto_delete: false
|
23
|
+
)
|
24
|
+
connection.publish! message
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when a response is expected' do
|
28
|
+
it 'subscribes to the callback queue to listen for a response' do
|
29
|
+
queue.should_receive :subscribe
|
30
|
+
connection.publish!(message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when no response is expected' do
|
35
|
+
it 'terminates' do
|
36
|
+
queue.should_not_receive :subscribe
|
37
|
+
connection.publish!(one_way_message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rabbit_rpc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sohaib Bhatti
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: msgpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.5.8
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.5.8
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: active_support
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.0.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.0.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bunny
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.10.8
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.10.8
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: amqp
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.0.4
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.0.4
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 2.14.1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.14.1
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: evented-spec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Framework for developing services and workers using RabbitMQ
|
126
|
+
email:
|
127
|
+
- sohaibbbhatti@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- .gitignore
|
133
|
+
- Gemfile
|
134
|
+
- LICENSE.txt
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- lib/rabbit_rpc.rb
|
138
|
+
- lib/rabbit_rpc/client.rb
|
139
|
+
- lib/rabbit_rpc/config.rb
|
140
|
+
- lib/rabbit_rpc/connection.rb
|
141
|
+
- lib/rabbit_rpc/logging.rb
|
142
|
+
- lib/rabbit_rpc/message.rb
|
143
|
+
- lib/rabbit_rpc/message_parser.rb
|
144
|
+
- lib/rabbit_rpc/request_handler.rb
|
145
|
+
- lib/rabbit_rpc/synchronous_connection.rb
|
146
|
+
- lib/rabbit_rpc/version.rb
|
147
|
+
- rabbit_rpc.gemspec
|
148
|
+
- spec/config_spec.rb
|
149
|
+
- spec/connection_spec.rb
|
150
|
+
- spec/logging_spec.rb
|
151
|
+
- spec/message_parser_spec.rb
|
152
|
+
- spec/message_spec.rb
|
153
|
+
- spec/request_handler_spec.rb
|
154
|
+
- spec/support/rpc.yaml
|
155
|
+
- spec/support/rpc_invalid_structure.yaml
|
156
|
+
- spec/support/rpc_no_address.yaml
|
157
|
+
- spec/synchronous_connection_spec.rb
|
158
|
+
homepage: https://github.com/sohaibbhatti/rabbit_rpc
|
159
|
+
licenses:
|
160
|
+
- MIT
|
161
|
+
metadata: {}
|
162
|
+
post_install_message:
|
163
|
+
rdoc_options: []
|
164
|
+
require_paths:
|
165
|
+
- lib
|
166
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - '>='
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - '>='
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
requirements: []
|
177
|
+
rubyforge_project:
|
178
|
+
rubygems_version: 2.0.3
|
179
|
+
signing_key:
|
180
|
+
specification_version: 4
|
181
|
+
summary: Ruby RabbitMQ framework
|
182
|
+
test_files:
|
183
|
+
- spec/config_spec.rb
|
184
|
+
- spec/connection_spec.rb
|
185
|
+
- spec/logging_spec.rb
|
186
|
+
- spec/message_parser_spec.rb
|
187
|
+
- spec/message_spec.rb
|
188
|
+
- spec/request_handler_spec.rb
|
189
|
+
- spec/support/rpc.yaml
|
190
|
+
- spec/support/rpc_invalid_structure.yaml
|
191
|
+
- spec/support/rpc_no_address.yaml
|
192
|
+
- spec/synchronous_connection_spec.rb
|