amq-client 0.7.0.alpha34 → 0.7.0.alpha35

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.
Files changed (32) hide show
  1. data/.travis.yml +4 -0
  2. data/Gemfile +1 -1
  3. data/README.textile +1 -1
  4. data/bin/ci/before_build.sh +24 -0
  5. data/examples/eventmachine_adapter/extensions/rabbitmq/handling_confirm_select_ok.rb +2 -2
  6. data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +1 -1
  7. data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_unroutable_message.rb +1 -1
  8. data/lib/amq/client.rb +29 -17
  9. data/lib/amq/client/adapter.rb +8 -504
  10. data/lib/amq/client/adapters/coolio.rb +4 -282
  11. data/lib/amq/client/adapters/event_machine.rb +4 -382
  12. data/lib/amq/client/async/adapter.rb +517 -0
  13. data/lib/amq/client/async/adapters/coolio.rb +291 -0
  14. data/lib/amq/client/async/adapters/event_machine.rb +392 -0
  15. data/lib/amq/client/async/adapters/eventmachine.rb +1 -0
  16. data/lib/amq/client/async/callbacks.rb +71 -0
  17. data/lib/amq/client/async/channel.rb +385 -0
  18. data/lib/amq/client/async/entity.rb +66 -0
  19. data/lib/amq/client/async/exchange.rb +157 -0
  20. data/lib/amq/client/async/extensions/rabbitmq/basic.rb +38 -0
  21. data/lib/amq/client/async/extensions/rabbitmq/confirm.rb +248 -0
  22. data/lib/amq/client/async/queue.rb +455 -0
  23. data/lib/amq/client/callbacks.rb +6 -65
  24. data/lib/amq/client/channel.rb +4 -376
  25. data/lib/amq/client/entity.rb +6 -57
  26. data/lib/amq/client/exchange.rb +4 -148
  27. data/lib/amq/client/extensions/rabbitmq/basic.rb +4 -28
  28. data/lib/amq/client/extensions/rabbitmq/confirm.rb +5 -240
  29. data/lib/amq/client/queue.rb +5 -450
  30. data/lib/amq/client/version.rb +1 -1
  31. data/spec/unit/client_spec.rb +10 -30
  32. metadata +16 -22
@@ -0,0 +1,455 @@
1
+ # encoding: utf-8
2
+
3
+ require "amq/client/async/entity"
4
+ require "amq/client/adapter"
5
+ require "amq/client/server_named_entity"
6
+ require "amq/protocol/get_response"
7
+
8
+ module AMQ
9
+ module Client
10
+ module Async
11
+ class Queue
12
+
13
+ #
14
+ # Behaviors
15
+ #
16
+
17
+ include Entity
18
+ include ServerNamedEntity
19
+ extend ProtocolMethodHandlers
20
+
21
+
22
+ #
23
+ # API
24
+ #
25
+
26
+ # Qeueue name. May be server-generated or assigned directly.
27
+ attr_reader :name
28
+
29
+ # Channel this queue belongs to.
30
+ attr_reader :channel
31
+
32
+ # Consumer tag identifies subscription for message delivery. It is nil for queues that are not subscribed for messages. See AMQ::Client::Queue#subscribe.
33
+ attr_reader :consumer_tag
34
+
35
+ # @param [AMQ::Client::Adapter] AMQ networking adapter to use.
36
+ # @param [AMQ::Client::Channel] AMQ channel this queue object uses.
37
+ # @param [String] Queue name. Please note that AMQP spec does not require brokers to support Unicode for queue names.
38
+ # @api public
39
+ def initialize(connection, channel, name = AMQ::Protocol::EMPTY_STRING)
40
+ raise ArgumentError.new("queue name must not be nil; if you want broker to generate queue name for you, pass an empty string") if name.nil?
41
+
42
+ super(connection)
43
+
44
+ @name = name
45
+ @channel = channel
46
+ end
47
+
48
+ def dup
49
+ if @name.empty?
50
+ raise RuntimeError.new("You can't clone anonymous queue until it receives server-generated name. Move the code with #dup to the callback for the #declare method.")
51
+ end
52
+
53
+ o = super
54
+ o.reset_consumer_tag!
55
+ o
56
+ end
57
+
58
+
59
+ # @return [Boolean] true if this queue was declared as durable (will survive broker restart).
60
+ # @api public
61
+ def durable?
62
+ @durable
63
+ end # durable?
64
+
65
+ # @return [Boolean] true if this queue was declared as exclusive (limited to just one consumer)
66
+ # @api public
67
+ def exclusive?
68
+ @exclusive
69
+ end # exclusive?
70
+
71
+ # @return [Boolean] true if this queue was declared as automatically deleted (deleted as soon as last consumer unbinds).
72
+ # @api public
73
+ def auto_delete?
74
+ @auto_delete
75
+ end # auto_delete?
76
+
77
+
78
+ # Declares this queue.
79
+ #
80
+ #
81
+ # @return [Queue] self
82
+ #
83
+ # @api public
84
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.7.2.1.)
85
+ def declare(passive = false, durable = false, exclusive = false, auto_delete = false, nowait = false, arguments = nil, &block)
86
+ raise ArgumentError, "declaration with nowait does not make sense for server-named queues! Either specify name other than empty string or use #declare without nowait" if nowait && self.anonymous?
87
+
88
+ @durable = durable
89
+ @exclusive = exclusive
90
+ @auto_delete = auto_delete
91
+
92
+ nowait = true if !block && !@name.empty?
93
+ @connection.send_frame(Protocol::Queue::Declare.encode(@channel.id, @name, passive, durable, exclusive, auto_delete, nowait, arguments))
94
+
95
+ if !nowait
96
+ self.append_callback(:declare, &block)
97
+ @channel.queues_awaiting_declare_ok.push(self)
98
+ end
99
+
100
+ self
101
+ end
102
+
103
+ # Deletes this queue.
104
+ #
105
+ # @param [Boolean] if_unused delete only if queue has no consumers (subscribers).
106
+ # @param [Boolean] if_empty delete only if queue has no messages in it.
107
+ # @param [Boolean] nowait Don't wait for reply from broker.
108
+ # @return [Queue] self
109
+ #
110
+ # @api public
111
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.7.2.9.)
112
+ def delete(if_unused = false, if_empty = false, nowait = false, &block)
113
+ nowait = true unless block
114
+ @connection.send_frame(Protocol::Queue::Delete.encode(@channel.id, @name, if_unused, if_empty, nowait))
115
+
116
+ if !nowait
117
+ self.append_callback(:delete, &block)
118
+
119
+ # TODO: delete itself from queues cache
120
+ @channel.queues_awaiting_delete_ok.push(self)
121
+ end
122
+
123
+ self
124
+ end # delete(channel, queue, if_unused, if_empty, nowait, &block)
125
+
126
+ #
127
+ # @return [Queue] self
128
+ #
129
+ # @api public
130
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.7.2.3.)
131
+ def bind(exchange, routing_key = AMQ::Protocol::EMPTY_STRING, nowait = false, arguments = nil, &block)
132
+ nowait = true unless block
133
+ exchange_name = if exchange.respond_to?(:name)
134
+ exchange.name
135
+ else
136
+
137
+ exchange
138
+ end
139
+
140
+ @connection.send_frame(Protocol::Queue::Bind.encode(@channel.id, @name, exchange_name, routing_key, nowait, arguments))
141
+
142
+ if !nowait
143
+ self.append_callback(:bind, &block)
144
+
145
+ # TODO: handle channel & connection-level exceptions
146
+ @channel.queues_awaiting_bind_ok.push(self)
147
+ end
148
+
149
+ self
150
+ end
151
+
152
+ #
153
+ # @return [Queue] self
154
+ #
155
+ # @api public
156
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.7.2.5.)
157
+ def unbind(exchange, routing_key = AMQ::Protocol::EMPTY_STRING, arguments = nil, &block)
158
+ exchange_name = if exchange.respond_to?(:name)
159
+ exchange.name
160
+ else
161
+
162
+ exchange
163
+ end
164
+
165
+ @connection.send_frame(Protocol::Queue::Unbind.encode(@channel.id, @name, exchange_name, routing_key, arguments))
166
+
167
+ self.append_callback(:unbind, &block)
168
+ # TODO: handle channel & connection-level exceptions
169
+ @channel.queues_awaiting_unbind_ok.push(self)
170
+
171
+ self
172
+ end
173
+
174
+
175
+ #
176
+ # @return [Queue] self
177
+ #
178
+ # @api public
179
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.3.)
180
+ def consume(no_ack = false, exclusive = false, nowait = false, no_local = false, arguments = nil, &block)
181
+ raise RuntimeError.new("This instance is already being consumed! Create another one using #dup.") if @consumer_tag
182
+
183
+ nowait = true unless block
184
+ @consumer_tag = generate_consumer_tag(name)
185
+ @connection.send_frame(Protocol::Basic::Consume.encode(@channel.id, @name, @consumer_tag, no_local, no_ack, exclusive, nowait, arguments))
186
+
187
+ @channel.consumers[@consumer_tag] = self
188
+
189
+ if !nowait
190
+ # unlike #get, here it is reasonable to expect more than one callback
191
+ # so we use #append_callback
192
+ self.append_callback(:consume, &block)
193
+
194
+ @channel.queues_awaiting_consume_ok.push(self)
195
+ end
196
+
197
+ self
198
+ end
199
+
200
+ # Unique string supposed to be used as a consumer tag.
201
+ #
202
+ # @return [String] Unique string.
203
+ # @api plugin
204
+ def generate_consumer_tag(name)
205
+ "#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
206
+ end
207
+
208
+ # Resets consumer tag by setting it to nil.
209
+ # @return [String] Consumer tag this queue previously used.
210
+ #
211
+ # @api plugin
212
+ def reset_consumer_tag!
213
+ ct = @consumer_tag.dup
214
+ @consumer_tag = nil
215
+
216
+ ct
217
+ end
218
+
219
+
220
+ #
221
+ # @return [Queue] self
222
+ #
223
+ # @api public
224
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.10.)
225
+ def get(no_ack = false, &block)
226
+ @connection.send_frame(Protocol::Basic::Get.encode(@channel.id, @name, no_ack))
227
+
228
+ # most people only want one callback per #get call. Consider the following example:
229
+ #
230
+ # 100.times { queue.get { ... } }
231
+ #
232
+ # most likely you won't expect 100 callback runs per message here. MK.
233
+ self.redefine_callback(:get, &block)
234
+ @channel.queues_awaiting_get_response.push(self)
235
+
236
+ self
237
+ end # get(no_ack = false, &block)
238
+
239
+ #
240
+ # @return [Queue] self
241
+ #
242
+ # @api public
243
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.5.)
244
+ def cancel(nowait = false, &block)
245
+ raise "There is no consumer tag for this queue. This usually means that you are trying to unsubscribe a queue that never was subscribed for messages in the first place." if @consumer_tag.nil?
246
+
247
+ @connection.send_frame(Protocol::Basic::Cancel.encode(@channel.id, @consumer_tag, nowait))
248
+ @consumer_tag = nil
249
+ self.clear_callbacks(:delivery)
250
+ self.clear_callbacks(:consume)
251
+
252
+ if !nowait
253
+ self.redefine_callback(:cancel, &block)
254
+ @channel.queues_awaiting_cancel_ok.push(self)
255
+ end
256
+
257
+ self
258
+ end # cancel(&block)
259
+
260
+ #
261
+ # @return [Queue] self
262
+ #
263
+ # @api public
264
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.7.2.7.)
265
+ def purge(nowait = false, &block)
266
+ nowait = true unless block
267
+ @connection.send_frame(Protocol::Queue::Purge.encode(@channel.id, @name, nowait))
268
+
269
+ if !nowait
270
+ self.redefine_callback(:purge, &block)
271
+ # TODO: handle channel & connection-level exceptions
272
+ @channel.queues_awaiting_purge_ok.push(self)
273
+ end
274
+
275
+ self
276
+ end # purge(nowait = false, &block)
277
+
278
+ #
279
+ # @return [Queue] self
280
+ #
281
+ # @api public
282
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
283
+ def acknowledge(delivery_tag)
284
+ @channel.acknowledge(delivery_tag)
285
+
286
+ self
287
+ end # acknowledge(delivery_tag)
288
+
289
+ #
290
+ # @return [Queue] self
291
+ #
292
+ # @api public
293
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
294
+ def reject(delivery_tag, requeue = true)
295
+ @channel.reject(delivery_tag, requeue)
296
+
297
+ self
298
+ end # reject(delivery_tag, requeue = true)
299
+
300
+
301
+
302
+ # @api public
303
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Sections 1.8.3.9)
304
+ def on_delivery(&block)
305
+ self.append_callback(:delivery, &block)
306
+ end # on_delivery(&block)
307
+
308
+
309
+
310
+ #
311
+ # Implementation
312
+ #
313
+
314
+ def handle_declare_ok(method)
315
+ @name = method.queue if self.anonymous?
316
+ @channel.register_queue(self)
317
+
318
+ self.exec_callback_once_yielding_self(:declare, method)
319
+ end
320
+
321
+ def handle_delete_ok(method)
322
+ self.exec_callback_once(:delete, method)
323
+ end # handle_delete_ok(method)
324
+
325
+ def handle_consume_ok(method)
326
+ self.exec_callback_once(:consume, method)
327
+ end # handle_consume_ok(method)
328
+
329
+ def handle_purge_ok(method)
330
+ self.exec_callback_once(:purge, method)
331
+ end # handle_purge_ok(method)
332
+
333
+ def handle_bind_ok(method)
334
+ self.exec_callback_once(:bind, method)
335
+ end # handle_bind_ok(method)
336
+
337
+ def handle_unbind_ok(method)
338
+ self.exec_callback_once(:unbind, method)
339
+ end # handle_unbind_ok(method)
340
+
341
+ def handle_delivery(method, header, payload)
342
+ self.exec_callback(:delivery, method, header, payload)
343
+ end # handle_delivery
344
+
345
+ def handle_cancel_ok(method)
346
+ @consumer_tag = nil
347
+ self.exec_callback_once(:cancel, method)
348
+ end # handle_cancel_ok(method)
349
+
350
+ def handle_get_ok(method, header, payload)
351
+ method = Protocol::GetResponse.new(method)
352
+ self.exec_callback(:get, method, header, payload)
353
+ end # handle_get_ok(method, header, payload)
354
+
355
+ def handle_get_empty(method)
356
+ method = Protocol::GetResponse.new(method)
357
+ self.exec_callback(:get, method)
358
+ end # handle_get_empty(method)
359
+
360
+
361
+
362
+ # Get the first queue which didn't receive Queue.Declare-Ok yet and run its declare callback.
363
+ # The cache includes only queues with {nowait: false}.
364
+ self.handle(Protocol::Queue::DeclareOk) do |connection, frame|
365
+ method = frame.decode_payload
366
+
367
+ channel = connection.channels[frame.channel]
368
+ queue = channel.queues_awaiting_declare_ok.shift
369
+
370
+ queue.handle_declare_ok(method)
371
+ end
372
+
373
+
374
+ self.handle(Protocol::Queue::DeleteOk) do |connection, frame|
375
+ channel = connection.channels[frame.channel]
376
+ queue = channel.queues_awaiting_delete_ok.shift
377
+ queue.handle_delete_ok(frame.decode_payload)
378
+ end
379
+
380
+
381
+ self.handle(Protocol::Queue::BindOk) do |connection, frame|
382
+ channel = connection.channels[frame.channel]
383
+ queue = channel.queues_awaiting_bind_ok.shift
384
+
385
+ queue.handle_bind_ok(frame.decode_payload)
386
+ end
387
+
388
+
389
+ self.handle(Protocol::Queue::UnbindOk) do |connection, frame|
390
+ channel = connection.channels[frame.channel]
391
+ queue = channel.queues_awaiting_unbind_ok.shift
392
+
393
+ queue.handle_unbind_ok(frame.decode_payload)
394
+ end
395
+
396
+
397
+ self.handle(Protocol::Basic::ConsumeOk) do |connection, frame|
398
+ channel = connection.channels[frame.channel]
399
+ queue = channel.queues_awaiting_consume_ok.shift
400
+
401
+ queue.handle_consume_ok(frame.decode_payload)
402
+ end
403
+
404
+
405
+ self.handle(Protocol::Basic::CancelOk) do |connection, frame|
406
+ channel = connection.channels[frame.channel]
407
+ queue = channel.queues_awaiting_cancel_ok.shift
408
+
409
+ queue.handle_consume_ok(frame.decode_payload)
410
+ end
411
+
412
+
413
+ # Basic.Deliver
414
+ self.handle(Protocol::Basic::Deliver) do |connection, method_frame, content_frames|
415
+ channel = connection.channels[method_frame.channel]
416
+ method = method_frame.decode_payload
417
+ queue = channel.consumers[method.consumer_tag]
418
+
419
+ header = content_frames.shift
420
+ body = content_frames.map { |frame| frame.payload }.join
421
+ queue.handle_delivery(method, header, body)
422
+ # TODO: ack if necessary
423
+ end
424
+
425
+
426
+ self.handle(Protocol::Queue::PurgeOk) do |connection, frame|
427
+ channel = connection.channels[frame.channel]
428
+ queue = channel.queues_awaiting_purge_ok.shift
429
+
430
+ queue.handle_purge_ok(frame.decode_payload)
431
+ end
432
+
433
+
434
+ self.handle(Protocol::Basic::GetOk) do |connection, frame, content_frames|
435
+ channel = connection.channels[frame.channel]
436
+ queue = channel.queues_awaiting_get_response.shift
437
+ method = frame.decode_payload
438
+
439
+ header = content_frames.shift
440
+ body = content_frames.map {|frame| frame.payload }.join
441
+
442
+ queue.handle_get_ok(method, header, body) if queue
443
+ end
444
+
445
+
446
+ self.handle(Protocol::Basic::GetEmpty) do |connection, frame|
447
+ channel = connection.channels[frame.channel]
448
+ queue = channel.queues_awaiting_get_response.shift
449
+
450
+ queue.handle_get_empty(frame.decode_payload)
451
+ end
452
+ end # Queue
453
+ end # Async
454
+ end # Client
455
+ end # AMQ