amqp 0.6.7 → 0.7.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|