beetle 0.2.11 → 0.3.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -1
- data/beetle.gemspec +9 -7
- data/examples/attempts.rb +2 -2
- data/examples/pause_and_resume.rb +75 -0
- data/examples/simple.rb +3 -3
- data/examples/test_publisher.rb +32 -0
- data/features/support/env.rb +3 -1
- data/features/support/test_daemons/redis.conf.erb +2 -2
- data/lib/beetle/base.rb +1 -1
- data/lib/beetle/client.rb +57 -21
- data/lib/beetle/handler.rb +22 -6
- data/lib/beetle/publisher.rb +8 -4
- data/lib/beetle/subscriber.rb +50 -12
- data/lib/beetle.rb +3 -1
- data/test/beetle/client_test.rb +64 -13
- data/test/beetle/handler_test.rb +10 -3
- data/test/beetle/publisher_test.rb +1 -1
- data/test/beetle/subscriber_test.rb +88 -11
- data/test/test_helper.rb +6 -5
- metadata +79 -47
data/Rakefile
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/testtask'
|
3
3
|
require 'rcov/rcovtask'
|
4
|
+
# rake 0.9.2 hack to supress deprecation warnings caused by cucumber
|
5
|
+
include Rake::DSL if RAKEVERSION >= "0.9"
|
4
6
|
require 'cucumber/rake/task'
|
5
|
-
require 'active_support'
|
6
7
|
|
7
8
|
# 1.8/1.9 compatible way of loading lib/beetle.rb
|
8
9
|
$:.unshift 'lib'
|
data/beetle.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "beetle"
|
3
|
-
s.version = "0.
|
4
|
-
s.required_rubygems_version = ">= 1.3.
|
3
|
+
s.version = "0.3.0.rc.1"
|
4
|
+
s.required_rubygems_version = ">= 1.3.7"
|
5
5
|
s.authors = ["Stefan Kaes", "Pascal Friederich", "Ali Jelveh", "Sebastian Roebke"]
|
6
6
|
s.date = Time.now.strftime('%Y-%m-%d')
|
7
7
|
s.default_executable = "beetle"
|
@@ -28,15 +28,17 @@ Gem::Specification.new do |s|
|
|
28
28
|
INFO
|
29
29
|
|
30
30
|
s.specification_version = 3
|
31
|
-
s.add_runtime_dependency("uuid4r", [">= 0.1.
|
32
|
-
s.add_runtime_dependency("bunny", ["
|
33
|
-
s.add_runtime_dependency("bunny-ext", [">= 0.6.5"])
|
31
|
+
s.add_runtime_dependency("uuid4r", [">= 0.1.2"])
|
32
|
+
s.add_runtime_dependency("bunny", ["~> 0.7.1"])
|
34
33
|
s.add_runtime_dependency("redis", ["= 2.0.4"])
|
35
|
-
s.add_runtime_dependency("amqp", ["
|
36
|
-
s.add_runtime_dependency("activesupport", ["
|
34
|
+
s.add_runtime_dependency("amqp", ["~> 0.6.7"])
|
35
|
+
s.add_runtime_dependency("activesupport", [">= 2.3.4"])
|
37
36
|
s.add_runtime_dependency("daemons", [">= 1.0.10"])
|
37
|
+
s.add_development_dependency("rake", [">= 0.8.7"])
|
38
38
|
s.add_development_dependency("mocha", [">= 0"])
|
39
39
|
s.add_development_dependency("rcov", [">= 0"])
|
40
|
+
s.add_development_dependency("redgreen", [">= 0"])
|
41
|
+
s.add_development_dependency("wirble", [">= 0"])
|
40
42
|
s.add_development_dependency("cucumber", [">= 0.7.2"])
|
41
43
|
s.add_development_dependency("daemon_controller", [">= 0"])
|
42
44
|
end
|
data/examples/attempts.rb
CHANGED
@@ -47,7 +47,7 @@ class Handler < Beetle::Handler
|
|
47
47
|
# we're stopping the event loop so this script stops after that
|
48
48
|
def failure(result)
|
49
49
|
super
|
50
|
-
$client.stop_listening
|
50
|
+
EM.add_timer(1){$client.stop_listening}
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -62,5 +62,5 @@ $client.listen
|
|
62
62
|
|
63
63
|
# error handling, if everything went right this shouldn't happen.
|
64
64
|
if $exceptions != $max_exceptions + 1
|
65
|
-
raise "
|
65
|
+
raise "Something is fishy. Failed #{$exceptions} times"
|
66
66
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# attempts.rb
|
2
|
+
# this example shows you how to use the exception limiting feature of beetle
|
3
|
+
# it allows you to control the number of retries your handler will go through
|
4
|
+
# with one message before giving up on it
|
5
|
+
#
|
6
|
+
# ! check the examples/README.rdoc for information on starting your redis/rabbit !
|
7
|
+
#
|
8
|
+
# start it with ruby attempts.rb
|
9
|
+
|
10
|
+
require "rubygems"
|
11
|
+
require File.expand_path("../lib/beetle", File.dirname(__FILE__))
|
12
|
+
|
13
|
+
# set Beetle log level to info, less noisy than debug
|
14
|
+
Beetle.config.logger.level = Logger::INFO
|
15
|
+
|
16
|
+
# setup client
|
17
|
+
$client = Beetle::Client.new
|
18
|
+
$client.register_queue(:test)
|
19
|
+
$client.register_message(:test)
|
20
|
+
|
21
|
+
# purge the test queue
|
22
|
+
$client.purge(:test)
|
23
|
+
|
24
|
+
# initially, our service is online
|
25
|
+
$online = true
|
26
|
+
|
27
|
+
# declare a handler class for message processing
|
28
|
+
# in this example we've not only overwritten the process method but also the
|
29
|
+
# error and failure methods of the handler baseclass
|
30
|
+
class Handler < Beetle::Handler
|
31
|
+
|
32
|
+
# called when the handler receives the message - fail everytime
|
33
|
+
def process
|
34
|
+
puts "received message #{message.data} online=#{$online}"
|
35
|
+
unless $online
|
36
|
+
$client.pause_listening(:test)
|
37
|
+
raise "offline"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# called when handler process raised an exception
|
42
|
+
def error(exception)
|
43
|
+
puts "execution failed: #{exception}"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
# publish a decent amount of messages
|
49
|
+
# 1000.times do |i|
|
50
|
+
# $client.publish(:test, i)
|
51
|
+
# end
|
52
|
+
# $client.stop_publishing
|
53
|
+
|
54
|
+
# register our handler to the message, configure it to our max_exceptions limit, we configure a delay of 0 to have it not wait before retrying
|
55
|
+
$client.register_handler(:test, Handler, :exceptions => 1, :delay => 0)
|
56
|
+
|
57
|
+
# and start our listening loop...
|
58
|
+
$client.listen do
|
59
|
+
n = 0
|
60
|
+
ptimer = EM.add_periodic_timer(0.1) do
|
61
|
+
data = (n+=1)
|
62
|
+
puts "publishing message #{data}"
|
63
|
+
$client.publish(:test, data)
|
64
|
+
end
|
65
|
+
|
66
|
+
EM.add_periodic_timer(2) do
|
67
|
+
$online = !$online
|
68
|
+
$client.resume_listening(:test) if $online
|
69
|
+
end
|
70
|
+
|
71
|
+
EM.add_timer(10) do
|
72
|
+
$client.pause_listening
|
73
|
+
EM.add_timer(1) { $client.stop_listening }
|
74
|
+
end
|
75
|
+
end
|
data/examples/simple.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
#
|
6
6
|
# ! check the examples/README.rdoc for information on starting your redis/rabbit !
|
7
7
|
#
|
8
|
-
# start it with ruby
|
8
|
+
# start it with ruby simple.rb
|
9
9
|
|
10
10
|
require "rubygems"
|
11
11
|
require File.expand_path("../lib/beetle", File.dirname(__FILE__))
|
@@ -27,8 +27,8 @@ client.deduplication_store.flushdb
|
|
27
27
|
# register our handler to the message, check out the message.rb for more stuff you can get from the message object
|
28
28
|
client.register_handler(:test) {|message| puts "got message: #{message.data}"}
|
29
29
|
|
30
|
-
# publish our message
|
31
|
-
client.publish(:test, 'bam')
|
30
|
+
# publish our message (NOTE: empty message bodies don't work, most likely due to bugs in bunny/amqp)
|
31
|
+
puts client.publish(:test, 'bam')
|
32
32
|
|
33
33
|
# start listening
|
34
34
|
# this starts the event machine event loop using EM.run
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# attempts.rb
|
2
|
+
# this example shows you how to use the exception limiting feature of beetle
|
3
|
+
# it allows you to control the number of retries your handler will go through
|
4
|
+
# with one message before giving up on it
|
5
|
+
#
|
6
|
+
# ! check the examples/README.rdoc for information on starting your redis/rabbit !
|
7
|
+
#
|
8
|
+
# start it with ruby attempts.rb
|
9
|
+
|
10
|
+
require "rubygems"
|
11
|
+
require File.expand_path("../lib/beetle", File.dirname(__FILE__))
|
12
|
+
require "eventmachine"
|
13
|
+
|
14
|
+
# set Beetle log level to info, less noisy than debug
|
15
|
+
Beetle.config.logger.level = Logger::INFO
|
16
|
+
|
17
|
+
# setup client
|
18
|
+
client = Beetle::Client.new
|
19
|
+
client.register_message(:test)
|
20
|
+
|
21
|
+
n = 0
|
22
|
+
EM.run do
|
23
|
+
EM.add_periodic_timer(0.1) do
|
24
|
+
data = (n+=1)
|
25
|
+
client.logger.info "publishing #{data}"
|
26
|
+
client.publish(:test, data)
|
27
|
+
end
|
28
|
+
trap("INT") do
|
29
|
+
client.stop_publishing
|
30
|
+
EM.stop_event_loop
|
31
|
+
end
|
32
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -40,7 +40,9 @@ def first_redis_configuration_client_pid
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def system_notification_log_path
|
43
|
-
tmp_path + "/system_notifications.log"
|
43
|
+
log_path = tmp_path + "/system_notifications.log"
|
44
|
+
`touch #{log_path}` unless File.exists?(log_path)
|
45
|
+
log_path
|
44
46
|
end
|
45
47
|
|
46
48
|
def tmp_path
|
@@ -185,5 +185,5 @@ glueoutputbuf yes
|
|
185
185
|
# WARNING: object sharing is experimental, don't enable this feature
|
186
186
|
# in production before of Redis 1.0-stable. Still please try this feature in
|
187
187
|
# your development environment so that we can test it better.
|
188
|
-
shareobjects no
|
189
|
-
shareobjectspoolsize 1024
|
188
|
+
# shareobjects no
|
189
|
+
# shareobjectspoolsize 1024
|
data/lib/beetle/base.rb
CHANGED
@@ -53,7 +53,7 @@ module Beetle
|
|
53
53
|
queues[name] ||=
|
54
54
|
begin
|
55
55
|
opts = @client.queues[name]
|
56
|
-
|
56
|
+
raise UnknownQueue.new("You are trying to bind a queue #{name} which is not configured!") unless opts
|
57
57
|
logger.debug("Beetle: binding queue #{name} with internal name #{opts[:amqp_name]} on server #{@server}")
|
58
58
|
queue_name = opts[:amqp_name]
|
59
59
|
creation_options = opts.slice(*QUEUE_CREATION_KEYS)
|
data/lib/beetle/client.rb
CHANGED
@@ -140,8 +140,7 @@ module Beetle
|
|
140
140
|
# For details on handler classes see class Beetle::Handler
|
141
141
|
|
142
142
|
def register_handler(queues, *args, &block)
|
143
|
-
queues = Array(queues)
|
144
|
-
queues.each {|q| raise UnknownQueue.new(q) unless self.queues.include?(q)}
|
143
|
+
queues = determine_queue_names(Array(queues))
|
145
144
|
opts = args.last.is_a?(Hash) ? args.pop : {}
|
146
145
|
handler = args.shift
|
147
146
|
raise ArgumentError.new("too many arguments for handler registration") unless args.empty?
|
@@ -167,9 +166,9 @@ module Beetle
|
|
167
166
|
end
|
168
167
|
|
169
168
|
# publishes a message. the given options hash is merged with options given on message registration.
|
169
|
+
# WARNING: empty message bodies can lead to problems.
|
170
170
|
def publish(message_name, data=nil, opts={})
|
171
|
-
message_name = message_name
|
172
|
-
raise UnknownMessage.new("unknown message #{message_name}") unless messages.include?(message_name)
|
171
|
+
message_name = validated_message_name(message_name)
|
173
172
|
publisher.publish(message_name, data, opts)
|
174
173
|
end
|
175
174
|
|
@@ -177,24 +176,28 @@ module Beetle
|
|
177
176
|
#
|
178
177
|
# unexpected behavior can ensue if the message gets routed to more than one recipient, so be careful.
|
179
178
|
def rpc(message_name, data=nil, opts={})
|
180
|
-
message_name = message_name
|
181
|
-
raise UnknownMessage.new("unknown message #{message_name}") unless messages.include?(message_name)
|
179
|
+
message_name = validated_message_name(message_name)
|
182
180
|
publisher.rpc(message_name, data, opts)
|
183
181
|
end
|
184
182
|
|
185
|
-
# purges the given
|
186
|
-
def purge(
|
187
|
-
|
188
|
-
|
189
|
-
publisher.purge(queue_name)
|
183
|
+
# purges the given queues on all configured servers
|
184
|
+
def purge(*queues)
|
185
|
+
queues = determine_queue_names(queues)
|
186
|
+
publisher.purge(queues)
|
190
187
|
end
|
191
188
|
|
192
|
-
# start listening to
|
189
|
+
# start listening to all registered queues. Calls #listen_queues internally
|
193
190
|
# runs the given block before entering the eventmachine loop.
|
194
|
-
def listen(
|
195
|
-
|
196
|
-
|
197
|
-
|
191
|
+
def listen(_deprecated_messages=nil, &block)
|
192
|
+
raise Error.new("Beetle::Client#listen no longer works with arguments. Please use #listen_queues(['queue1', 'queue2']) instead") if _deprecated_messages
|
193
|
+
listen_queues(&block)
|
194
|
+
end
|
195
|
+
|
196
|
+
# start listening to a list of queues (default to all registered queues).
|
197
|
+
# runs the given block before entering the eventmachine loop.
|
198
|
+
def listen_queues(*queues, &block)
|
199
|
+
queues = determine_queue_names(queues)
|
200
|
+
subscriber.listen_queues(queues, &block)
|
198
201
|
end
|
199
202
|
|
200
203
|
# stops the eventmachine loop
|
@@ -207,19 +210,32 @@ module Beetle
|
|
207
210
|
publisher.stop
|
208
211
|
end
|
209
212
|
|
210
|
-
#
|
211
|
-
def
|
212
|
-
queues
|
213
|
+
# pause listening on a list of queues
|
214
|
+
def pause_listening(*queues)
|
215
|
+
queues = determine_queue_names(queues)
|
216
|
+
subscriber.pause_listening(queues)
|
217
|
+
end
|
218
|
+
|
219
|
+
# resume listening on a list of queues
|
220
|
+
def resume_listening(*queues)
|
221
|
+
queues = determine_queue_names(queues)
|
222
|
+
subscriber.resume_listening(queues)
|
223
|
+
end
|
224
|
+
|
225
|
+
# traces queues without consuming them. useful for debugging message flow.
|
226
|
+
def trace(queue_names=self.queues.keys, &block)
|
227
|
+
queues_to_trace = self.queues.slice(*queue_names)
|
228
|
+
queues_to_trace.each do |name, opts|
|
213
229
|
opts.merge! :durable => false, :auto_delete => true, :amqp_name => queue_name_for_tracing(opts[:amqp_name])
|
214
230
|
end
|
215
|
-
register_handler(
|
231
|
+
register_handler(queue_names) do |msg|
|
216
232
|
puts "-----===== new message =====-----"
|
217
233
|
puts "SERVER: #{msg.server}"
|
218
234
|
puts "HEADER: #{msg.header.inspect}"
|
219
235
|
puts "MSGID: #{msg.msg_id}"
|
220
236
|
puts "DATA: #{msg.data}"
|
221
237
|
end
|
222
|
-
|
238
|
+
listen_queues(queue_names, &block)
|
223
239
|
end
|
224
240
|
|
225
241
|
# evaluate the ruby files matching the given +glob+ pattern in the context of the client instance.
|
@@ -232,6 +248,26 @@ module Beetle
|
|
232
248
|
|
233
249
|
private
|
234
250
|
|
251
|
+
def determine_queue_names(queues)
|
252
|
+
if queues.empty?
|
253
|
+
self.queues.keys
|
254
|
+
else
|
255
|
+
queues.flatten.map{|q| validated_queue_name(q)}
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def validated_queue_name(queue_name)
|
260
|
+
queue_name = queue_name.to_s
|
261
|
+
raise UnknownQueue.new("unknown queue #{queue_name}") unless queues.include?(queue_name)
|
262
|
+
queue_name
|
263
|
+
end
|
264
|
+
|
265
|
+
def validated_message_name(message_name)
|
266
|
+
message_name = message_name.to_s
|
267
|
+
raise UnknownMessage.new("unknown message #{message_name}") unless messages.include?(message_name)
|
268
|
+
message_name
|
269
|
+
end
|
270
|
+
|
235
271
|
class Configurator #:nodoc:all
|
236
272
|
def initialize(client, options={})
|
237
273
|
@client = client
|
data/lib/beetle/handler.rb
CHANGED
@@ -11,13 +11,16 @@ module Beetle
|
|
11
11
|
# the Message instance which caused the handler to be created
|
12
12
|
attr_reader :message
|
13
13
|
|
14
|
-
def self.create(
|
15
|
-
if
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
def self.create(handler, opts={}) #:nodoc:
|
15
|
+
if handler.is_a? Handler
|
16
|
+
# a handler instance
|
17
|
+
handler
|
18
|
+
elsif handler.is_a?(Class) && handler.ancestors.include?(Handler)
|
19
|
+
# handler class
|
20
|
+
handler.new
|
19
21
|
else
|
20
|
-
|
22
|
+
# presumably something which responds to call
|
23
|
+
new(handler, opts)
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
@@ -70,6 +73,13 @@ module Beetle
|
|
70
73
|
Beetle::reraise_expectation_errors!
|
71
74
|
end
|
72
75
|
|
76
|
+
# should not be overriden in subclasses
|
77
|
+
def processing_completed
|
78
|
+
completed
|
79
|
+
rescue Exception
|
80
|
+
Beetle::reraise_expectation_errors!
|
81
|
+
end
|
82
|
+
|
73
83
|
# called when handler execution raised an exception and no error callback was
|
74
84
|
# specified when the handler instance was created
|
75
85
|
def error(exception)
|
@@ -83,6 +93,12 @@ module Beetle
|
|
83
93
|
logger.error "Beetle: handler has finally failed"
|
84
94
|
end
|
85
95
|
|
96
|
+
# called after all normal processing has been completed. flushes the loggger, if it responds to flush.
|
97
|
+
def completed
|
98
|
+
logger.debug "Beetle: message processing completed"
|
99
|
+
logger.flush if logger.respond_to?(:flush)
|
100
|
+
end
|
101
|
+
|
86
102
|
# returns the configured Beetle logger
|
87
103
|
def self.logger
|
88
104
|
Beetle.config.logger
|
data/lib/beetle/publisher.rb
CHANGED
@@ -16,7 +16,7 @@ module Beetle
|
|
16
16
|
Bunny::ConnectionError, Bunny::ForcedChannelCloseError, Bunny::ForcedConnectionCloseError,
|
17
17
|
Bunny::MessageError, Bunny::ProtocolError, Bunny::ServerDownError, Bunny::UnsubscribeError,
|
18
18
|
Bunny::AcknowledgementError, Qrack::BufferOverflowError, Qrack::InvalidTypeError,
|
19
|
-
Errno::EHOSTUNREACH, Errno::ECONNRESET
|
19
|
+
Errno::EHOSTUNREACH, Errno::ECONNRESET, Timeout::Error
|
20
20
|
]
|
21
21
|
end
|
22
22
|
|
@@ -130,8 +130,12 @@ module Beetle
|
|
130
130
|
[status, result]
|
131
131
|
end
|
132
132
|
|
133
|
-
def purge(
|
134
|
-
each_server
|
133
|
+
def purge(queue_names) #:nodoc:
|
134
|
+
each_server do
|
135
|
+
queue_names.each do |name|
|
136
|
+
queue(name).purge rescue nil
|
137
|
+
end
|
138
|
+
end
|
135
139
|
end
|
136
140
|
|
137
141
|
def stop #:nodoc:
|
@@ -210,7 +214,7 @@ module Beetle
|
|
210
214
|
end
|
211
215
|
end
|
212
216
|
rescue Exception => e
|
213
|
-
logger.
|
217
|
+
logger.warn "Beetle: error closing down bunny #{e}"
|
214
218
|
Beetle::reraise_expectation_errors!
|
215
219
|
ensure
|
216
220
|
@bunnies[@server] = nil
|
data/lib/beetle/subscriber.rb
CHANGED
@@ -12,34 +12,46 @@ module Beetle
|
|
12
12
|
@handlers = {}
|
13
13
|
@amqp_connections = {}
|
14
14
|
@mqs = {}
|
15
|
+
@subscriptions = {}
|
15
16
|
end
|
16
17
|
|
17
|
-
# the client calls this method to subscribe to
|
18
|
-
#
|
19
|
-
# things:
|
18
|
+
# the client calls this method to subscribe to a list of queues.
|
19
|
+
# this method does the following things:
|
20
20
|
#
|
21
|
-
# * creates all exchanges which have been registered for the given
|
22
|
-
# * creates and binds
|
21
|
+
# * creates all exchanges which have been registered for the given queues
|
22
|
+
# * creates and binds each listed queue queues
|
23
23
|
# * subscribes the handlers for all these queues
|
24
24
|
#
|
25
25
|
# yields before entering the eventmachine loop (if a block was given)
|
26
|
-
def
|
26
|
+
def listen_queues(queues) #:nodoc:
|
27
27
|
EM.run do
|
28
|
-
exchanges =
|
28
|
+
exchanges = exchanges_for_queues(queues)
|
29
29
|
create_exchanges(exchanges)
|
30
|
-
queues = queues_for_exchanges(exchanges)
|
31
30
|
bind_queues(queues)
|
32
31
|
subscribe_queues(queues)
|
33
32
|
yield if block_given?
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
36
|
+
def pause_listening(queues)
|
37
|
+
each_server do
|
38
|
+
queues.each { |name| pause(name) if has_subscription?(name) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def resume_listening(queues)
|
43
|
+
each_server do
|
44
|
+
queues.each { |name| resume(name) if has_subscription?(name) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
37
48
|
# closes all AMQP connections and stops the eventmachine loop
|
38
49
|
def stop! #:nodoc:
|
39
50
|
if @amqp_connections.empty?
|
40
51
|
EM.stop_event_loop
|
41
52
|
else
|
42
53
|
server, connection = @amqp_connections.shift
|
54
|
+
logger.debug "Beetle: closing connection to #{server}"
|
43
55
|
connection.close { stop! }
|
44
56
|
end
|
45
57
|
end
|
@@ -53,8 +65,8 @@ module Beetle
|
|
53
65
|
|
54
66
|
private
|
55
67
|
|
56
|
-
def
|
57
|
-
@client.
|
68
|
+
def exchanges_for_queues(queues)
|
69
|
+
@client.bindings.slice(*queues).map{|_, opts| opts.map{|opt| opt[:exchange]}}.flatten.uniq
|
58
70
|
end
|
59
71
|
|
60
72
|
def queues_for_exchanges(exchanges)
|
@@ -88,24 +100,46 @@ module Beetle
|
|
88
100
|
@mqs[server] ||= MQ.new(amqp_connection).prefetch(1)
|
89
101
|
end
|
90
102
|
|
103
|
+
def subscriptions(server=@server)
|
104
|
+
@subscriptions[server] ||= {}
|
105
|
+
end
|
106
|
+
|
107
|
+
def has_subscription?(name)
|
108
|
+
subscriptions.include?(name)
|
109
|
+
end
|
110
|
+
|
91
111
|
def subscribe(queue_name)
|
92
112
|
error("no handler for queue #{queue_name}") unless @handlers.include?(queue_name)
|
93
113
|
opts, handler = @handlers[queue_name]
|
94
114
|
queue_opts = @client.queues[queue_name][:amqp_name]
|
95
115
|
amqp_queue_name = queue_opts
|
96
116
|
callback = create_subscription_callback(queue_name, amqp_queue_name, handler, opts)
|
117
|
+
keys = opts.slice(*SUBSCRIPTION_KEYS).merge(:key => "#", :ack => true)
|
97
118
|
logger.debug "Beetle: subscribing to queue #{amqp_queue_name} with key # on server #{@server}"
|
98
119
|
begin
|
99
|
-
queues[queue_name].subscribe(
|
120
|
+
queues[queue_name].subscribe(keys, &callback)
|
121
|
+
subscriptions[queue_name] = [keys, callback]
|
100
122
|
rescue MQ::Error
|
101
123
|
error("Beetle: binding multiple handlers for the same queue isn't possible.")
|
102
124
|
end
|
103
125
|
end
|
104
126
|
|
127
|
+
def pause(queue_name)
|
128
|
+
return unless queues[queue_name].subscribed?
|
129
|
+
queues[queue_name].unsubscribe
|
130
|
+
end
|
131
|
+
|
132
|
+
def resume(queue_name)
|
133
|
+
return if queues[queue_name].subscribed?
|
134
|
+
keys, callback = subscriptions[queue_name]
|
135
|
+
queues[queue_name].subscribe(keys, &callback)
|
136
|
+
end
|
137
|
+
|
105
138
|
def create_subscription_callback(queue_name, amqp_queue_name, handler, opts)
|
106
139
|
server = @server
|
107
140
|
lambda do |header, data|
|
108
141
|
begin
|
142
|
+
# logger.debug "Beetle: received message"
|
109
143
|
processor = Handler.create(handler, opts)
|
110
144
|
message_options = opts.merge(:server => server, :store => @client.deduplication_store)
|
111
145
|
m = Message.new(amqp_queue_name, header, data, message_options)
|
@@ -121,10 +155,14 @@ module Beetle
|
|
121
155
|
exchange = MQ::Exchange.new(mq(server), :direct, "", :key => reply_to)
|
122
156
|
exchange.publish(m.handler_result.to_s, :headers => {:status => status})
|
123
157
|
end
|
158
|
+
# logger.debug "Beetle: processed message"
|
124
159
|
rescue Exception
|
125
160
|
Beetle::reraise_expectation_errors!
|
126
161
|
# swallow all exceptions
|
127
162
|
logger.error "Beetle: internal error during message processing: #{$!}: #{$!.backtrace.join("\n")}"
|
163
|
+
ensure
|
164
|
+
# processing_completed swallows all exceptions, so we don't need to protect this call
|
165
|
+
processor.processing_completed
|
128
166
|
end
|
129
167
|
end
|
130
168
|
end
|
@@ -146,7 +184,7 @@ module Beetle
|
|
146
184
|
|
147
185
|
def new_amqp_connection
|
148
186
|
# FIXME: wtf, how to test that reconnection feature....
|
149
|
-
con = AMQP.connect(:host => current_host, :port => current_port,
|
187
|
+
con = AMQP.connect(:host => current_host, :port => current_port, :logging => false,
|
150
188
|
:user => Beetle.config.user, :pass => Beetle.config.password, :vhost => Beetle.config.vhost)
|
151
189
|
con.instance_variable_set("@on_disconnect", proc{ con.__send__(:reconnect) })
|
152
190
|
con
|
data/lib/beetle.rb
CHANGED
data/test/beetle/client_test.rb
CHANGED
@@ -236,14 +236,14 @@ module Beetle
|
|
236
236
|
test "should delegate queue purging to the publisher instance" do
|
237
237
|
client = Client.new
|
238
238
|
client.register_queue(:queue)
|
239
|
-
client.send(:publisher).expects(:purge).with("queue").returns("ha!")
|
239
|
+
client.send(:publisher).expects(:purge).with(["queue"]).returns("ha!")
|
240
240
|
assert_equal "ha!", client.purge("queue")
|
241
241
|
end
|
242
242
|
|
243
243
|
test "purging a queue should convert the queue name to a string" do
|
244
244
|
client = Client.new
|
245
245
|
client.register_queue(:queue)
|
246
|
-
client.send(:publisher).expects(:purge).with("queue").returns("ha!")
|
246
|
+
client.send(:publisher).expects(:purge).with(["queue"]).returns("ha!")
|
247
247
|
assert_equal "ha!", client.purge(:queue)
|
248
248
|
end
|
249
249
|
|
@@ -251,6 +251,14 @@ module Beetle
|
|
251
251
|
assert_raises(UnknownQueue) { Client.new.purge(:mumu) }
|
252
252
|
end
|
253
253
|
|
254
|
+
test "should be possible to purge multiple queues with a single call" do
|
255
|
+
client = Client.new
|
256
|
+
client.register_queue(:queue1)
|
257
|
+
client.register_queue(:queue2)
|
258
|
+
client.send(:publisher).expects(:purge).with(["queue1","queue2"]).returns("ha!")
|
259
|
+
assert_equal "ha!", client.purge(:queue1, :queue2)
|
260
|
+
end
|
261
|
+
|
254
262
|
test "should delegate rpc calls to the publisher instance" do
|
255
263
|
client = Client.new
|
256
264
|
client.register_message("deadletter")
|
@@ -261,18 +269,36 @@ module Beetle
|
|
261
269
|
|
262
270
|
test "should delegate listening to the subscriber instance" do
|
263
271
|
client = Client.new
|
264
|
-
client.register_queue(
|
265
|
-
client.
|
266
|
-
client.
|
267
|
-
client.
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
+
client.register_queue("b_queue")
|
273
|
+
client.register_queue("a_queue")
|
274
|
+
client.send(:subscriber).expects(:listen_queues).with {|value| value.include?("a_queue") && value.include?("b_queue")}.yields
|
275
|
+
client.listen
|
276
|
+
end
|
277
|
+
|
278
|
+
test "trying to listen to a message is no longer supported and should raise an exception" do
|
279
|
+
assert_raises(Error) { Client.new.listen([:a])}
|
280
|
+
end
|
281
|
+
|
282
|
+
test "should delegate listening to queues to the subscriber instance" do
|
283
|
+
client = Client.new
|
284
|
+
client.register_queue(:test)
|
285
|
+
client.send(:subscriber).expects(:listen_queues).with(['test']).yields
|
286
|
+
client.listen_queues([:test])
|
272
287
|
end
|
273
288
|
|
274
|
-
test "trying to listen to an unknown
|
275
|
-
|
289
|
+
test "trying to listen to an unknown queue should raise an exception" do
|
290
|
+
client = Client.new
|
291
|
+
assert_raises(UnknownQueue) { Client.new.listen_queues([:foobar])}
|
292
|
+
end
|
293
|
+
|
294
|
+
test "trying to pause listening on an unknown queue should raise an exception" do
|
295
|
+
client = Client.new
|
296
|
+
assert_raises(UnknownQueue) { Client.new.pause_listening(:foobar)}
|
297
|
+
end
|
298
|
+
|
299
|
+
test "trying to resume listening on an unknown queue should raise an exception" do
|
300
|
+
client = Client.new
|
301
|
+
assert_raises(UnknownQueue) { Client.new.pause_listening(:foobar)}
|
276
302
|
end
|
277
303
|
|
278
304
|
test "should delegate stop_listening to the subscriber instance" do
|
@@ -281,6 +307,20 @@ module Beetle
|
|
281
307
|
client.stop_listening
|
282
308
|
end
|
283
309
|
|
310
|
+
test "should delegate pause_listening to the subscriber instance" do
|
311
|
+
client = Client.new
|
312
|
+
client.register_queue(:test)
|
313
|
+
client.send(:subscriber).expects(:pause_listening).with(%w(test))
|
314
|
+
client.pause_listening(:test)
|
315
|
+
end
|
316
|
+
|
317
|
+
test "should delegate resume_listening to the subscriber instance" do
|
318
|
+
client = Client.new
|
319
|
+
client.register_queue(:test)
|
320
|
+
client.send(:subscriber).expects(:resume_listening).with(%w(test))
|
321
|
+
client.resume_listening(:test)
|
322
|
+
end
|
323
|
+
|
284
324
|
test "should delegate handler registration to the subscriber instance" do
|
285
325
|
client = Client.new
|
286
326
|
client.register_queue("huhu")
|
@@ -314,7 +354,7 @@ module Beetle
|
|
314
354
|
client.register_queue("test")
|
315
355
|
sub = client.send(:subscriber)
|
316
356
|
sub.expects(:register_handler).with(client.queues.keys, {}, nil).yields(stub_everything("message"))
|
317
|
-
sub.expects(:
|
357
|
+
sub.expects(:listen_queues)
|
318
358
|
client.stubs(:puts)
|
319
359
|
client.trace
|
320
360
|
test_queue_opts = client.queues["test"]
|
@@ -324,5 +364,16 @@ module Beetle
|
|
324
364
|
assert !test_queue_opts[:durable]
|
325
365
|
end
|
326
366
|
|
367
|
+
test "limiting tracing to some queues" do
|
368
|
+
client = Client.new
|
369
|
+
client.register_queue("test")
|
370
|
+
client.register_queue("irrelevant")
|
371
|
+
sub = client.send(:subscriber)
|
372
|
+
sub.expects(:register_handler).with(["test"], {}, nil).yields(stub_everything("message"))
|
373
|
+
sub.expects(:listen_queues).with(["test"])
|
374
|
+
client.stubs(:puts)
|
375
|
+
client.trace(["test"])
|
376
|
+
end
|
377
|
+
|
327
378
|
end
|
328
379
|
end
|
data/test/beetle/handler_test.rb
CHANGED
@@ -77,17 +77,18 @@ module Beetle
|
|
77
77
|
assert_equal Beetle.config.logger, Handler.logger
|
78
78
|
end
|
79
79
|
|
80
|
-
test "default implementation of error and process and failure should not crash" do
|
80
|
+
test "default implementation of error and process and failure and completed should not crash" do
|
81
81
|
handler = Handler.create(lambda {})
|
82
82
|
handler.process
|
83
83
|
handler.error('barfoo')
|
84
84
|
handler.failure('razzmatazz')
|
85
|
+
handler.completed
|
85
86
|
end
|
86
87
|
|
87
88
|
test "should silently rescue exceptions in the process_exception call" do
|
88
89
|
mock = mock('error handler')
|
89
90
|
e = Exception.new
|
90
|
-
mock.expects(:call).with('message', e).raises(
|
91
|
+
mock.expects(:call).with('message', e).raises(Exception)
|
91
92
|
handler = Handler.create(lambda {}, :errback => mock)
|
92
93
|
handler.instance_variable_set(:@message, 'message')
|
93
94
|
assert_nothing_raised {handler.process_exception(e)}
|
@@ -95,11 +96,17 @@ module Beetle
|
|
95
96
|
|
96
97
|
test "should silently rescue exceptions in the process_failure call" do
|
97
98
|
mock = mock('failure handler')
|
98
|
-
mock.expects(:call).with('message', 1).raises(
|
99
|
+
mock.expects(:call).with('message', 1).raises(Exception)
|
99
100
|
handler = Handler.create(lambda {}, :failback => mock)
|
100
101
|
handler.instance_variable_set(:@message, 'message')
|
101
102
|
assert_nothing_raised {handler.process_failure(1)}
|
102
103
|
end
|
103
104
|
|
105
|
+
test "should silently rescue exceptions in the processing_completed call" do
|
106
|
+
handler = Handler.create(lambda {})
|
107
|
+
handler.expects(:completed).raises(Exception)
|
108
|
+
assert_nothing_raised {handler.processing_completed}
|
109
|
+
end
|
110
|
+
|
104
111
|
end
|
105
112
|
end
|
@@ -274,7 +274,7 @@ module Beetle
|
|
274
274
|
@pub.expects(:set_current_server).with("b").in_sequence(s)
|
275
275
|
@pub.expects(:queue).with("queue").returns(queue).in_sequence(s)
|
276
276
|
queue.expects(:purge).in_sequence(s)
|
277
|
-
@pub.purge("queue")
|
277
|
+
@pub.purge(["queue"])
|
278
278
|
end
|
279
279
|
end
|
280
280
|
|
@@ -27,7 +27,7 @@ module Beetle
|
|
27
27
|
m = mock("dummy")
|
28
28
|
expected_amqp_options = {
|
29
29
|
:host => @sub.send(:current_host), :port => @sub.send(:current_port),
|
30
|
-
:user => "guest", :pass => "guest", :vhost => "/"
|
30
|
+
:user => "guest", :pass => "guest", :vhost => "/", :logging => false
|
31
31
|
}
|
32
32
|
AMQP.expects(:connect).with(expected_amqp_options).returns(m)
|
33
33
|
# TODO: smarter way to test? what triggers the amqp_connection private method call?
|
@@ -56,6 +56,75 @@ module Beetle
|
|
56
56
|
|
57
57
|
end
|
58
58
|
|
59
|
+
class SubscriberPauseAndResumeTest < Test::Unit::TestCase
|
60
|
+
def setup
|
61
|
+
@client = Client.new
|
62
|
+
@sub = @client.send(:subscriber)
|
63
|
+
@sub.servers << "localhost:7777"
|
64
|
+
@server1, @server2 = @sub.servers
|
65
|
+
@client.register_message(:a)
|
66
|
+
@client.register_queue(:a)
|
67
|
+
@client.register_handler(%W(a)){}
|
68
|
+
end
|
69
|
+
|
70
|
+
test "should pause on all servers when a handler has been registered" do
|
71
|
+
@sub.expects(:pause).with("a").times(2)
|
72
|
+
@sub.stubs(:has_subscription?).returns(true)
|
73
|
+
@sub.pause_listening(%w(a))
|
74
|
+
end
|
75
|
+
|
76
|
+
test "should resume on all servers when a handler has been registered" do
|
77
|
+
@sub.expects(:resume).with("a").times(2)
|
78
|
+
@sub.stubs(:has_subscription?).returns(true)
|
79
|
+
@sub.resume_listening(%w(a))
|
80
|
+
end
|
81
|
+
|
82
|
+
test "should pause on no servers when no handler has been registered" do
|
83
|
+
@sub.expects(:pause).never
|
84
|
+
@sub.stubs(:has_subscription?).returns(false)
|
85
|
+
@sub.pause_listening(%w(a))
|
86
|
+
end
|
87
|
+
|
88
|
+
test "should resume on no servers when no handler has been registered" do
|
89
|
+
@sub.expects(:resume).never
|
90
|
+
@sub.stubs(:has_subscription?).returns(false)
|
91
|
+
@sub.resume_listening(%w(a))
|
92
|
+
end
|
93
|
+
|
94
|
+
test "pausing a single queue should call amqp unsubscribe" do
|
95
|
+
q = mock("queue a")
|
96
|
+
q.expects(:subscribed?).returns(true)
|
97
|
+
q.expects(:unsubscribe)
|
98
|
+
@sub.stubs(:queues).returns({"a" =>q})
|
99
|
+
@sub.__send__(:pause, "a")
|
100
|
+
end
|
101
|
+
|
102
|
+
test "pausing a single queue which is already paused should not call amqp unsubscribe" do
|
103
|
+
q = mock("queue a")
|
104
|
+
q.expects(:subscribed?).returns(false)
|
105
|
+
q.expects(:unsubscribe).never
|
106
|
+
@sub.stubs(:queues).returns({"a" =>q})
|
107
|
+
@sub.__send__(:pause, "a")
|
108
|
+
end
|
109
|
+
|
110
|
+
test "resuming a single queue should call amqp subscribe" do
|
111
|
+
q = mock("queue a")
|
112
|
+
q.expects(:subscribed?).returns(false)
|
113
|
+
q.expects(:subscribe)
|
114
|
+
@sub.stubs(:queues).returns({"a" =>q})
|
115
|
+
@sub.__send__(:resume, "a")
|
116
|
+
end
|
117
|
+
|
118
|
+
test "resuming a single queue which is already subscribed should not call amqp subscribe" do
|
119
|
+
q = mock("queue a")
|
120
|
+
q.expects(:subscribed?).returns(true)
|
121
|
+
q.expects(:subscribe).never
|
122
|
+
@sub.stubs(:queues).returns({"a" =>q})
|
123
|
+
@sub.__send__(:resume, "a")
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
59
128
|
class AdditionalSubscriptionServersTest < Test::Unit::TestCase
|
60
129
|
def setup
|
61
130
|
@config = Configuration.new
|
@@ -160,6 +229,8 @@ module Beetle
|
|
160
229
|
@sub = client.send(:subscriber)
|
161
230
|
@exception = Exception.new "murks"
|
162
231
|
@handler = Handler.create(lambda{|*args| raise @exception})
|
232
|
+
# handler method 'processing_completed' should be called under all circumstances
|
233
|
+
@handler.expects(:processing_completed).once
|
163
234
|
@callback = @sub.send(:create_subscription_callback, "my myessage", @queue, @handler, :exceptions => 1)
|
164
235
|
end
|
165
236
|
|
@@ -225,7 +296,7 @@ module Beetle
|
|
225
296
|
@sub.send(:subscribe_queues, %W(a b))
|
226
297
|
end
|
227
298
|
|
228
|
-
test "subscribe should subscribe with a subscription callback created from the registered block" do
|
299
|
+
test "subscribe should subscribe with a subscription callback created from the registered block and remember the subscription" do
|
229
300
|
@client.register_queue(:some_queue, :exchange => "some_exchange", :key => "some_key")
|
230
301
|
server = @sub.server
|
231
302
|
header = header_with_params({})
|
@@ -239,24 +310,30 @@ module Beetle
|
|
239
310
|
end
|
240
311
|
@sub.register_handler("some_queue", &proc)
|
241
312
|
q = mock("QUEUE")
|
242
|
-
|
243
|
-
|
313
|
+
subscription_options = {:ack => true, :key => "#"}
|
314
|
+
q.expects(:subscribe).with(subscription_options).yields(header, "foo")
|
315
|
+
@sub.expects(:queues).returns({"some_queue" => q}).twice
|
244
316
|
@sub.send(:subscribe, "some_queue")
|
245
317
|
assert block_called
|
318
|
+
assert @sub.__send__(:has_subscription?, "some_queue")
|
319
|
+
q.expects(:subscribe).with(subscription_options).raises(MQ::Error)
|
320
|
+
assert_raises(Error) { @sub.send(:subscribe, "some_queue") }
|
246
321
|
end
|
247
322
|
|
248
323
|
test "subscribe should fail if no handler exists for given message" do
|
249
324
|
assert_raises(Error){ @sub.send(:subscribe, "some_queue") }
|
250
325
|
end
|
251
326
|
|
252
|
-
test "listening should use eventmachine. create exchanges. bind queues. install subscribers. and yield." do
|
253
|
-
@client.
|
254
|
-
@client.
|
327
|
+
test "listening on queues should use eventmachine. create exchanges. bind queues. install subscribers. and yield." do
|
328
|
+
@client.register_exchange(:an_exchange)
|
329
|
+
@client.register_queue(:a_queue, :exchange => :an_exchange)
|
330
|
+
@client.register_message(:a_message, :key => "foo", :exchange => :an_exchange)
|
331
|
+
|
255
332
|
EM.expects(:run).yields
|
256
|
-
@sub.expects(:create_exchanges).with(["
|
257
|
-
@sub.expects(:bind_queues).with(["
|
258
|
-
@sub.expects(:subscribe_queues).with(["
|
259
|
-
@sub.
|
333
|
+
@sub.expects(:create_exchanges).with(["an_exchange"])
|
334
|
+
@sub.expects(:bind_queues).with(["a_queue"])
|
335
|
+
@sub.expects(:subscribe_queues).with(["a_queue"])
|
336
|
+
@sub.listen_queues(["a_queue"]) {}
|
260
337
|
end
|
261
338
|
end
|
262
339
|
|
data/test/test_helper.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'active_support'
|
3
|
-
require 'active_support/testing/declarative'
|
4
2
|
require 'test/unit'
|
3
|
+
require 'mocha'
|
4
|
+
require 'active_support/testing/declarative'
|
5
|
+
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/beetle')
|
7
|
+
|
5
8
|
begin
|
6
9
|
require 'redgreen' unless ENV['TM_FILENAME']
|
7
|
-
rescue
|
10
|
+
rescue LoadError => e
|
8
11
|
end
|
9
12
|
|
10
13
|
# we can remove this hack which is needed only for testing
|
@@ -17,8 +20,6 @@ rescue LoadError
|
|
17
20
|
end
|
18
21
|
end
|
19
22
|
|
20
|
-
require 'mocha'
|
21
|
-
require File.expand_path(File.dirname(__FILE__) + '/../lib/beetle')
|
22
23
|
|
23
24
|
class Test::Unit::TestCase
|
24
25
|
extend ActiveSupport::Testing::Declarative
|
metadata
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beetle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 15424039
|
5
|
+
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
- rc
|
11
|
+
- 1
|
12
|
+
version: 0.3.0.rc.1
|
11
13
|
platform: ruby
|
12
14
|
authors:
|
13
15
|
- Stefan Kaes
|
@@ -18,7 +20,7 @@ autorequire:
|
|
18
20
|
bindir: bin
|
19
21
|
cert_chain: []
|
20
22
|
|
21
|
-
date: 2011-
|
23
|
+
date: 2011-08-27 00:00:00 +02:00
|
22
24
|
default_executable: beetle
|
23
25
|
dependencies:
|
24
26
|
- !ruby/object:Gem::Dependency
|
@@ -29,12 +31,12 @@ dependencies:
|
|
29
31
|
requirements:
|
30
32
|
- - ">="
|
31
33
|
- !ruby/object:Gem::Version
|
32
|
-
hash:
|
34
|
+
hash: 31
|
33
35
|
segments:
|
34
36
|
- 0
|
35
37
|
- 1
|
36
|
-
-
|
37
|
-
version: 0.1.
|
38
|
+
- 2
|
39
|
+
version: 0.1.2
|
38
40
|
type: :runtime
|
39
41
|
version_requirements: *id001
|
40
42
|
- !ruby/object:Gem::Dependency
|
@@ -43,36 +45,20 @@ dependencies:
|
|
43
45
|
requirement: &id002 !ruby/object:Gem::Requirement
|
44
46
|
none: false
|
45
47
|
requirements:
|
46
|
-
- -
|
48
|
+
- - ~>
|
47
49
|
- !ruby/object:Gem::Version
|
48
|
-
hash:
|
50
|
+
hash: 1
|
49
51
|
segments:
|
50
52
|
- 0
|
51
|
-
-
|
52
|
-
-
|
53
|
-
version: 0.
|
53
|
+
- 7
|
54
|
+
- 1
|
55
|
+
version: 0.7.1
|
54
56
|
type: :runtime
|
55
57
|
version_requirements: *id002
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: bunny-ext
|
58
|
-
prerelease: false
|
59
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
60
|
-
none: false
|
61
|
-
requirements:
|
62
|
-
- - ">="
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
hash: 13
|
65
|
-
segments:
|
66
|
-
- 0
|
67
|
-
- 6
|
68
|
-
- 5
|
69
|
-
version: 0.6.5
|
70
|
-
type: :runtime
|
71
|
-
version_requirements: *id003
|
72
58
|
- !ruby/object:Gem::Dependency
|
73
59
|
name: redis
|
74
60
|
prerelease: false
|
75
|
-
requirement: &
|
61
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
76
62
|
none: false
|
77
63
|
requirements:
|
78
64
|
- - "="
|
@@ -84,14 +70,14 @@ dependencies:
|
|
84
70
|
- 4
|
85
71
|
version: 2.0.4
|
86
72
|
type: :runtime
|
87
|
-
version_requirements: *
|
73
|
+
version_requirements: *id003
|
88
74
|
- !ruby/object:Gem::Dependency
|
89
75
|
name: amqp
|
90
76
|
prerelease: false
|
91
|
-
requirement: &
|
77
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
92
78
|
none: false
|
93
79
|
requirements:
|
94
|
-
- -
|
80
|
+
- - ~>
|
95
81
|
- !ruby/object:Gem::Version
|
96
82
|
hash: 9
|
97
83
|
segments:
|
@@ -100,14 +86,14 @@ dependencies:
|
|
100
86
|
- 7
|
101
87
|
version: 0.6.7
|
102
88
|
type: :runtime
|
103
|
-
version_requirements: *
|
89
|
+
version_requirements: *id004
|
104
90
|
- !ruby/object:Gem::Dependency
|
105
91
|
name: activesupport
|
106
92
|
prerelease: false
|
107
|
-
requirement: &
|
93
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
108
94
|
none: false
|
109
95
|
requirements:
|
110
|
-
- -
|
96
|
+
- - ">="
|
111
97
|
- !ruby/object:Gem::Version
|
112
98
|
hash: 11
|
113
99
|
segments:
|
@@ -116,11 +102,11 @@ dependencies:
|
|
116
102
|
- 4
|
117
103
|
version: 2.3.4
|
118
104
|
type: :runtime
|
119
|
-
version_requirements: *
|
105
|
+
version_requirements: *id005
|
120
106
|
- !ruby/object:Gem::Dependency
|
121
107
|
name: daemons
|
122
108
|
prerelease: false
|
123
|
-
requirement: &
|
109
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
124
110
|
none: false
|
125
111
|
requirements:
|
126
112
|
- - ">="
|
@@ -132,6 +118,22 @@ dependencies:
|
|
132
118
|
- 10
|
133
119
|
version: 1.0.10
|
134
120
|
type: :runtime
|
121
|
+
version_requirements: *id006
|
122
|
+
- !ruby/object:Gem::Dependency
|
123
|
+
name: rake
|
124
|
+
prerelease: false
|
125
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
hash: 49
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
- 8
|
134
|
+
- 7
|
135
|
+
version: 0.8.7
|
136
|
+
type: :development
|
135
137
|
version_requirements: *id007
|
136
138
|
- !ruby/object:Gem::Dependency
|
137
139
|
name: mocha
|
@@ -162,9 +164,37 @@ dependencies:
|
|
162
164
|
type: :development
|
163
165
|
version_requirements: *id009
|
164
166
|
- !ruby/object:Gem::Dependency
|
165
|
-
name:
|
167
|
+
name: redgreen
|
166
168
|
prerelease: false
|
167
169
|
requirement: &id010 !ruby/object:Gem::Requirement
|
170
|
+
none: false
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
hash: 3
|
175
|
+
segments:
|
176
|
+
- 0
|
177
|
+
version: "0"
|
178
|
+
type: :development
|
179
|
+
version_requirements: *id010
|
180
|
+
- !ruby/object:Gem::Dependency
|
181
|
+
name: wirble
|
182
|
+
prerelease: false
|
183
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
184
|
+
none: false
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
hash: 3
|
189
|
+
segments:
|
190
|
+
- 0
|
191
|
+
version: "0"
|
192
|
+
type: :development
|
193
|
+
version_requirements: *id011
|
194
|
+
- !ruby/object:Gem::Dependency
|
195
|
+
name: cucumber
|
196
|
+
prerelease: false
|
197
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
168
198
|
none: false
|
169
199
|
requirements:
|
170
200
|
- - ">="
|
@@ -176,11 +206,11 @@ dependencies:
|
|
176
206
|
- 2
|
177
207
|
version: 0.7.2
|
178
208
|
type: :development
|
179
|
-
version_requirements: *
|
209
|
+
version_requirements: *id012
|
180
210
|
- !ruby/object:Gem::Dependency
|
181
211
|
name: daemon_controller
|
182
212
|
prerelease: false
|
183
|
-
requirement: &
|
213
|
+
requirement: &id013 !ruby/object:Gem::Requirement
|
184
214
|
none: false
|
185
215
|
requirements:
|
186
216
|
- - ">="
|
@@ -190,7 +220,7 @@ dependencies:
|
|
190
220
|
- 0
|
191
221
|
version: "0"
|
192
222
|
type: :development
|
193
|
-
version_requirements: *
|
223
|
+
version_requirements: *id013
|
194
224
|
description: A highly available, reliable messaging infrastructure
|
195
225
|
email: developers@xing.com
|
196
226
|
executables:
|
@@ -210,9 +240,11 @@ files:
|
|
210
240
|
- examples/handling_exceptions.rb
|
211
241
|
- examples/multiple_exchanges.rb
|
212
242
|
- examples/multiple_queues.rb
|
243
|
+
- examples/pause_and_resume.rb
|
213
244
|
- examples/redundant.rb
|
214
245
|
- examples/rpc.rb
|
215
246
|
- examples/simple.rb
|
247
|
+
- examples/test_publisher.rb
|
216
248
|
- lib/beetle/base.rb
|
217
249
|
- lib/beetle/client.rb
|
218
250
|
- lib/beetle/commands/configuration_client.rb
|
@@ -290,16 +322,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
290
322
|
requirements:
|
291
323
|
- - ">="
|
292
324
|
- !ruby/object:Gem::Version
|
293
|
-
hash:
|
325
|
+
hash: 21
|
294
326
|
segments:
|
295
327
|
- 1
|
296
328
|
- 3
|
297
|
-
-
|
298
|
-
version: 1.3.
|
329
|
+
- 7
|
330
|
+
version: 1.3.7
|
299
331
|
requirements: []
|
300
332
|
|
301
333
|
rubyforge_project:
|
302
|
-
rubygems_version: 1.
|
334
|
+
rubygems_version: 1.6.2
|
303
335
|
signing_key:
|
304
336
|
specification_version: 3
|
305
337
|
summary: High Availability AMQP Messaging with Redundant Queues
|