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 @@
1
+ require "amq/client/async/adapters/event_machine"
@@ -0,0 +1,71 @@
1
+ module AMQ
2
+ module Client
3
+ module Async
4
+ module Callbacks
5
+
6
+ def redefine_callback(event, callable = nil, &block)
7
+ f = (callable || block)
8
+ # yes, re-assign!
9
+ @callbacks[event] = [f]
10
+
11
+ self
12
+ end
13
+
14
+ def define_callback(event, callable = nil, &block)
15
+ f = (callable || block)
16
+
17
+ @callbacks[event] ||= []
18
+ @callbacks[event] << f if f
19
+
20
+ self
21
+ end # define_callback(event, &block)
22
+ alias append_callback define_callback
23
+
24
+ def prepend_callback(event, &block)
25
+ @callbacks[event] ||= []
26
+ @callbacks[event].unshift(block)
27
+
28
+ self
29
+ end # prepend_callback(event, &block)
30
+
31
+ def clear_callbacks(event)
32
+ @callbacks[event].clear if @callbacks[event]
33
+ end # clear_callbacks(event)
34
+
35
+
36
+ def exec_callback(name, *args, &block)
37
+ list = Array(self.callbacks[name])
38
+ if list.any?
39
+ list.each { |c| c.call(*args, &block) }
40
+ end
41
+ end
42
+
43
+ def exec_callback_once(name, *args, &block)
44
+ list = (self.callbacks.delete(name) || Array.new)
45
+ if list.any?
46
+ list.each { |c| c.call(*args, &block) }
47
+ end
48
+ end
49
+
50
+ def exec_callback_yielding_self(name, *args, &block)
51
+ list = Array(self.callbacks[name])
52
+ if list.any?
53
+ list.each { |c| c.call(self, *args, &block) }
54
+ end
55
+ end
56
+
57
+ def exec_callback_once_yielding_self(name, *args, &block)
58
+ list = (self.callbacks.delete(name) || Array.new)
59
+
60
+ if list.any?
61
+ list.each { |c| c.call(self, *args, &block) }
62
+ end
63
+ end
64
+
65
+ def has_callback?(name)
66
+ self.callbacks[name] && !self.callbacks[name].empty?
67
+ end # has_callback?
68
+ end # Callbacks
69
+ end # Async
70
+ end # Client
71
+ end # AMQ
@@ -0,0 +1,385 @@
1
+ # encoding: utf-8
2
+
3
+ require "amq/client/async/entity"
4
+ require "amq/client/queue"
5
+ require "amq/client/exchange"
6
+
7
+ module AMQ
8
+ module Client
9
+ module Async
10
+ class Channel
11
+
12
+ #
13
+ # Behaviors
14
+ #
15
+
16
+ extend RegisterEntityMixin
17
+ include Entity
18
+ extend ProtocolMethodHandlers
19
+
20
+ register_entity :queue, AMQ::Client::Queue
21
+ register_entity :exchange, AMQ::Client::Exchange
22
+
23
+ #
24
+ # API
25
+ #
26
+
27
+
28
+ class ChannelOutOfBadError < StandardError # TODO: inherit from some AMQP error class defined in amq-protocol or use it straight away.
29
+ def initialize(max, given)
30
+ super("Channel max is #{max}, #{given} given.")
31
+ end
32
+ end
33
+
34
+
35
+ DEFAULT_REPLY_TEXT = "Goodbye".freeze
36
+
37
+ attr_reader :id
38
+
39
+ attr_reader :exchanges_awaiting_declare_ok, :exchanges_awaiting_delete_ok
40
+ attr_reader :queues_awaiting_declare_ok, :queues_awaiting_delete_ok, :queues_awaiting_bind_ok, :queues_awaiting_unbind_ok, :queues_awaiting_purge_ok, :queues_awaiting_consume_ok, :queues_awaiting_cancel_ok, :queues_awaiting_get_response
41
+
42
+ attr_accessor :flow_is_active
43
+
44
+
45
+ def initialize(connection, id)
46
+ super(connection)
47
+
48
+ @id = id
49
+ @exchanges = Hash.new
50
+ @queues = Hash.new
51
+ @consumers = Hash.new
52
+
53
+ reset_state!
54
+
55
+ # 65536 is here for cases when channel is opened without passing a callback in,
56
+ # otherwise channel_mix would be nil and it causes a lot of needless headaches.
57
+ # lets just have this default. MK.
58
+ channel_max = if @connection.open?
59
+ @connection.channel_max || 65536
60
+ else
61
+ 65536
62
+ end
63
+
64
+ if channel_max != 0 && !(0..channel_max).include?(id)
65
+ raise ChannelOutOfBadError.new(channel_max, id)
66
+ end
67
+ end
68
+
69
+ def consumers
70
+ @consumers
71
+ end # consumers
72
+
73
+ # @return [Array<Queue>] Collection of queues that were declared on this channel.
74
+ def queues
75
+ @queues.values
76
+ end
77
+
78
+ # @return [Array<Exchange>] Collection of exchanges that were declared on this channel.
79
+ def exchanges
80
+ @exchanges.values
81
+ end
82
+
83
+
84
+ # AMQP connection this channel belongs to.
85
+ #
86
+ # @return [AMQ::Client::Connection] Connection this channel belongs to.
87
+ def connection
88
+ @connection
89
+ end # connection
90
+
91
+
92
+ # @group Channel lifecycle
93
+
94
+ # Opens AMQP channel.
95
+ #
96
+ # @api public
97
+ def open(&block)
98
+ @connection.send_frame(Protocol::Channel::Open.encode(@id, AMQ::Protocol::EMPTY_STRING))
99
+ @connection.channels[@id] = self
100
+ self.status = :opening
101
+
102
+ self.redefine_callback :open, &block
103
+ end
104
+
105
+ # Closes AMQP channel.
106
+ #
107
+ # @api public
108
+ def close(reply_code = 200, reply_text = DEFAULT_REPLY_TEXT, class_id = 0, method_id = 0, &block)
109
+ @connection.send_frame(Protocol::Channel::Close.encode(@id, reply_code, reply_text, class_id, method_id))
110
+
111
+ self.redefine_callback :close, &block
112
+ end
113
+
114
+ # @endgroup
115
+
116
+
117
+
118
+ # @group Message acknowledgements
119
+
120
+ # Acknowledge one or all messages on the channel.
121
+ #
122
+ # @api public
123
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
124
+ def acknowledge(delivery_tag, multiple = false)
125
+ @connection.send_frame(Protocol::Basic::Ack.encode(self.id, delivery_tag, multiple))
126
+
127
+ self
128
+ end # acknowledge(delivery_tag, multiple = false)
129
+
130
+ # Reject a message with given delivery tag.
131
+ #
132
+ # @api public
133
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
134
+ def reject(delivery_tag, requeue = true)
135
+ @connection.send_frame(Protocol::Basic::Reject.encode(self.id, delivery_tag, requeue))
136
+
137
+ self
138
+ end # reject(delivery_tag, requeue = true)
139
+
140
+ # Notifies AMQ broker that consumer has recovered and unacknowledged messages need
141
+ # to be redelivered.
142
+ #
143
+ # @return [Channel] self
144
+ #
145
+ # @note RabbitMQ as of 2.3.1 does not support basic.recover with requeue = false.
146
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.16.)
147
+ # @api public
148
+ def recover(requeue = true, &block)
149
+ @connection.send_frame(Protocol::Basic::Recover.encode(@id, requeue))
150
+
151
+ self.redefine_callback :recover, &block
152
+ self
153
+ end # recover(requeue = false, &block)
154
+
155
+ # @endgroup
156
+
157
+
158
+
159
+ # @group QoS and flow handling
160
+
161
+ # Requests a specific quality of service. The QoS can be specified for the current channel
162
+ # or for all channels on the connection.
163
+ #
164
+ # @note RabbitMQ as of 2.3.1 does not support prefetch_size.
165
+ # @api public
166
+ def qos(prefetch_size = 0, prefetch_count = 32, global = false, &block)
167
+ @connection.send_frame(Protocol::Basic::Qos.encode(@id, prefetch_size, prefetch_count, global))
168
+
169
+ self.redefine_callback :qos, &block
170
+ self
171
+ end # qos(prefetch_size = 4096, prefetch_count = 32, global = false, &block)
172
+
173
+ # Asks the peer to pause or restart the flow of content data sent to a consumer.
174
+ # This is a simple flow­control mechanism that a peer can use to avoid overflowing its
175
+ # queues or otherwise finding itself receiving more messages than it can process. Note that
176
+ # this method is not intended for window control. It does not affect contents returned to
177
+ # Queue#get callers.
178
+ #
179
+ # @param [Boolean] active Desired flow state.
180
+ #
181
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.5.2.3.)
182
+ # @api public
183
+ def flow(active = false, &block)
184
+ @connection.send_frame(Protocol::Channel::Flow.encode(@id, active))
185
+
186
+ self.redefine_callback :flow, &block
187
+ self
188
+ end # flow(active = false, &block)
189
+
190
+ # @return [Boolean] True if flow in this channel is active (messages will be delivered to consumers that use this channel).
191
+ #
192
+ # @api public
193
+ def flow_is_active?
194
+ @flow_is_active
195
+ end # flow_is_active?
196
+
197
+ # @endgroup
198
+
199
+
200
+
201
+ # @group Transactions
202
+
203
+ # Sets the channel to use standard transactions. One must use this method at least
204
+ # once on a channel before using #tx_tommit or tx_rollback methods.
205
+ #
206
+ # @api public
207
+ def tx_select(&block)
208
+ @connection.send_frame(Protocol::Tx::Select.encode(@id))
209
+
210
+ self.redefine_callback :tx_select, &block
211
+ self
212
+ end # tx_select(&block)
213
+
214
+ # Commits AMQP transaction.
215
+ #
216
+ # @api public
217
+ def tx_commit(&block)
218
+ @connection.send_frame(Protocol::Tx::Commit.encode(@id))
219
+
220
+ self.redefine_callback :tx_commit, &block
221
+ self
222
+ end # tx_commit(&block)
223
+
224
+ # Rolls AMQP transaction back.
225
+ #
226
+ # @api public
227
+ def tx_rollback(&block)
228
+ @connection.send_frame(Protocol::Tx::Rollback.encode(@id))
229
+
230
+ self.redefine_callback :tx_rollback, &block
231
+ self
232
+ end # tx_rollback(&block)
233
+
234
+ # @endgroup
235
+
236
+
237
+
238
+ # @group Error handling
239
+
240
+ # Defines a callback that will be executed when channel is closed after
241
+ # channel-level exception.
242
+ #
243
+ # @api public
244
+ def on_error(&block)
245
+ self.define_callback(:error, &block)
246
+ end
247
+
248
+ # @endgroup
249
+
250
+
251
+ #
252
+ # Implementation
253
+ #
254
+
255
+ def register_exchange(exchange)
256
+ raise ArgumentError, "argument is nil!" if exchange.nil?
257
+
258
+ @exchanges[exchange.name] = exchange
259
+ end # register_exchange(exchange)
260
+
261
+ # Finds exchange in the exchanges cache on this channel by name. Exchange only exists in the cache if
262
+ # it was previously instantiated on this channel.
263
+ #
264
+ # @param [String] name Exchange name
265
+ # @return [AMQ::Client::Exchange] Exchange (if found)
266
+ # @api plugin
267
+ def find_exchange(name)
268
+ @exchanges[name]
269
+ end
270
+
271
+ def register_queue(queue)
272
+ raise ArgumentError, "argument is nil!" if queue.nil?
273
+
274
+ @queues[queue.name] = queue
275
+ end # register_queue(queue)
276
+
277
+ def find_queue(name)
278
+ @queues[name]
279
+ end
280
+
281
+
282
+ def reset_state!
283
+ @flow_is_active = true
284
+
285
+ @queues_awaiting_declare_ok = Array.new
286
+ @exchanges_awaiting_declare_ok = Array.new
287
+
288
+ @queues_awaiting_delete_ok = Array.new
289
+
290
+ @exchanges_awaiting_delete_ok = Array.new
291
+ @queues_awaiting_purge_ok = Array.new
292
+ @queues_awaiting_bind_ok = Array.new
293
+ @queues_awaiting_unbind_ok = Array.new
294
+ @queues_awaiting_consume_ok = Array.new
295
+ @queues_awaiting_cancel_ok = Array.new
296
+
297
+ @queues_awaiting_get_response = Array.new
298
+
299
+ @callbacks = Hash.new
300
+ end # reset_state!
301
+
302
+
303
+ def handle_connection_interruption(method = nil)
304
+ self.reset_state!
305
+ end # handle_connection_interruption
306
+
307
+
308
+
309
+ def handle_open_ok(open_ok)
310
+ self.status = :opened
311
+ self.exec_callback_once_yielding_self(:open, open_ok)
312
+ end
313
+
314
+ def handle_close_ok(close_ok)
315
+ self.status = :closed
316
+ self.exec_callback_once_yielding_self(:close, close_ok)
317
+ end
318
+
319
+ def handle_close(channel_close)
320
+ self.status = :closed
321
+ self.exec_callback_yielding_self(:error, channel_close)
322
+
323
+ self.handle_connection_interruption(channel_close)
324
+ end
325
+
326
+
327
+
328
+ self.handle(Protocol::Channel::OpenOk) do |connection, frame|
329
+ channel = connection.channels[frame.channel]
330
+ channel.handle_open_ok(frame.decode_payload)
331
+ end
332
+
333
+ self.handle(Protocol::Channel::CloseOk) do |connection, frame|
334
+ method = frame.decode_payload
335
+ channels = connection.channels
336
+
337
+ channel = channels[frame.channel]
338
+ channels.delete(channel)
339
+ channel.handle_close_ok(method)
340
+ end
341
+
342
+ self.handle(Protocol::Channel::Close) do |connection, frame|
343
+ method = frame.decode_payload
344
+ channels = connection.channels
345
+ channel = channels[frame.channel]
346
+
347
+ channel.handle_close(method)
348
+ end
349
+
350
+ self.handle(Protocol::Basic::QosOk) do |connection, frame|
351
+ channel = connection.channels[frame.channel]
352
+ channel.exec_callback(:qos, frame.decode_payload)
353
+ end
354
+
355
+ self.handle(Protocol::Basic::RecoverOk) do |connection, frame|
356
+ channel = connection.channels[frame.channel]
357
+ channel.exec_callback(:recover, frame.decode_payload)
358
+ end
359
+
360
+ self.handle(Protocol::Channel::FlowOk) do |connection, frame|
361
+ channel = connection.channels[frame.channel]
362
+ method = frame.decode_payload
363
+
364
+ channel.flow_is_active = method.active
365
+ channel.exec_callback(:flow, method)
366
+ end
367
+
368
+ self.handle(Protocol::Tx::SelectOk) do |connection, frame|
369
+ channel = connection.channels[frame.channel]
370
+ channel.exec_callback(:tx_select, frame.decode_payload)
371
+ end
372
+
373
+ self.handle(Protocol::Tx::CommitOk) do |connection, frame|
374
+ channel = connection.channels[frame.channel]
375
+ channel.exec_callback(:tx_commit, frame.decode_payload)
376
+ end
377
+
378
+ self.handle(Protocol::Tx::RollbackOk) do |connection, frame|
379
+ channel = connection.channels[frame.channel]
380
+ channel.exec_callback(:tx_rollback, frame.decode_payload)
381
+ end
382
+ end # Channel
383
+ end # Async
384
+ end # Client
385
+ end # AMQ