amqp 0.6.7 → 0.7.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/CHANGELOG +5 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +16 -0
- data/README.md +145 -0
- data/Rakefile +5 -0
- data/amqp.gemspec +31 -79
- data/amqp.pre.gemspec +6 -0
- data/bin/irb +9 -0
- data/examples/mq/callbacks.rb +32 -0
- data/examples/mq/clock.rb +15 -6
- data/lib/amqp/buffer.rb +5 -5
- data/lib/amqp/client.rb +25 -1
- data/lib/amqp/frame.rb +1 -1
- data/lib/amqp/protocol.rb +1 -1
- data/lib/amqp/server.rb +1 -1
- data/lib/amqp/version.rb +1 -1
- data/lib/mq.rb +113 -82
- data/lib/mq/collection.rb +132 -0
- data/lib/mq/exchange.rb +70 -36
- data/lib/mq/queue.rb +53 -30
- data/spec/channel_close_spec.rb +13 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/sync_async_spec.rb +52 -0
- metadata +53 -22
- data/README +0 -128
@@ -0,0 +1,132 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class MQ
|
4
|
+
class Collection < ::Array
|
5
|
+
class IncompatibleItemError < ArgumentError
|
6
|
+
def initialize(item)
|
7
|
+
super("Instance of #{item.class} doesn't respond to #name!")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](name)
|
12
|
+
self.find do |object|
|
13
|
+
object.name == name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Collection#[]= doesn't really make any sense, as we can't
|
18
|
+
# redefine already existing Queues and Exchanges (we can declare
|
19
|
+
# them multiple times, but if the queue resp. exchange is already
|
20
|
+
# in the collection, it doesn't make sense to do so and we can't
|
21
|
+
# run declare twice in order to change options, because the AMQP
|
22
|
+
# broker closes the connection if we try to do so).
|
23
|
+
|
24
|
+
# Use Collection#<< for adding items to the collection.
|
25
|
+
undef_method :[]=
|
26
|
+
|
27
|
+
def <<(item)
|
28
|
+
if (item.name rescue nil).nil? || ! self[item.name]
|
29
|
+
self.add!(item)
|
30
|
+
end
|
31
|
+
|
32
|
+
return item
|
33
|
+
end
|
34
|
+
|
35
|
+
alias_method :__push__, :push
|
36
|
+
alias_method :push, :<<
|
37
|
+
|
38
|
+
def add!(item)
|
39
|
+
unless item.respond_to?(:name)
|
40
|
+
raise IncompatibleItemError.new(item)
|
41
|
+
end
|
42
|
+
|
43
|
+
__push__(item)
|
44
|
+
return item
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if $0 =~ /bacon/ or $0 == __FILE__
|
50
|
+
require "bacon"
|
51
|
+
|
52
|
+
Item = Struct.new(:name)
|
53
|
+
|
54
|
+
describe MQ::Collection do
|
55
|
+
before do
|
56
|
+
@items = 3.times.map { |int| Item.new("name-#{int}") }
|
57
|
+
@collection = MQ::Collection.new(@items)
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "accessors" do
|
61
|
+
should "be accessible by its name" do
|
62
|
+
@collection["name-1"].should.not.be.nil
|
63
|
+
@collection["name-1"].should.eql(@items[1])
|
64
|
+
end
|
65
|
+
|
66
|
+
should "not allow to change already existing object" do
|
67
|
+
lambda { @collection["name-1"] = Item.new("test") }.should.raise(NoMethodError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#<<" do
|
72
|
+
should "raise IncompatibleItemError if the argument doesn't have method :name" do
|
73
|
+
lambda { @collection << nil }.should.raise(MQ::Collection::IncompatibleItemError)
|
74
|
+
end
|
75
|
+
|
76
|
+
should "add an item into the collection" do
|
77
|
+
length = @collection.length
|
78
|
+
@collection << Item.new("test")
|
79
|
+
@collection.length.should.eql(length + 1)
|
80
|
+
end
|
81
|
+
|
82
|
+
should "not add an item to the collection if another item with given name already exists and the name IS NOT nil" do
|
83
|
+
@collection << Item.new("test")
|
84
|
+
length = @collection.length
|
85
|
+
@collection << Item.new("test")
|
86
|
+
@collection.length.should.eql(length)
|
87
|
+
end
|
88
|
+
|
89
|
+
should "add an item to the collection if another item with given name already exists and the name IS nil" do
|
90
|
+
@collection << Item.new(nil)
|
91
|
+
length = @collection.length
|
92
|
+
@collection << Item.new(nil)
|
93
|
+
@collection.length.should.eql(length + 1)
|
94
|
+
end
|
95
|
+
|
96
|
+
should "return the item" do
|
97
|
+
item = Item.new("test")
|
98
|
+
(@collection << item).should.eql item
|
99
|
+
end
|
100
|
+
|
101
|
+
should "return the item even if it already existed" do
|
102
|
+
item = Item.new("test")
|
103
|
+
@collection << item
|
104
|
+
(@collection << item).should.eql item
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#add!" do
|
109
|
+
should "raise IncompatibleItemError if the argument doesn't have method :name" do
|
110
|
+
lambda { @collection << nil }.should.raise(MQ::Collection::IncompatibleItemError)
|
111
|
+
end
|
112
|
+
|
113
|
+
should "add an item into the collection" do
|
114
|
+
length = @collection.length
|
115
|
+
@collection << Item.new("test")
|
116
|
+
@collection.length.should.eql(length + 1)
|
117
|
+
end
|
118
|
+
|
119
|
+
should "add an item to the collection if another item with given name already exists" do
|
120
|
+
@collection.add! Item.new("test")
|
121
|
+
length = @collection.length
|
122
|
+
@collection.add! Item.new("test")
|
123
|
+
@collection.length.should.eql(length + 1)
|
124
|
+
end
|
125
|
+
|
126
|
+
should "return the item" do
|
127
|
+
item = Item.new("test")
|
128
|
+
(@collection << item).should.eql item
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/mq/exchange.rb
CHANGED
@@ -10,7 +10,7 @@ class MQ
|
|
10
10
|
# There are three (3) supported Exchange types: direct, fanout and topic.
|
11
11
|
#
|
12
12
|
# As part of the standard, the server _must_ predeclare the direct exchange
|
13
|
-
# 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
|
13
|
+
# 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
|
14
14
|
# starting with 'amq.' are reserved). Attempts to declare an exchange using
|
15
15
|
# 'amq.' as the name will raise an MQ:Error and fail. In practice these
|
16
16
|
# default exchanges are never used directly by client code.
|
@@ -28,7 +28,7 @@ class MQ
|
|
28
28
|
# There are three (3) supported Exchange types: direct, fanout and topic.
|
29
29
|
#
|
30
30
|
# As part of the standard, the server _must_ predeclare the direct exchange
|
31
|
-
# 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
|
31
|
+
# 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
|
32
32
|
# starting with 'amq.' are reserved). Attempts to declare an exchange using
|
33
33
|
# 'amq.' as the name will raise an MQ:Error and fail. In practice these
|
34
34
|
# default exchanges are never used directly by client code.
|
@@ -36,8 +36,8 @@ class MQ
|
|
36
36
|
# == Direct
|
37
37
|
# A direct exchange is useful for 1:1 communication between a publisher and
|
38
38
|
# subscriber. Messages are routed to the queue with a binding that shares
|
39
|
-
# the same name as the exchange. Alternately, the messages are routed to
|
40
|
-
# the bound queue that shares the same name as the routing key used for
|
39
|
+
# the same name as the exchange. Alternately, the messages are routed to
|
40
|
+
# the bound queue that shares the same name as the routing key used for
|
41
41
|
# defining the exchange. This exchange type does not honor the :key option
|
42
42
|
# when defining a new instance with a name. It _will_ honor the :key option
|
43
43
|
# if the exchange name is the empty string. This is because an exchange
|
@@ -57,14 +57,14 @@ class MQ
|
|
57
57
|
# queue.pop { |data| puts "received data [#{data}]" }
|
58
58
|
#
|
59
59
|
# == Fanout
|
60
|
-
# A fanout exchange is useful for 1:N communication where one publisher
|
61
|
-
# feeds multiple subscribers. Like direct exchanges, messages published
|
62
|
-
# to a fanout exchange are delivered to queues whose name matches the
|
63
|
-
# exchange name (or are bound to that exchange name). Each queue gets
|
60
|
+
# A fanout exchange is useful for 1:N communication where one publisher
|
61
|
+
# feeds multiple subscribers. Like direct exchanges, messages published
|
62
|
+
# to a fanout exchange are delivered to queues whose name matches the
|
63
|
+
# exchange name (or are bound to that exchange name). Each queue gets
|
64
64
|
# its own copy of the message.
|
65
65
|
#
|
66
|
-
# Like the direct exchange type, this exchange type does not honor the
|
67
|
-
# :key option when defining a new instance with a name. It _will_ honor
|
66
|
+
# Like the direct exchange type, this exchange type does not honor the
|
67
|
+
# :key option when defining a new instance with a name. It _will_ honor
|
68
68
|
# the :key option if the exchange name is the empty string. Fanout exchanges
|
69
69
|
# defined with the empty string as the name use the default 'amq.fanout'.
|
70
70
|
# In this case it needs to use :key to do its matching.
|
@@ -91,20 +91,20 @@ class MQ
|
|
91
91
|
# end
|
92
92
|
#
|
93
93
|
# == Topic
|
94
|
-
# A topic exchange allows for messages to be published to an exchange
|
94
|
+
# A topic exchange allows for messages to be published to an exchange
|
95
95
|
# tagged with a specific routing key. The Exchange uses the routing key
|
96
|
-
# to determine which queues to deliver the message. Wildcard matching
|
97
|
-
# is allowed. The topic must be declared using dot notation to separate
|
96
|
+
# to determine which queues to deliver the message. Wildcard matching
|
97
|
+
# is allowed. The topic must be declared using dot notation to separate
|
98
98
|
# each subtopic.
|
99
99
|
#
|
100
100
|
# This is the only exchange type to honor the :key parameter.
|
101
101
|
#
|
102
|
-
# As part of the AMQP standard, each server _should_ predeclare a topic
|
102
|
+
# As part of the AMQP standard, each server _should_ predeclare a topic
|
103
103
|
# exchange called 'amq.topic' (this is not required by the standard).
|
104
104
|
#
|
105
105
|
# The classic example is delivering market data. When publishing market
|
106
|
-
# data for stocks, we may subdivide the stream based on 2
|
107
|
-
# characteristics: nation code and trading symbol. The topic tree for
|
106
|
+
# data for stocks, we may subdivide the stream based on 2
|
107
|
+
# characteristics: nation code and trading symbol. The topic tree for
|
108
108
|
# Apple Computer would look like:
|
109
109
|
# 'stock.us.aapl'
|
110
110
|
# For a foreign stock, it may look like:
|
@@ -139,10 +139,10 @@ class MQ
|
|
139
139
|
# end
|
140
140
|
# end
|
141
141
|
#
|
142
|
-
# For matching, the '*' (asterisk) wildcard matches against one
|
143
|
-
# dot-separated item only. The '#' wildcard (hash or pound symbol)
|
144
|
-
# matches against 0 or more dot-separated items. If none of these
|
145
|
-
# symbols are used, the exchange performs a comparison looking for an
|
142
|
+
# For matching, the '*' (asterisk) wildcard matches against one
|
143
|
+
# dot-separated item only. The '#' wildcard (hash or pound symbol)
|
144
|
+
# matches against 0 or more dot-separated items. If none of these
|
145
|
+
# symbols are used, the exchange performs a comparison looking for an
|
146
146
|
# exact match.
|
147
147
|
#
|
148
148
|
# == Options
|
@@ -150,12 +150,12 @@ class MQ
|
|
150
150
|
# If set, the server will not create the exchange if it does not
|
151
151
|
# already exist. The client can use this to check whether an exchange
|
152
152
|
# exists without modifying the server state.
|
153
|
-
#
|
153
|
+
#
|
154
154
|
# * :durable => true | false (default false)
|
155
155
|
# If set when creating a new exchange, the exchange will be marked as
|
156
156
|
# durable. Durable exchanges remain active when a server restarts.
|
157
157
|
# Non-durable exchanges (transient exchanges) are purged if/when a
|
158
|
-
# server restarts.
|
158
|
+
# server restarts.
|
159
159
|
#
|
160
160
|
# A transient exchange (the default) is stored in memory-only
|
161
161
|
# therefore it is a good choice for high-performance and low-latency
|
@@ -183,26 +183,56 @@ class MQ
|
|
183
183
|
# not wait for a reply method. If the server could not complete the
|
184
184
|
# method it will raise a channel or connection exception.
|
185
185
|
#
|
186
|
+
# * :no_declare => true | false (default false)
|
187
|
+
# If set, the exchange will not be declared to the
|
188
|
+
# AMQP broker at instantiation-time. This allows the AMQP
|
189
|
+
# client to send messages to exchanges that were
|
190
|
+
# already declared by someone else, e.g. if the client
|
191
|
+
# does not have sufficient privilege to declare (create)
|
192
|
+
# an exchange. Use with caution, as binding to an exchange
|
193
|
+
# with the no-declare option causes your system to become
|
194
|
+
# sensitive to the ordering of clients' actions!
|
195
|
+
#
|
186
196
|
# == Exceptions
|
187
197
|
# Doing any of these activities are illegal and will raise MQ:Error.
|
188
198
|
# * redeclare an already-declared exchange to a different type
|
189
199
|
# * :passive => true and the exchange does not exist (NOT_FOUND)
|
190
200
|
#
|
191
|
-
def initialize mq, type, name, opts = {}
|
201
|
+
def initialize mq, type, name, opts = {}, &block
|
192
202
|
@mq = mq
|
193
|
-
@type, @
|
194
|
-
@
|
203
|
+
@type, @opts = type, opts
|
204
|
+
@opts = { :exchange => name, :type => type, :nowait => block.nil? }.merge(opts)
|
195
205
|
@key = opts[:key]
|
196
|
-
|
206
|
+
@name = name unless name.empty?
|
207
|
+
@status = :unknown
|
208
|
+
|
209
|
+
# The AMQP 0.8 specification (as well as 0.9.1) in 1.1.4.2 mentiones
|
210
|
+
# that Exchange.Declare-Ok confirms the name of the exchange (because
|
211
|
+
# of automaticallynamed), which is logical to interpret that this
|
212
|
+
# functionality should be the same as for Queue (though it isn't
|
213
|
+
# explicitely told in the specification). In fact, RabbitMQ (and
|
214
|
+
# probably other implementations as well) doesn't support it and
|
215
|
+
# there is a default exchange with an empty name (so-called default
|
216
|
+
# or nameless exchange), so if we'd send Exchange.Declare(exchange=""),
|
217
|
+
# then RabbitMQ interpret it as if we'd try to redefine this default
|
218
|
+
# exchange so it'd produce an error.
|
197
219
|
unless name == "amq.#{type}" or name == '' or opts[:no_declare]
|
198
|
-
@
|
199
|
-
|
200
|
-
|
201
|
-
:nowait => true }.merge(opts))
|
220
|
+
@status = :unfinished
|
221
|
+
@mq.callback {
|
222
|
+
@mq.send Protocol::Exchange::Declare.new(@opts)
|
202
223
|
}
|
224
|
+
else
|
225
|
+
# Call the callback immediately, as given exchange is already
|
226
|
+
# declared.
|
227
|
+
@status = :finished
|
228
|
+
block.call(self)
|
203
229
|
end
|
230
|
+
|
231
|
+
self.callback = block
|
204
232
|
end
|
205
|
-
|
233
|
+
|
234
|
+
attr_reader :name, :type, :key, :status
|
235
|
+
attr_accessor :opts, :callback
|
206
236
|
|
207
237
|
# This method publishes a staged file message to a specific exchange.
|
208
238
|
# The file message will be routed to queues as defined by the exchange
|
@@ -212,7 +242,7 @@ class MQ
|
|
212
242
|
# exchange = MQ.direct('name', :key => 'foo.bar')
|
213
243
|
# exchange.publish("some data")
|
214
244
|
#
|
215
|
-
# The method takes several hash key options which modify the behavior or
|
245
|
+
# The method takes several hash key options which modify the behavior or
|
216
246
|
# lifecycle of the message.
|
217
247
|
#
|
218
248
|
# * :routing_key => 'string'
|
@@ -236,7 +266,7 @@ class MQ
|
|
236
266
|
# no guarantee that it will ever be consumed.
|
237
267
|
#
|
238
268
|
# * :persistent
|
239
|
-
# True or False. When true, this message will remain in the queue until
|
269
|
+
# True or False. When true, this message will remain in the queue until
|
240
270
|
# it is consumed (if the queue is durable). When false, the message is
|
241
271
|
# lost if the server restarts and the queue is recreated.
|
242
272
|
#
|
@@ -254,7 +284,7 @@ class MQ
|
|
254
284
|
data = data.to_s
|
255
285
|
|
256
286
|
out << Protocol::Header.new(Protocol::Basic,
|
257
|
-
data.
|
287
|
+
data.bytesize, { :content_type => 'application/octet-stream',
|
258
288
|
:delivery_mode => (opts[:persistent] ? 2 : 1),
|
259
289
|
:priority => 0 }.merge(opts))
|
260
290
|
|
@@ -286,7 +316,7 @@ class MQ
|
|
286
316
|
# If set, the server will only delete the exchange if it has no queue
|
287
317
|
# bindings. If the exchange has queue bindings the server does not
|
288
318
|
# delete it but raises a channel exception instead (MQ:Error).
|
289
|
-
#
|
319
|
+
#
|
290
320
|
def delete opts = {}
|
291
321
|
@mq.callback{
|
292
322
|
@mq.send Protocol::Exchange::Delete.new({ :exchange => name,
|
@@ -300,5 +330,9 @@ class MQ
|
|
300
330
|
@deferred_status = nil
|
301
331
|
initialize @mq, @type, @name, @opts
|
302
332
|
end
|
333
|
+
|
334
|
+
def receive_response response
|
335
|
+
self.callback && self.callback.call(self)
|
336
|
+
end
|
303
337
|
end
|
304
|
-
end
|
338
|
+
end
|
data/lib/mq/queue.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class MQ
|
2
2
|
class Queue
|
3
3
|
include AMQP
|
4
|
-
|
4
|
+
|
5
5
|
# Queues store and forward messages. Queues can be configured in the server
|
6
6
|
# or created at runtime. Queues must be attached to at least one exchange
|
7
7
|
# in order to receive messages from publishers.
|
@@ -10,7 +10,7 @@ class MQ
|
|
10
10
|
# internal use. Attempts to create queue names in violation of this
|
11
11
|
# reservation will raise MQ:Error (ACCESS_REFUSED).
|
12
12
|
#
|
13
|
-
# When a queue is created without a name, the server will generate a
|
13
|
+
# When a queue is created without a name, the server will generate a
|
14
14
|
# unique name internally (not currently supported in this library).
|
15
15
|
#
|
16
16
|
# == Options
|
@@ -18,7 +18,7 @@ class MQ
|
|
18
18
|
# If set, the server will not create the exchange if it does not
|
19
19
|
# already exist. The client can use this to check whether an exchange
|
20
20
|
# exists without modifying the server state.
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# * :durable => true | false (default false)
|
23
23
|
# If set when creating a new queue, the queue will be marked as
|
24
24
|
# durable. Durable queues remain active when a server restarts.
|
@@ -47,7 +47,7 @@ class MQ
|
|
47
47
|
# If set, the queue is deleted when all consumers have finished
|
48
48
|
# using it. Last consumer can be cancelled either explicitly or because
|
49
49
|
# its channel is closed. If there was no consumer ever on the queue, it
|
50
|
-
# won't be deleted.
|
50
|
+
# won't be deleted.
|
51
51
|
#
|
52
52
|
# The server waits for a short period of time before
|
53
53
|
# determining the queue is unused to give time to the client code
|
@@ -61,17 +61,21 @@ class MQ
|
|
61
61
|
# not wait for a reply method. If the server could not complete the
|
62
62
|
# method it will raise a channel or connection exception.
|
63
63
|
#
|
64
|
-
def initialize mq, name, opts = {}
|
64
|
+
def initialize mq, name, opts = {}, &block
|
65
65
|
@mq = mq
|
66
|
-
@opts = opts
|
66
|
+
@opts = { :queue => name, :nowait => block.nil? }.merge(opts)
|
67
67
|
@bindings ||= {}
|
68
|
-
@
|
69
|
-
@
|
70
|
-
|
71
|
-
|
68
|
+
@name = name unless name.empty?
|
69
|
+
@status = @opts[:nowait] ? :unknown : :unfinished
|
70
|
+
@mq.callback {
|
71
|
+
@mq.send Protocol::Queue::Declare.new(@opts)
|
72
72
|
}
|
73
|
+
|
74
|
+
self.callback = block
|
73
75
|
end
|
76
|
+
|
74
77
|
attr_reader :name
|
78
|
+
attr_accessor :opts, :callback, :bind_callback
|
75
79
|
|
76
80
|
# This method binds a queue to an exchange. Until a queue is
|
77
81
|
# bound it will not receive any messages. In a classic messaging
|
@@ -106,7 +110,8 @@ class MQ
|
|
106
110
|
# not wait for a reply method. If the server could not complete the
|
107
111
|
# method it will raise a channel or connection exception.
|
108
112
|
#
|
109
|
-
def bind exchange, opts = {}
|
113
|
+
def bind exchange, opts = {}, &block
|
114
|
+
@status = :unbound
|
110
115
|
exchange = exchange.respond_to?(:name) ? exchange.name : exchange
|
111
116
|
@bindings[exchange] = opts
|
112
117
|
|
@@ -114,13 +119,14 @@ class MQ
|
|
114
119
|
@mq.send Protocol::Queue::Bind.new({ :queue => name,
|
115
120
|
:exchange => exchange,
|
116
121
|
:routing_key => opts[:key],
|
117
|
-
:nowait =>
|
122
|
+
:nowait => block.nil? }.merge(opts))
|
118
123
|
}
|
124
|
+
self.bind_callback = block
|
119
125
|
self
|
120
126
|
end
|
121
127
|
|
122
128
|
# Remove the binding between the queue and exchange. The queue will
|
123
|
-
# not receive any more messages until it is bound to another
|
129
|
+
# not receive any more messages until it is bound to another
|
124
130
|
# exchange.
|
125
131
|
#
|
126
132
|
# Due to the asynchronous nature of the protocol, it is possible for
|
@@ -197,7 +203,7 @@ class MQ
|
|
197
203
|
# EM.add_periodic_timer(1) do
|
198
204
|
# exchange.publish("random number #{rand(1000)}")
|
199
205
|
# end
|
200
|
-
#
|
206
|
+
#
|
201
207
|
# # note that #bind is never called; it is implicit because
|
202
208
|
# # the exchange and queue names match
|
203
209
|
# queue = MQ.queue('foo queue')
|
@@ -215,9 +221,9 @@ class MQ
|
|
215
221
|
# EM.add_periodic_timer(1) do
|
216
222
|
# exchange.publish("random number #{rand(1000)}")
|
217
223
|
# end
|
218
|
-
#
|
224
|
+
#
|
219
225
|
# queue = MQ.queue('foo queue')
|
220
|
-
# queue.pop do |header, body|
|
226
|
+
# queue.pop do |header, body|
|
221
227
|
# p header
|
222
228
|
# puts "received payload [#{body}]"
|
223
229
|
# end
|
@@ -268,7 +274,7 @@ class MQ
|
|
268
274
|
# EM.add_periodic_timer(1) do
|
269
275
|
# exchange.publish("random number #{rand(1000)}")
|
270
276
|
# end
|
271
|
-
#
|
277
|
+
#
|
272
278
|
# queue = MQ.queue('foo queue')
|
273
279
|
# queue.subscribe { |body| puts "received payload [#{body}]" }
|
274
280
|
# end
|
@@ -282,11 +288,11 @@ class MQ
|
|
282
288
|
# EM.add_periodic_timer(1) do
|
283
289
|
# exchange.publish("random number #{rand(1000)}")
|
284
290
|
# end
|
285
|
-
#
|
291
|
+
#
|
286
292
|
# # note that #bind is never called; it is implicit because
|
287
293
|
# # the exchange and queue names match
|
288
294
|
# queue = MQ.queue('foo queue')
|
289
|
-
# queue.subscribe do |header, body|
|
295
|
+
# queue.subscribe do |header, body|
|
290
296
|
# p header
|
291
297
|
# puts "received payload [#{body}]"
|
292
298
|
# end
|
@@ -340,11 +346,11 @@ class MQ
|
|
340
346
|
# Those messages will be serviced by the last block used in a
|
341
347
|
# #subscribe or #pop call.
|
342
348
|
#
|
343
|
-
# Additionally, if the queue was created with _autodelete_ set to
|
349
|
+
# Additionally, if the queue was created with _autodelete_ set to
|
344
350
|
# true, the server will delete the queue after its wait period
|
345
351
|
# has expired unless the queue is bound to an active exchange.
|
346
352
|
#
|
347
|
-
# The method accepts a block which will be executed when the
|
353
|
+
# The method accepts a block which will be executed when the
|
348
354
|
# unsubscription request is acknowledged as complete by the server.
|
349
355
|
#
|
350
356
|
# * :nowait => true | false (default true)
|
@@ -363,21 +369,21 @@ class MQ
|
|
363
369
|
def publish data, opts = {}
|
364
370
|
exchange.publish(data, opts)
|
365
371
|
end
|
366
|
-
|
372
|
+
|
367
373
|
# Boolean check to see if the current queue has already been subscribed
|
368
|
-
# to an exchange.
|
374
|
+
# to an exchange.
|
369
375
|
#
|
370
376
|
# Attempts to #subscribe multiple times to any exchange will raise an
|
371
|
-
# Exception. Only a single block at a time can be associated with any
|
377
|
+
# Exception. Only a single block at a time can be associated with any
|
372
378
|
# one queue for processing incoming messages.
|
373
379
|
#
|
374
380
|
def subscribed?
|
375
381
|
!!@on_msg
|
376
382
|
end
|
377
383
|
|
378
|
-
# Passes the message to the block passed to pop or subscribe.
|
384
|
+
# Passes the message to the block passed to pop or subscribe.
|
379
385
|
#
|
380
|
-
# Performs an arity check on the block's parameters. If arity == 1,
|
386
|
+
# Performs an arity check on the block's parameters. If arity == 1,
|
381
387
|
# pass only the message body. If arity != 1, pass the headers and
|
382
388
|
# the body to the block.
|
383
389
|
#
|
@@ -385,7 +391,7 @@ class MQ
|
|
385
391
|
# the headers parameter. See #pop or #subscribe for a code example.
|
386
392
|
#
|
387
393
|
def receive headers, body
|
388
|
-
headers = MQ::Header.new(@mq, headers)
|
394
|
+
headers = MQ::Header.new(@mq, headers) unless headers.nil?
|
389
395
|
|
390
396
|
if cb = (@on_msg || @on_pop)
|
391
397
|
cb.call *(cb.arity == 1 ? [body] : [headers, body])
|
@@ -399,6 +405,8 @@ class MQ
|
|
399
405
|
# }
|
400
406
|
#
|
401
407
|
def status opts = {}, &blk
|
408
|
+
return @status if opts.empty? && blk.nil?
|
409
|
+
|
402
410
|
@on_status = blk
|
403
411
|
@mq.callback{
|
404
412
|
@mq.send Protocol::Queue::Declare.new({ :queue => name,
|
@@ -408,6 +416,13 @@ class MQ
|
|
408
416
|
end
|
409
417
|
|
410
418
|
def receive_status declare_ok
|
419
|
+
@name = declare_ok.queue
|
420
|
+
@status = :finished
|
421
|
+
|
422
|
+
if self.callback
|
423
|
+
self.callback.call(self, declare_ok.message_count, declare_ok.consumer_count)
|
424
|
+
end
|
425
|
+
|
411
426
|
if @on_status
|
412
427
|
m, c = declare_ok.message_count, declare_ok.consumer_count
|
413
428
|
@on_status.call *(@on_status.arity == 1 ? [m] : [m, c])
|
@@ -415,6 +430,13 @@ class MQ
|
|
415
430
|
end
|
416
431
|
end
|
417
432
|
|
433
|
+
def after_bind bind_ok
|
434
|
+
@status = :bound
|
435
|
+
if self.bind_callback
|
436
|
+
self.bind_callback.call(self)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
418
440
|
def confirm_subscribe
|
419
441
|
@on_confirm_subscribe.call if @on_confirm_subscribe
|
420
442
|
@on_confirm_subscribe = nil
|
@@ -424,6 +446,7 @@ class MQ
|
|
424
446
|
@on_cancel.call if @on_cancel
|
425
447
|
@on_cancel = @on_msg = nil
|
426
448
|
@mq.consumers.delete @consumer_tag
|
449
|
+
@mq.queues.delete(@name) if @opts[:auto_delete]
|
427
450
|
@consumer_tag = nil
|
428
451
|
end
|
429
452
|
|
@@ -444,11 +467,11 @@ class MQ
|
|
444
467
|
pop @on_pop_opts, &@on_pop
|
445
468
|
end
|
446
469
|
end
|
447
|
-
|
470
|
+
|
448
471
|
private
|
449
|
-
|
472
|
+
|
450
473
|
def exchange
|
451
474
|
@exchange ||= Exchange.new(@mq, :direct, '', :key => name)
|
452
475
|
end
|
453
476
|
end
|
454
|
-
end
|
477
|
+
end
|