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 @@
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