amq-client 0.7.0.alpha34 → 0.7.0.alpha35

Sign up to get free protection for your applications and to get access to all the features.
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