amqp 0.9.10 → 1.0.0.pre1

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 (78) hide show
  1. data/.travis.yml +0 -3
  2. data/CHANGELOG +4 -0
  3. data/Gemfile +1 -0
  4. data/README.md +72 -81
  5. data/amqp.gemspec +14 -5
  6. data/docs/08Migration.textile +0 -4
  7. data/docs/AMQP091ModelExplained.textile +0 -5
  8. data/docs/Bindings.textile +0 -4
  9. data/docs/Clustering.textile +0 -4
  10. data/docs/ConnectingToTheBroker.textile +1 -5
  11. data/docs/ConnectionEncryptionWithTLS.textile +0 -4
  12. data/docs/DocumentationGuidesIndex.textile +0 -4
  13. data/docs/Durability.textile +0 -4
  14. data/docs/ErrorHandling.textile +40 -106
  15. data/docs/Exchanges.textile +0 -4
  16. data/docs/GettingStarted.textile +6 -10
  17. data/docs/PatternsAndUseCases.textile +1 -4
  18. data/docs/Queues.textile +0 -4
  19. data/docs/RabbitMQVersions.textile +0 -4
  20. data/docs/RunningTests.textile +0 -4
  21. data/docs/TestingWithEventedSpec.textile +0 -4
  22. data/docs/Troubleshooting.textile +0 -4
  23. data/docs/VendorSpecificExtensions.textile +0 -4
  24. data/examples/error_handling/hello_world_producer.rb +1 -1
  25. data/examples/issues/issue_121.rb +23 -0
  26. data/examples/patterns/request_reply/client.rb +2 -1
  27. data/examples/patterns/request_reply/server.rb +1 -0
  28. data/examples/publishing/returned_messages.rb +1 -1
  29. data/lib/amqp.rb +0 -7
  30. data/lib/amqp/channel.rb +15 -33
  31. data/lib/amqp/client.rb +2 -2
  32. data/lib/amqp/compatibility/ruby187_patchlevel_check.rb +4 -4
  33. data/lib/amqp/connection.rb +0 -1
  34. data/lib/amqp/consumer.rb +2 -2
  35. data/lib/amqp/exceptions.rb +1 -10
  36. data/lib/amqp/exchange.rb +5 -5
  37. data/lib/amqp/queue.rb +23 -47
  38. data/lib/amqp/session.rb +4 -4
  39. data/lib/amqp/version.rb +1 -1
  40. data/spec/integration/basic_get_spec.rb +24 -80
  41. data/spec/integration/basic_return_spec.rb +3 -3
  42. data/spec/integration/channel_level_exception_with_multiple_channels_spec.rb +1 -0
  43. data/spec/integration/exchange_declaration_spec.rb +102 -71
  44. data/spec/integration/extensions/rabbitmq/publisher_confirmations_spec.rb +17 -1
  45. data/spec/integration/fanout_exchange_routing_spec.rb +1 -1
  46. data/spec/integration/immediate_messages_spec.rb +59 -0
  47. data/spec/integration/multiple_consumers_per_queue_spec.rb +101 -39
  48. data/spec/integration/queue_redeclaration_with_incompatible_attributes_spec.rb +12 -25
  49. data/spec/integration/regressions/concurrent_publishing_on_the_same_channel_spec.rb +1 -1
  50. data/spec/integration/reply_queue_communication_spec.rb +2 -1
  51. data/spec/integration/store_and_forward_spec.rb +9 -6
  52. data/spec/integration/topic_subscription_spec.rb +4 -5
  53. data/spec/spec_helper.rb +2 -8
  54. data/spec/unit/amqp/connection_spec.rb +1 -3
  55. metadata +112 -116
  56. data/examples/deprecated/default_thread_local_channel_instance.rb +0 -34
  57. data/examples/legacy/ack.rb +0 -70
  58. data/examples/legacy/callbacks.rb +0 -45
  59. data/examples/legacy/clock.rb +0 -74
  60. data/examples/legacy/hashtable.rb +0 -60
  61. data/examples/legacy/logger.rb +0 -92
  62. data/examples/legacy/multiclock.rb +0 -56
  63. data/examples/legacy/pingpong.rb +0 -51
  64. data/examples/legacy/primes-simple.rb +0 -29
  65. data/examples/legacy/primes.rb +0 -74
  66. data/examples/legacy/stocks.rb +0 -59
  67. data/lib/amqp/deprecated/fork.rb +0 -17
  68. data/lib/amqp/deprecated/logger.rb +0 -100
  69. data/lib/amqp/deprecated/mq.rb +0 -22
  70. data/lib/amqp/deprecated/rpc.rb +0 -169
  71. data/lib/amqp/ext/em.rb +0 -3
  72. data/lib/amqp/ext/emfork.rb +0 -72
  73. data/lib/amqp/logger.rb +0 -19
  74. data/lib/amqp/rpc.rb +0 -20
  75. data/lib/mq.rb +0 -35
  76. data/lib/mq/logger.rb +0 -4
  77. data/lib/mq/rpc.rb +0 -4
  78. data/spec/integration/remove_individual_binding_spec.rb +0 -51
@@ -67,6 +67,22 @@ describe "Publisher confirmation(s)" do
67
67
  @channel2 = AMQP::Channel.new
68
68
  end
69
69
 
70
+ it 'should increment publisher_index confirming channel' do
71
+ channel3 = AMQP::Channel.new
72
+ exchange = channel3.fanout("amqpgem.tests.fanout0", :auto_delete => true)
73
+
74
+ channel3.confirm_select
75
+ channel3.publisher_index.should == 0
76
+
77
+ EventMachine.add_timer(0.5) do
78
+ exchange.publish("Hi")
79
+ end
80
+
81
+ done(2.0) do
82
+ channel3.publisher_index.should == 1
83
+ end
84
+ end
85
+
70
86
 
71
87
  context "when messages are transient" do
72
88
  context "and routable" do
@@ -116,7 +132,7 @@ describe "Publisher confirmation(s)" do
116
132
  end
117
133
 
118
134
  EventMachine.add_timer(0.5) do
119
- exchange.publish("Hi", :persistent => false)
135
+ exchange.publish("Hi", :persistent => false, :immediately => true)
120
136
  end
121
137
 
122
138
  done(2.0) do
@@ -77,7 +77,7 @@ describe AMQP::Exchange, "of type fanout" do
77
77
  end
78
78
 
79
79
  # for Rubinius, it is surprisingly slow on this workload
80
- done(1.5) {
80
+ done(2.5) {
81
81
  [@queue1, @queue2, @queue3].each do |q|
82
82
  @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
83
83
 
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+
5
+ describe "When queue has no consumers" do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include EventedSpec::AMQPSpec
12
+ default_timeout 1.5
13
+
14
+ amqp_before do
15
+ @producer_channel = AMQP::Channel.new
16
+ @consumer_channel = AMQP::Channel.new
17
+ end
18
+
19
+ # ...
20
+
21
+
22
+ #
23
+ # Examples
24
+ #
25
+
26
+ context "and message is published as :immediate" do
27
+ it "that message is returned to the publisher" do
28
+ exchange = @producer_channel.fanout("amq.fanout")
29
+ queue = @consumer_channel.queue("", :exclusive => true)
30
+
31
+ exchange.on_return do |basic_return, metadata, payload|
32
+ done if payload == "immediate message body"
33
+ end
34
+
35
+ queue.bind(exchange) do
36
+ exchange.publish "immediate message body", :immediate => true
37
+ end
38
+ end
39
+ end
40
+
41
+
42
+
43
+ context "and message is published as non :immediate" do
44
+ it "that message is dropped" do
45
+ exchange = @producer_channel.fanout("amq.fanout")
46
+ queue = @consumer_channel.queue("", :exclusive => true)
47
+
48
+ exchange.on_return do |basic_return, metadata, payload|
49
+ fail "Should not happen"
50
+ end
51
+
52
+ queue.bind(exchange) do
53
+ exchange.publish "non-immediate message body", :immediate => false
54
+ end
55
+
56
+ done(1.0)
57
+ end
58
+ end
59
+ end # describe
@@ -58,7 +58,7 @@ describe "Multiple non-exclusive consumers per queue" do
58
58
 
59
59
  EventMachine.add_timer(1.0) do
60
60
  messages.each do |message|
61
- exchange.publish(message, :mandatory => true, :routing_key => queue.name)
61
+ exchange.publish(message, :immediate => true, :mandatory => true, :routing_key => queue.name)
62
62
  end
63
63
  end
64
64
 
@@ -104,7 +104,7 @@ describe "Multiple non-exclusive consumers per queue" do
104
104
 
105
105
  EventMachine.add_timer(1.0) do
106
106
  messages.each do |message|
107
- exchange.publish(message, :mandatory => true, :routing_key => queue.name)
107
+ exchange.publish(message, :immediate => true, :mandatory => true, :routing_key => queue.name)
108
108
  end
109
109
  end
110
110
 
@@ -150,7 +150,7 @@ describe "Multiple non-exclusive consumers per queue" do
150
150
 
151
151
  EventMachine.add_timer(1.0) do
152
152
  messages.each do |message|
153
- exchange.publish(message, :mandatory => true, :routing_key => queue.name)
153
+ exchange.publish(message, :immediate => true, :mandatory => true, :routing_key => queue.name)
154
154
  end
155
155
  end
156
156
 
@@ -199,7 +199,7 @@ describe "Multiple non-exclusive consumers per queue" do
199
199
 
200
200
  EventMachine.add_timer(1.0) do
201
201
  messages.each do |message|
202
- exchange.publish(message, :mandatory => true, :routing_key => queue.name)
202
+ exchange.publish(message, :immediate => true, :mandatory => true, :routing_key => queue.name)
203
203
  end
204
204
  end
205
205
 
@@ -213,47 +213,109 @@ describe "Multiple non-exclusive consumers per queue" do
213
213
 
214
214
 
215
215
 
216
- context "with equal prefetch levels, a server-named queue and two consumers cancelled mid-flight" do
217
- it "have messages distributed to the only active consumer" do
218
- channel = AMQP::Channel.new
219
- channel.on_error do |ch, channel_close|
220
- raise(channel_close.reply_text)
221
- end
216
+ unless ENV["CI"]
217
+ context "with equal prefetch levels, a server-named queue and two consumers cancelled mid-flight" do
218
+ it "have messages distributed to the only active consumer" do
219
+ channel = AMQP::Channel.new
220
+ channel.on_error do |ch, channel_close|
221
+ raise(channel_close.reply_text)
222
+ end
222
223
 
223
- queue = channel.queue("", :auto_delete => true)
224
- consumer1 = AMQP::Consumer.new(channel, queue)
225
- consumer2 = AMQP::Consumer.new(channel, queue)
224
+ queue = channel.queue("", :auto_delete => true)
225
+ consumer1 = AMQP::Consumer.new(channel, queue)
226
+ consumer2 = AMQP::Consumer.new(channel, queue)
226
227
 
227
- consumer1.consume.on_delivery do |basic_deliver, metadata, payload|
228
- @consumer1_mailbox << payload
229
- end
228
+ consumer1.consume.on_delivery do |basic_deliver, metadata, payload|
229
+ @consumer1_mailbox << payload
230
+ end
230
231
 
231
- consumer2.consume(true).on_delivery do |metadata, payload|
232
- @consumer2_mailbox << payload
233
- end
234
- queue.subscribe do |metadata, payload|
235
- @consumer3_mailbox << payload
236
- end
237
- queue.unsubscribe
238
- consumer2.cancel
232
+ consumer2.consume(true).on_delivery do |metadata, payload|
233
+ @consumer2_mailbox << payload
234
+ end
235
+ queue.subscribe do |metadata, payload|
236
+ @consumer3_mailbox << payload
237
+ end
238
+ queue.unsubscribe
239
+ consumer2.cancel
239
240
 
240
241
 
241
- exchange = channel.default_exchange
242
- exchange.on_return do |basic_return, metadata, payload|
243
- raise(basic_return.reply_text)
244
- end
242
+ exchange = channel.default_exchange
243
+ exchange.on_return do |basic_return, metadata, payload|
244
+ raise(basic_return.reply_text)
245
+ end
245
246
 
246
- EventMachine.add_timer(1.0) do
247
- messages.each do |message|
248
- exchange.publish(message, :mandatory => true, :routing_key => queue.name)
247
+ EventMachine.add_timer(1.0) do
248
+ messages.each do |message|
249
+ exchange.publish(message, :immediate => true, :mandatory => true, :routing_key => queue.name)
250
+ end
249
251
  end
250
- end
251
252
 
252
- done(5.0) {
253
- @consumer1_mailbox.size.should == 100
254
- @consumer2_mailbox.size.should == 0
255
- @consumer3_mailbox.size.should == 0
256
- }
257
- end # it
258
- end # context
253
+ done(5.0) {
254
+ @consumer1_mailbox.size.should == 100
255
+ @consumer2_mailbox.size.should == 0
256
+ @consumer3_mailbox.size.should == 0
257
+ }
258
+ end # it
259
+ end # context
260
+
261
+
262
+
263
+ context "with equal prefetch levels and ALL consumers cancelled mid-flight" do
264
+ it "returns all immediate messages" do
265
+ @returned_messages = []
266
+
267
+ channel = AMQP::Channel.new
268
+ channel.on_error do |ch, channel_close|
269
+ raise(channel_close.reply_text)
270
+ end
271
+
272
+ queue = channel.queue("amqpgem.integration.roundrobin.queue1", :auto_delete => true) do
273
+ consumer1 = AMQP::Consumer.new(channel, queue)
274
+ consumer2 = AMQP::Consumer.new(channel, queue, "#{queue.name}-consumer-#{rand}-#{Time.now}", false, true)
275
+
276
+ consumer1.consume.on_delivery do |basic_deliver, metadata, payload|
277
+ @consumer1_mailbox << payload
278
+ end
279
+
280
+ consumer2.consume(true).on_delivery do |metadata, payload|
281
+ @consumer2_mailbox << payload
282
+ end
283
+
284
+ queue.subscribe do |metadata, payload|
285
+ @consumer3_mailbox << payload
286
+ end
287
+ queue.should be_subscribed
288
+ queue.unsubscribe
289
+ queue.should_not be_subscribed
290
+
291
+ consumer2.should be_subscribed
292
+ consumer2.callback.should_not be_nil
293
+ consumer2.cancel
294
+ consumer2.should_not be_subscribed
295
+ consumer2.callback.should be_nil
296
+
297
+ consumer1.should be_subscribed
298
+ consumer1.callback.should_not be_nil
299
+ consumer1.cancel
300
+ consumer1.should_not be_subscribed
301
+ consumer1.callback.should be_nil
302
+ end
303
+
304
+ exchange = channel.default_exchange
305
+ exchange.on_return do |basic_return, metadata, payload|
306
+ @returned_messages << payload
307
+ end
308
+
309
+ EventMachine.add_timer(1.0) do
310
+ messages.each do |message|
311
+ exchange.publish(message, :immediate => true, :mandatory => true, :routing_key => queue.name)
312
+ end
313
+ end
314
+
315
+ done(6.0) {
316
+ @returned_messages.size.should == 100
317
+ }
318
+ end # it
319
+ end # context
320
+ end
259
321
  end # describe
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe "AMQP queue redeclaration with different attributes" do
5
+ describe AMQP do
6
6
 
7
7
  #
8
8
  # Environment
@@ -17,7 +17,7 @@ describe "AMQP queue redeclaration with different attributes" do
17
17
  # Examples
18
18
  #
19
19
 
20
- context "when :durable value is different" do
20
+ context "when queue is redeclared with different attributes" do
21
21
  let(:name) { "amqp-gem.nondurable.queue" }
22
22
  let(:options) {
23
23
  { :durable => false, :exclusive => true, :auto_delete => true, :arguments => {}, :passive => false }
@@ -25,48 +25,35 @@ describe "AMQP queue redeclaration with different attributes" do
25
25
  let(:different_options) {
26
26
  { :durable => true, :exclusive => true, :auto_delete => true, :arguments => {}, :passive => false}
27
27
  }
28
+ let(:irrelevant_different_options) {
29
+ { :durable => false, :exclusive => true, :auto_delete => true, :arguments => {}, :passive => false, :header => {:random => 'stuff' } }
30
+ }
28
31
 
29
32
 
30
33
  it "should raise AMQP::IncompatibleOptionsError for incompatable options" do
31
34
  channel = AMQP::Channel.new
32
- channel.on_error do |ch, channel_close|
33
- @error_code = channel_close.reply_code
35
+ channel.on_error do |ch, close|
36
+ @callback_fired = true
34
37
  end
35
38
 
36
39
  channel.queue(name, options)
37
40
  expect {
38
41
  channel.queue(name, different_options)
39
42
  }.to raise_error(AMQP::IncompatibleOptionsError)
40
-
41
- done(0.5) {
42
- @error_code.should == 406
43
- }
43
+ done
44
44
  end
45
- end
46
-
47
- context "when :headers are different" do
48
- let(:name) { "amqp-gem.nondurable.queue" }
49
- let(:options) {
50
- { :durable => false, :exclusive => true, :auto_delete => true, :arguments => {}, :passive => false }
51
- }
52
- let(:different_options) {
53
- { :durable => false, :exclusive => true, :auto_delete => true, :arguments => {}, :passive => false, :header => {:random => 'stuff' } }
54
- }
55
45
 
56
46
  it "should not raise AMQP::IncompatibleOptionsError for irrelevant options" do
57
47
  channel = AMQP::Channel.new
58
- channel.on_error do |ch, channel_close|
59
- @error_code = channel_close.reply_code
48
+ channel.on_error do |ch, close|
49
+ @callback_fired = true
60
50
  end
61
51
 
62
52
  channel.queue(name, options)
63
53
  expect {
64
- channel.queue(name, different_options)
54
+ channel.queue(name, irrelevant_different_options)
65
55
  }.to_not raise_error(AMQP::IncompatibleOptionsError)
66
-
67
- done(0.5) {
68
- @error_code.should == 406
69
- }
56
+ done
70
57
  end
71
58
  end
72
59
  end # describe AMQP
@@ -73,7 +73,7 @@ if mri?
73
73
  20.times do
74
74
  Thread.new do
75
75
  messages.each do |message|
76
- exchange.publish(message, :routing_key => queue.name, :mandatory => true)
76
+ exchange.publish(message, :routing_key => queue.name, :immediate => true, :mandatory => true)
77
77
  end
78
78
  end
79
79
  end
@@ -53,7 +53,8 @@ describe "Exclusive server-named queue" do
53
53
  :reply_to => queue2.name,
54
54
  :app_id => "Client",
55
55
  :timestamp => request_timestamp,
56
- :mandatory => true)
56
+ :mandatory => true,
57
+ :immediate => true)
57
58
 
58
59
  done(0.2) {
59
60
  queue1.unsubscribe
@@ -53,16 +53,19 @@ describe "Store-and-forward routing" do
53
53
  @queue.subscribe(:ack => false) do |payload|
54
54
  payload.should_not be_nil
55
55
  number_of_received_messages += 1
56
- payload.should == dispatched_data
56
+ if RUBY_VERSION =~ /^1.9/
57
+ payload.force_encoding("UTF-8").should == dispatched_data
58
+ else
59
+ payload.should == dispatched_data
60
+ end
57
61
  end # subscribe
58
62
 
59
- delayed(0.3) do
60
- expected_number_of_messages.times do
61
- @exchange.publish(dispatched_data, :routing_key => @queue_name)
62
- end
63
+ expected_number_of_messages.times do
64
+ @exchange.publish(dispatched_data, :routing_key => @queue_name)
63
65
  end
64
66
 
65
- done(4.0) {
67
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
68
+ done(6.0) {
66
69
  number_of_received_messages.should == expected_number_of_messages
67
70
  @queue.unsubscribe
68
71
  }
@@ -66,9 +66,9 @@ describe "Topic-based subscription" do
66
66
  # publish some messages none of our queues should be receiving
67
67
  3.times do
68
68
  @exchange.publish(626 + rand(1000)/400.0, :key => "nasdaq.goog")
69
- end
69
+ end # do
70
70
 
71
- done(0.5) {
71
+ done(0.4) {
72
72
  received_messages.should == expected_messages
73
73
  @aapl_queue.unsubscribe
74
74
  @amzn_queue.unsubscribe
@@ -117,7 +117,7 @@ describe "Topic-based subscription" do
117
117
  @exchange.publish("Blatche, Wall lead Wizards over Jazz 108-101", :key => "sports.nba.jazz")
118
118
  @exchange.publish("Deron Williams Receives NBA Cares Community Assist Award", :key => "sports.nba.jazz")
119
119
 
120
- done(0.6) {
120
+ done(0.2) {
121
121
  received_messages.should == expected_messages
122
122
 
123
123
  @nba_queue.unsubscribe
@@ -151,7 +151,6 @@ describe "Topic-based subscription" do
151
151
  @celtics_queue.name => 3
152
152
  }
153
153
 
154
-
155
154
  @sports_queue.bind(@exchange, :key => "sports.#").subscribe do |payload|
156
155
  received_messages[@sports_queue.name] += 1
157
156
  end
@@ -181,7 +180,7 @@ describe "Topic-based subscription" do
181
180
  @exchange.publish("Philadelphia's Daniel Briere has been named as an All-Star replacement for Jarome Iginla.", :key => "sports.nhl.allstargame")
182
181
  @exchange.publish("Devils blank Sid- and Malkin-less Penguins 2-0", :key => "sports.nhl.penguins")
183
182
 
184
- done(0.5) {
183
+ done(0.2) {
185
184
  received_messages.should == expected_messages
186
185
 
187
186
  @sports_queue.unsubscribe
@@ -15,12 +15,6 @@ puts "Using Ruby #{RUBY_VERSION}, amq-client #{AMQ::Client::VERSION} and amq-pro
15
15
 
16
16
  amqp_config = File.dirname(__FILE__) + '/amqp.yml'
17
17
 
18
- port = if ENV["TRACER"]
19
- 5673
20
- else
21
- 5672
22
- end
23
-
24
18
  if File.exists? amqp_config
25
19
  class Hash
26
20
  def symbolize_keys
@@ -34,7 +28,7 @@ if File.exists? amqp_config
34
28
  end
35
29
  AMQP_OPTS = YAML::load_file(amqp_config).symbolize_keys[:test]
36
30
  else
37
- AMQP_OPTS = {:host => 'localhost', :port => port}
31
+ AMQP_OPTS = {:host => 'localhost', :port => 5672}
38
32
  end
39
33
 
40
34
  # puts "AMQP_OPTS = #{AMQP_OPTS.inspect}"
@@ -88,4 +82,4 @@ module PlatformDetection
88
82
  def rubinius?
89
83
  defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'rbx')
90
84
  end
91
- end
85
+ end