amqp 0.8.0.rc12 → 0.8.0.rc13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/.travis.yml +1 -1
  2. data/.yardopts +2 -1
  3. data/CONTRIBUTORS +29 -22
  4. data/Gemfile +2 -1
  5. data/README.md +241 -0
  6. data/amqp.gemspec +7 -5
  7. data/bin/set_test_suite_realms_up.sh +6 -6
  8. data/docs/08Migration.textile +3 -1
  9. data/docs/Bindings.textile +3 -1
  10. data/docs/Clustering.textile +4 -0
  11. data/docs/ConnectingToTheBroker.textile +108 -86
  12. data/docs/ConnectionEncryptionWithTLS.textile +3 -1
  13. data/docs/DocumentationGuidesIndex.textile +24 -2
  14. data/docs/Durability.textile +22 -1
  15. data/docs/ErrorHandling.textile +21 -1
  16. data/docs/Exchanges.textile +181 -9
  17. data/docs/GettingStarted.textile +65 -167
  18. data/docs/Queues.textile +400 -355
  19. data/docs/RabbitMQVersions.textile +34 -3
  20. data/docs/Routing.textile +4 -1
  21. data/docs/RunningTests.textile +116 -0
  22. data/docs/Troubleshooting.textile +131 -0
  23. data/docs/VendorSpecificExtensions.textile +20 -0
  24. data/examples/channels/qos_aka_prefetch.rb +3 -3
  25. data/examples/channels/qos_aka_prefetch_without_callback.rb +2 -2
  26. data/examples/error_handling/channel_level_exception.rb +1 -1
  27. data/examples/error_handling/channel_level_exception_with_multiple_channels_involved.rb +1 -0
  28. data/examples/error_handling/connection_level_exception.rb +26 -0
  29. data/examples/error_handling/global_channel_level_exception_handler.rb +3 -3
  30. data/examples/exchanges/autodeletion_of_exchanges.rb +37 -0
  31. data/examples/extensions/rabbitmq/per_queue_message_ttl.rb +3 -3
  32. data/examples/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +2 -2
  33. data/examples/guides/getting_started/{03_babblr.rb → 03_blabbr.rb} +0 -0
  34. data/examples/guides/queues/01a_declaring_a_server_named_queue_using_queue_constructor.rb +18 -0
  35. data/examples/guides/queues/01b_declaring_a_queue_using_queue_constructor.rb +18 -0
  36. data/examples/guides/queues/02a_declaring_a_durable_shared_queue.rb +18 -0
  37. data/examples/guides/queues/02b_declaring_a_durable_shared_queue.rb +18 -0
  38. data/examples/guides/queues/03a_declaring_a_temporary_exclusive_queue.rb +19 -0
  39. data/examples/guides/queues/03b_declaring_a_temporary_exclusive_queue.rb +18 -0
  40. data/examples/guides/queues/{05_binding_a_queue_using_exchange_instance.rb → 04_bind_a_queue_using_exchange_instance.rb} +2 -2
  41. data/examples/guides/queues/{06_biding_a_queue_using_exchange_name_string.rb → 05_bind_a_queue_using_exchange_name.rb} +2 -2
  42. data/examples/guides/queues/{07_subscribing_to_receive_messages.rb → 06_subscribe_to_receive_messages.rb} +1 -1
  43. data/examples/guides/queues/{08_poll_for_messages.rb → 07_fetch_a_message_from_the_queue.rb} +1 -1
  44. data/examples/guides/queues/{09_unsubscribing_a_consumer.rb → 08_unsubscribing_a_consumer.rb} +0 -0
  45. data/examples/guides/queues/{10_unbinding_from_exchange.rb → 09_unbinding_from_exchange.rb} +2 -2
  46. data/examples/guides/queues/{11_purge_a_queue.rb → 10_purge_a_queue.rb} +2 -2
  47. data/examples/guides/queues/{12_deleting_a_queue.rb → 11_deleting_a_queue.rb} +2 -2
  48. data/examples/hello_world.rb +1 -1
  49. data/examples/hello_world_with_an_empty_string.rb +33 -0
  50. data/examples/issues/amq_client_issue_7.rb +31 -0
  51. data/examples/issues/amq_protocol_issue_14.rb +46 -0
  52. data/examples/issues/issue_75.rb +23 -0
  53. data/examples/issues/issue_79.rb +35 -0
  54. data/examples/issues/issue_80.rb +40 -0
  55. data/examples/publishing/{publishing_and_immediately_stopping_event_loop.rb → publishing_a_one_off_message.rb} +9 -12
  56. data/examples/publishing/publishing_callback.rb +52 -0
  57. data/examples/publishing/returned_messages.rb +3 -3
  58. data/examples/queues/accessing_message_metadata.rb +60 -0
  59. data/examples/queues/queue_status.rb +0 -7
  60. data/examples/queues/rejecting_messages_without_requeueuing.rb +47 -0
  61. data/examples/queues/using_explicit_acknowledgements.rb +96 -0
  62. data/examples/routing/headers_routing.rb +54 -0
  63. data/lib/amqp/channel.rb +245 -40
  64. data/lib/amqp/client.rb +23 -11
  65. data/lib/amqp/exchange.rb +58 -41
  66. data/lib/amqp/queue.rb +66 -13
  67. data/lib/amqp/version.rb +1 -1
  68. data/spec/integration/authentication_spec.rb +5 -5
  69. data/spec/integration/basic_get_spec.rb +1 -1
  70. data/spec/integration/channel_close_spec.rb +10 -3
  71. data/spec/integration/queue_declaration_spec.rb +26 -5
  72. data/spec/integration/topic_subscription_spec.rb +1 -1
  73. data/spec/unit/amqp/client_spec.rb +7 -54
  74. data/tasks.rb +1 -8
  75. metadata +64 -23
  76. data/README.textile +0 -229
@@ -8,12 +8,6 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
8
 
9
9
  require 'amqp'
10
10
 
11
- if RUBY_VERSION == "1.8.7"
12
- class Array
13
- alias sample choice
14
- end
15
- end
16
-
17
11
  puts "=> Publishing and immediately stopping the event loop in the callback"
18
12
  puts
19
13
 
@@ -23,15 +17,18 @@ puts
23
17
  # http://bit.ly/lQP1Al
24
18
 
25
19
  EventMachine.run do
26
- client = AMQP.connect(:host => '127.0.0.1')
27
- channel = AMQP::Channel.new(client)
28
- channel.on_error { puts 'channel error'; EM.stop }
20
+ connection = AMQP.connect(:host => '127.0.0.1')
21
+ channel = AMQP::Channel.new(connection)
22
+ channel.on_error do |ch, channel_close|
23
+ puts "Channel-level error: #{channel_close.reply_text}, shutting down..."
24
+ connection.close { EventMachine.stop }
25
+ end
29
26
 
30
27
  queue = channel.queue("some_topic", :auto_delete => true)
31
28
  exchange = channel.topic("foo", :durable => true, :auto_delete => true)
32
29
 
33
- exchange.publish( 'hello world', :routing_key => "some_topic", :persistent => true, :nowait => false ) do
34
- puts 'enqueued message for publishing on next event loop tick'
35
- EventMachine.stop
30
+ exchange.publish('hello world', :routing_key => "some_topic", :persistent => true, :nowait => false ) do
31
+ puts 'About to disconnect'
32
+ connection.close { EventMachine.stop }
36
33
  end
37
34
  end
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'amqp'
10
+
11
+ puts "=> Using a callback to #publish. It is run on the _next_ EventMachine loop run."
12
+ puts
13
+
14
+ EventMachine.run do
15
+ connection = AMQP.connect(:host => '127.0.0.1')
16
+ channel = AMQP::Channel.new(connection)
17
+ channel.on_error do |ch, channel_close|
18
+ puts "Channel-level error: #{channel_close.reply_text}, shutting down..."
19
+ connection.close { EventMachine.stop }
20
+ end
21
+
22
+ queue = channel.queue("amqpgem.examples.publishing.queue1", :auto_delete => true)
23
+ exchange = channel.fanout("amqpgem.examples.topic", :durable => true, :auto_delete => true)
24
+
25
+ queue.bind(exchange, :routing_key => "some_topic")
26
+
27
+
28
+ # Don't be deceived: this callback is run on the next event loop tick. There is no guarantee that your
29
+ # data was sent: there is buffering going on on multiple layers (C++ core of EventMachine, libc functions,
30
+ # kernel uses buffering for many I/O system calls).
31
+ #
32
+ # This callback is simply for convenience. In a distributed environment, the only way to know when your data
33
+ # is sent is when you receive an acknowledgement. TCP works that way. MK.
34
+
35
+ 100.times do |i|
36
+ exchange.publish("hello world #{i}", :routing_key => "some_topic", :persistent => true) do
37
+ puts "Callback #{i} has fired"
38
+ end
39
+ end
40
+
41
+ exchange.publish("hello world 101", :routing_key => "some_topic", :persistent => false) do
42
+ puts "Callback 101 has fired"
43
+ end
44
+
45
+ exchange.publish("hello world 102", :routing_key => "some_topic", :persistent => true) do
46
+ puts "Callback 102 has fired"
47
+ end
48
+
49
+ EventMachine.add_timer(1) do
50
+ connection.close { EventMachine.stop }
51
+ end
52
+ end
@@ -19,17 +19,17 @@ puts
19
19
 
20
20
  AMQP.start(:host => '127.0.0.1') do |connection|
21
21
  channel = AMQP.channel
22
- channel.on_error { EM.stop; raise 'channel error' }
22
+ channel.on_error { |ch, channel_close| EventMachine.stop; raise "channel error: #{channel_close.reply_text}" }
23
23
 
24
24
  exchange = channel.fanout("amq.fanout")
25
- exchange.on_return do |basic_return, header, payload|
25
+ exchange.on_return do |basic_return, metadata, payload|
26
26
  puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
27
27
  end
28
28
 
29
29
  EventMachine.add_timer(0.3) {
30
30
  10.times do |i|
31
31
  exchange.publish("Message ##{i}", :immediate => true)
32
- end
32
+ end
33
33
  }
34
34
 
35
35
  EventMachine.add_timer(2) {
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'amqp'
10
+
11
+ EventMachine.run do
12
+ connection = AMQP.connect(:host => '127.0.0.1')
13
+ puts "Connected to AMQP broker. Running #{AMQP::VERSION} version of the gem..."
14
+
15
+ channel = AMQP::Channel.new(connection)
16
+ queue = channel.queue("amqpgem.examples.hello_world", :auto_delete => true)
17
+ exchange = channel.direct("amq.direct")
18
+
19
+ queue.bind(exchange)
20
+
21
+ channel.on_error do |ch, channel_close|
22
+ puts channel_close.reply_text
23
+ connection.close { EventMachine.stop }
24
+ end
25
+
26
+ queue.subscribe do |metadata, payload|
27
+ puts "metadata.routing_key : #{metadata.routing_key}"
28
+ puts "metadata.content_type: #{metadata.content_type}"
29
+ puts "metadata.priority : #{metadata.priority}"
30
+ puts "metadata.headers : #{metadata.headers.inspect}"
31
+ puts "metadata.timestamp : #{metadata.timestamp.inspect}"
32
+ puts "metadata.type : #{metadata.type}"
33
+ puts "metadata.delivery_tag: #{metadata.delivery_tag}"
34
+ puts "metadata.redelivered : #{metadata.redelivered}"
35
+
36
+ puts "metadata.app_id : #{metadata.app_id}"
37
+ puts "metadata.exchange : #{metadata.exchange}"
38
+ puts
39
+ puts "Received a message: #{payload}. Disconnecting..."
40
+
41
+ connection.close {
42
+ EventMachine.stop { exit }
43
+ }
44
+ end
45
+
46
+ exchange.publish("Hello, world!",
47
+ :app_id => "amqpgem.example",
48
+ :priority => 8,
49
+ :type => "kinda.checkin",
50
+ # headers table keys can be anything
51
+ :headers => {
52
+ :coordinates => {
53
+ :latitude => 59.35,
54
+ :longitude => 18.066667
55
+ },
56
+ :participants => 11,
57
+ :venue => "Stockholm"
58
+ },
59
+ :timestamp => Time.now.to_i)
60
+ end
@@ -8,13 +8,6 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
8
 
9
9
  require 'amqp'
10
10
 
11
- if RUBY_VERSION == "1.8.7"
12
- class Array
13
- alias sample choice
14
- end
15
- end
16
-
17
-
18
11
  puts "=> Queue#status example"
19
12
  puts
20
13
  AMQP.start(:host => 'localhost') do |connection|
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'amqp'
10
+
11
+ if RUBY_VERSION == "1.8.7"
12
+ class Array
13
+ alias sample choice
14
+ end
15
+ end
16
+
17
+
18
+ puts "=> Queue#status example"
19
+ puts
20
+ AMQP.start(:host => 'localhost') do |connection|
21
+ channel = AMQP::Channel.new(connection)
22
+
23
+ exchange = channel.fanout("amqpgem.integration.queue.status.fanout", :auto_delete => true)
24
+ queue = channel.queue("amqpgem.integration.queue.status.queue", :auto_delete => true)
25
+
26
+ queue.bind(exchange).subscribe do |metadata, payload|
27
+ puts "Rejecting #{payload}"
28
+ channel.reject(metadata.delivery_tag)
29
+ end
30
+
31
+ 100.times do |i|
32
+ print "."
33
+ exchange.publish(Time.now.to_i.to_s + "_#{i}", :key => queue.name)
34
+ end
35
+ $stdout.flush
36
+
37
+
38
+ show_stopper = Proc.new do
39
+ $stdout.puts "Stopping..."
40
+ connection.close {
41
+ EventMachine.stop { exit }
42
+ }
43
+ end
44
+
45
+ Signal.trap "INT", show_stopper
46
+ EventMachine.add_timer(2, show_stopper)
47
+ end
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'amqp'
10
+
11
+ puts "=> Subscribing for messages using explicit acknowledgements model"
12
+ puts
13
+
14
+ # this example uses Kernel#sleep and thus we must run EventMachine reactor in
15
+ # a separate thread, or nothing will be sent/received while we sleep() on the current thread.
16
+ t = Thread.new { EventMachine.run }
17
+ sleep(0.5)
18
+
19
+ # open two connections to imitate two apps
20
+ connection1 = AMQP.connect
21
+ connection2 = AMQP.connect
22
+ connection3 = AMQP.connect
23
+
24
+ channel_exception_handler = Proc.new { |ch, channel_close| EventMachine.stop; raise "channel error: #{channel_close.reply_text}" }
25
+
26
+ # open two channels
27
+ channel1 = AMQP::Channel.new(connection1)
28
+ channel1.on_error(&channel_exception_handler)
29
+ # first app will be given up to 3 messages at a time. If it doesn't
30
+ # ack any messages after it was delivered 3, messages will be routed to
31
+ # the app #2.
32
+ channel1.prefetch(3)
33
+
34
+ channel2 = AMQP::Channel.new(connection2)
35
+ channel2.on_error(&channel_exception_handler)
36
+ # app #2 processes messages one-by-one and has to send and ack every time
37
+ channel2.prefetch(1)
38
+
39
+ # app 3 will just publish messages
40
+ channel3 = AMQP::Channel.new(connection3)
41
+ channel3.on_error(&channel_exception_handler)
42
+
43
+ exchange = channel3.direct("amq.direct")
44
+
45
+ queue1 = channel1.queue("amqpgem.examples.acknowledgements.explicit", :auto_delete => false)
46
+ # purge the queue so that we don't get any redeliveries from previous runs
47
+ queue1.purge
48
+ queue1.bind(exchange).subscribe(:ack => true) do |metadata, payload|
49
+ # do some work
50
+ sleep(0.2)
51
+
52
+ # acknowledge some messages, they will be removed from the queue
53
+ if rand > 0.5
54
+ # FYI: there is a shortcut, metadata.ack
55
+ channel1.acknowledge(metadata.delivery_tag, false)
56
+ puts "[consumer1] Got message ##{metadata.headers['i']}, ack-ed"
57
+ else
58
+ # some messages are not ack-ed and will remain in the queue for redelivery
59
+ # when app #1 connection is closed (either properly or due to a crash)
60
+ puts "[consumer1] Got message ##{metadata.headers['i']}, SKIPPPED"
61
+ end
62
+ end
63
+
64
+ queue2 = channel2.queue!("amqpgem.examples.acknowledgements.explicit", :auto_delete => false)
65
+ queue2.subscribe(:ack => true) do |metadata, payload|
66
+ metadata.ack
67
+ # app 2 always acks messages
68
+ puts "[consumer2] Received #{payload}, redelivered = #{metadata.redelivered}, ack-ed"
69
+ end
70
+
71
+ # after some time one of the consumers quits/crashes
72
+ EventMachine.add_timer(4.0) {
73
+ connection1.close
74
+ puts "----- Connection 1 is now closed (we pretend that it has crashed) -----"
75
+ }
76
+
77
+ EventMachine.add_timer(10.0) do
78
+ # purge the queue so that we don't get any redeliveries on the next run
79
+ queue2.purge {
80
+ connection2.close {
81
+ connection3.close { EventMachine.stop }
82
+ }
83
+ }
84
+ end
85
+
86
+
87
+ i = 0
88
+ EventMachine.add_periodic_timer(0.8) {
89
+ 3.times do
90
+ exchange.publish("Message ##{i}", :headers => { :i => i })
91
+ i += 1
92
+ end
93
+ }
94
+
95
+
96
+ t.join
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'amqp'
10
+
11
+ puts "=> Headers routing example"
12
+ puts
13
+ AMQP.start do |connection|
14
+ channel = AMQP::Channel.new(connection)
15
+ channel.on_error do |ch, channel_close|
16
+ puts "A channel-level exception: #{channel_close.inspect}"
17
+ end
18
+
19
+ exchange = channel.headers("amq.match", :durable => true)
20
+
21
+ channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'all', :arch => "x64", :os => 'linux' }).subscribe do |metadata, payload|
22
+ puts "[linux/x64] Got a message: #{payload}"
23
+ end
24
+ channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'all', :arch => "x32", :os => 'linux' }).subscribe do |metadata, payload|
25
+ puts "[linux/x32] Got a message: #{payload}"
26
+ end
27
+ channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'any', :os => 'linux', :arch => "__any__" }).subscribe do |metadata, payload|
28
+ puts "[linux] Got a message: #{payload}"
29
+ end
30
+ channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'any', :os => 'macosx', :cores => 8 }).subscribe do |metadata, payload|
31
+ puts "[macosx|octocore] Got a message: #{payload}"
32
+ end
33
+
34
+
35
+ EventMachine.add_timer(0.5) do
36
+ exchange.publish "For linux/x64", :headers => { :arch => "x64", :os => 'linux' }
37
+ exchange.publish "For linux/x32", :headers => { :arch => "x32", :os => 'linux' }
38
+ exchange.publish "For linux", :headers => { :os => 'linux' }
39
+ exchange.publish "For OS X", :headers => { :os => 'macosx' }
40
+ exchange.publish "For solaris/x64", :headers => { :os => 'solaris', :arch => 'x64' }
41
+ exchange.publish "For ocotocore", :headers => { :cores => 8 }
42
+ end
43
+
44
+
45
+ show_stopper = Proc.new do
46
+ $stdout.puts "Stopping..."
47
+ connection.close {
48
+ EventMachine.stop { exit }
49
+ }
50
+ end
51
+
52
+ Signal.trap "INT", show_stopper
53
+ EventMachine.add_timer(2, show_stopper)
54
+ end
@@ -205,6 +205,7 @@ module AMQP
205
205
  def initialize(connection = nil, id = self.class.next_channel_id, options = {}, &block)
206
206
  raise 'AMQP can only be used from within EM.run {}' unless EM.reactor_running?
207
207
 
208
+ @options = options
208
209
  @connection = connection || AMQP.connection || AMQP.start
209
210
 
210
211
  super(@connection, id)
@@ -241,20 +242,10 @@ module AMQP
241
242
  end # @connection.on_open
242
243
  end
243
244
 
244
- # Takes a block that will be deferred till the moment when channel is considered open
245
- # (channel.open-ok is received from the broker). If you need to delay an operation
246
- # till the moment channel is open, this method is what you are looking for.
247
- #
248
- # Multiple callbacks are supported. If when this moment is called, channel is already
249
- # open, block is executed immediately.
250
- #
251
- # @api public
252
- def once_open(&block)
253
- @channel_is_open_deferrable.callback(&block)
254
- end # once_open(&block)
255
- alias once_opened once_open
256
245
 
257
246
 
247
+ # @group Declaring exchanges
248
+
258
249
  # Defines, intializes and returns a direct Exchange instance.
259
250
  #
260
251
  # Learn more about direct exchanges in {Exchange Exchange class documentation}.
@@ -336,6 +327,7 @@ module AMQP
336
327
 
337
328
  validate_parameters_match!(exchange, extended_opts)
338
329
 
330
+ block.call(exchange) if block
339
331
  exchange
340
332
  else
341
333
  register_exchange(Exchange.new(self, :direct, name, opts, &block))
@@ -443,6 +435,7 @@ module AMQP
443
435
 
444
436
  validate_parameters_match!(exchange, extended_opts)
445
437
 
438
+ block.call(exchange) if block
446
439
  exchange
447
440
  else
448
441
  register_exchange(Exchange.new(self, :fanout, name, opts, &block))
@@ -558,6 +551,7 @@ module AMQP
558
551
 
559
552
  validate_parameters_match!(exchange, extended_opts)
560
553
 
554
+ block.call(exchange) if block
561
555
  exchange
562
556
  else
563
557
  register_exchange(Exchange.new(self, :topic, name, opts, &block))
@@ -601,9 +595,53 @@ module AMQP
601
595
  # @raise [AMQP::Error] Raised when exchange is declared with :passive => true and the exchange does not exist.
602
596
  #
603
597
  #
604
- # @example Using fanout exchange to deliver messages to multiple consumers
598
+ # @example Using headers exchange to route messages based on multiple attributes (OS, architecture, # of cores)
599
+ #
600
+ # puts "=> Headers routing example"
601
+ # puts
602
+ # AMQP.start do |connection|
603
+ # channel = AMQP::Channel.new(connection)
604
+ # channel.on_error do |ch, channel_close|
605
+ # puts "A channel-level exception: #{channel_close.inspect}"
606
+ # end
607
+ #
608
+ # exchange = channel.headers("amq.match", :durable => true)
609
+ #
610
+ # channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'all', :arch => "x64", :os => 'linux' }).subscribe do |metadata, payload|
611
+ # puts "[linux/x64] Got a message: #{payload}"
612
+ # end
613
+ # channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'all', :arch => "x32", :os => 'linux' }).subscribe do |metadata, payload|
614
+ # puts "[linux/x32] Got a message: #{payload}"
615
+ # end
616
+ # channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'any', :os => 'linux', :arch => "__any__" }).subscribe do |metadata, payload|
617
+ # puts "[linux] Got a message: #{payload}"
618
+ # end
619
+ # channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'any', :os => 'macosx', :cores => 8 }).subscribe do |metadata, payload|
620
+ # puts "[macosx|octocore] Got a message: #{payload}"
621
+ # end
622
+ #
623
+ #
624
+ # EventMachine.add_timer(0.5) do
625
+ # exchange.publish "For linux/x64", :headers => { :arch => "x64", :os => 'linux' }
626
+ # exchange.publish "For linux/x32", :headers => { :arch => "x32", :os => 'linux' }
627
+ # exchange.publish "For linux", :headers => { :os => 'linux' }
628
+ # exchange.publish "For OS X", :headers => { :os => 'macosx' }
629
+ # exchange.publish "For solaris/x64", :headers => { :os => 'solaris', :arch => 'x64' }
630
+ # exchange.publish "For ocotocore", :headers => { :cores => 8 }
631
+ # end
632
+ #
633
+ #
634
+ # show_stopper = Proc.new do
635
+ # $stdout.puts "Stopping..."
636
+ # connection.close {
637
+ # EventMachine.stop { exit }
638
+ # }
639
+ # end
640
+ #
641
+ # Signal.trap "INT", show_stopper
642
+ # EventMachine.add_timer(2, show_stopper)
643
+ # end
605
644
  #
606
- # # TODO
607
645
  #
608
646
  #
609
647
  # @see Exchange
@@ -619,12 +657,18 @@ module AMQP
619
657
 
620
658
  validate_parameters_match!(exchange, extended_opts)
621
659
 
660
+ block.call(exchange) if block
622
661
  exchange
623
662
  else
624
663
  register_exchange(Exchange.new(self, :headers, name, opts, &block))
625
664
  end
626
665
  end
627
666
 
667
+ # @endgroup
668
+
669
+
670
+ # @group Declaring queues
671
+
628
672
 
629
673
  # Declares and returns a Queue instance associated with this channel. See {Queue Queue class documentation} for
630
674
  # more information about queues.
@@ -714,19 +758,13 @@ module AMQP
714
758
 
715
759
  validate_parameters_match!(queue, extended_opts)
716
760
 
761
+ block.call(queue) if block
717
762
  queue
718
763
  else
719
764
  self.queue!(name, opts, &block)
720
765
  end
721
766
  end
722
767
 
723
- # Returns true if channel is not closed.
724
- # @return [Boolean]
725
- # @api public
726
- def open?
727
- self.status == :opened || self.status == :opening
728
- end # open?
729
-
730
768
  # Same as {Channel#queue} but when queue with the same name already exists in this channel
731
769
  # object's cache, this method will replace existing queue with a newly defined one. Consider
732
770
  # using {Channel#queue} instead.
@@ -753,6 +791,9 @@ module AMQP
753
791
  register_queue(queue)
754
792
  end
755
793
 
794
+ # @endgroup
795
+
796
+
756
797
 
757
798
  # Instantiates and returns an RPC instance associated with this channel.
758
799
  #
@@ -763,8 +804,8 @@ module AMQP
763
804
  #
764
805
  # Marshalling and unmarshalling the objects is handled internally. This
765
806
  # marshalling is subject to the same restrictions as defined in the
766
- # Marshal[http://ruby-doc.org/core/classes/Marshal.html] standard
767
- # library. See that documentation for further reference.
807
+ # [http://ruby-doc.org/core/classes/Marshal.html Marshal module} in the Ruby standard
808
+ # library.
768
809
  #
769
810
  # When the optional object is not passed, the returned rpc reference is
770
811
  # used to send messages and arguments to the queue. See {AMQP::RPC#method_missing}
@@ -787,6 +828,82 @@ module AMQP
787
828
 
788
829
 
789
830
 
831
+ # Returns a hash of all rpc proxy objects.
832
+ #
833
+ # Most of the time, this method is not
834
+ # called by application code.
835
+ # @api plugin
836
+ def rpcs
837
+ @rpcs.values
838
+ end
839
+
840
+
841
+
842
+ # @group Channel lifecycle
843
+
844
+ # Opens AMQP channel.
845
+ #
846
+ # @note Instantiated channels are opened by default. This method should only be used for error recovery after network connection loss.
847
+ # @api public
848
+ def open(&block)
849
+ super(&block)
850
+ end
851
+
852
+ # @return [Boolean] true if channel is not closed.
853
+ # @api public
854
+ def open?
855
+ self.status == :opened || self.status == :opening
856
+ end # open?
857
+
858
+ # Takes a block that will be deferred till the moment when channel is considered open
859
+ # (channel.open-ok is received from the broker). If you need to delay an operation
860
+ # till the moment channel is open, this method is what you are looking for.
861
+ #
862
+ # Multiple callbacks are supported. If when this moment is called, channel is already
863
+ # open, block is executed immediately.
864
+ #
865
+ # @api public
866
+ def once_open(&block)
867
+ @channel_is_open_deferrable.callback(&block)
868
+ end # once_open(&block)
869
+ alias once_opened once_open
870
+
871
+ # Closes AMQP channel.
872
+ #
873
+ # @api public
874
+ def close(reply_code = 200, reply_text = DEFAULT_REPLY_TEXT, class_id = 0, method_id = 0, &block)
875
+ super(reply_code, reply_text, class_id, method_id, &block)
876
+ end
877
+
878
+ # @endgroup
879
+
880
+
881
+
882
+
883
+ # @group QoS and flow handling
884
+
885
+ # Asks the peer to pause or restart the flow of content data sent to a consumer.
886
+ # This is a simple flow­control mechanism that a peer can use to avoid overflowing its
887
+ # queues or otherwise finding itself receiving more messages than it can process. Note that
888
+ # this method is not intended for window control. It does not affect contents returned to
889
+ # Queue#get callers.
890
+ #
891
+ # @param [Boolean] Desired flow state.
892
+ #
893
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.5.2.3.)
894
+ # @api public
895
+ def flow(active = false, &block)
896
+ super(active, &block)
897
+ end
898
+
899
+ # @return [Boolean] True if flow in this channel is active (messages will be delivered to consumers that use this channel).
900
+ #
901
+ # @api public
902
+ def flow_is_active?
903
+ @flow_is_active
904
+ end # flow_is_active?
905
+
906
+
790
907
 
791
908
  # @param [Fixnum] Message count
792
909
  # @param [Boolean] global (false)
@@ -803,18 +920,106 @@ module AMQP
803
920
  self
804
921
  end
805
922
 
923
+ # @endgroup
806
924
 
807
925
 
808
- # Returns a hash of all rpc proxy objects.
926
+
927
+ # @group Message acknowledgements
928
+
929
+ # Acknowledge one or all messages on the channel.
809
930
  #
810
- # Most of the time, this method is not
811
- # called by application code.
812
- # @api plugin
813
- def rpcs
814
- @rpcs.values
931
+ # @api public
932
+ # @see #reject
933
+ # @see #recover
934
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
935
+ def acknowledge(delivery_tag, multiple = false)
936
+ super(delivery_tag, multiple)
937
+ end # acknowledge(delivery_tag, multiple = false)
938
+
939
+ # Reject a message with given delivery tag.
940
+ #
941
+ # @api public
942
+ # @see #acknowledge
943
+ # @see #recover
944
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
945
+ def reject(delivery_tag, requeue = true)
946
+ super(delivery_tag, requeue)
947
+ end # reject(delivery_tag, requeue = true)
948
+
949
+ # Notifies AMQ broker that consumer has recovered and unacknowledged messages need
950
+ # to be redelivered.
951
+ #
952
+ # @return [Channel] self
953
+ #
954
+ # @note RabbitMQ as of 2.3.1 does not support basic.recover with requeue = false.
955
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.16.)
956
+ # @see #acknowledge
957
+ # @api public
958
+ def recover(requeue = true, &block)
959
+ super(requeue, &block)
960
+ end # recover(requeue = false, &block)
961
+
962
+ # @endgroup
963
+
964
+
965
+
966
+
967
+ # @group Transactions
968
+
969
+ # Sets the channel to use standard transactions. One must use this method at least
970
+ # once on a channel before using #tx_tommit or tx_rollback methods.
971
+ #
972
+ # @api public
973
+ def tx_select(&block)
974
+ super(&block)
975
+ end # tx_select(&block)
976
+
977
+ # Commits AMQP transaction.
978
+ #
979
+ # @api public
980
+ def tx_commit(&block)
981
+ super(&block)
982
+ end # tx_commit(&block)
983
+
984
+ # Rolls AMQP transaction back.
985
+ #
986
+ # @api public
987
+ def tx_rollback(&block)
988
+ super(&block)
989
+ end # tx_rollback(&block)
990
+
991
+
992
+ # @endgroup
993
+
994
+
995
+
996
+
997
+
998
+ # @group Error handling
999
+
1000
+ # Defines a callback that will be executed when channel is closed after
1001
+ # channel-level exception.
1002
+ #
1003
+ # @api public
1004
+ def on_error(&block)
1005
+ super(&block)
815
1006
  end
816
1007
 
817
1008
 
1009
+ # Defines a global callback to be run on channel-level exception across
1010
+ # all channels. Consider using Channel#on_error instead. This method is here for sake
1011
+ # of backwards compatibility with 0.6.x and 0.7.x releases.
1012
+ # @see AMQP::Channel#on_error
1013
+ # @deprecated
1014
+ # @api public
1015
+ def self.on_error(&block)
1016
+ self.error(&block)
1017
+ end # self.on_error(&block)
1018
+
1019
+ # @endgroup
1020
+
1021
+
1022
+
818
1023
 
819
1024
  #
820
1025
  # Implementation
@@ -838,15 +1043,6 @@ module AMQP
838
1043
  end
839
1044
  end
840
1045
 
841
- # Defines a global callback to be run on channel-level exception across
842
- # all channels. Consider using Channel#on_error instead. This method is here for sake
843
- # of backwards compatibility with 0.6.x and 0.7.x releases.
844
- # @see AMQP::Channel#on_error
845
- # @deprecated
846
- # @api public
847
- def self.on_error(&block)
848
- self.error(&block)
849
- end # self.on_error(&block)
850
1046
 
851
1047
  # Overrides AMQ::Client::Channel version to also call global callback
852
1048
  # (if defined) for backwards compatibility.
@@ -867,12 +1063,19 @@ module AMQP
867
1063
  #
868
1064
  # @private
869
1065
  # @api plugin
870
- def reset
1066
+ def reset(&block)
871
1067
  # See AMQ::Client::Channel
872
1068
  self.reset_state!
873
1069
 
874
1070
  # there is no way to reset a deferrable; we have to use a new instance. MK.
875
1071
  @channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
1072
+ @channel_is_open_deferrable.callback(&block)
1073
+
1074
+ @connection.on_connection do
1075
+ @channel_is_open_deferrable.succeed
1076
+
1077
+ self.prefetch(@options[:prefetch], false) if @options[:prefetch]
1078
+ end
876
1079
  end
877
1080
 
878
1081
  # @private
@@ -930,8 +1133,9 @@ module AMQP
930
1133
  # Backwards compatibility with 0.6.x
931
1134
  #
932
1135
 
933
- # unique identifier
1136
+ # unique identifier of the default thread-local channel
934
1137
  # @deprecated
1138
+ # @private
935
1139
  def self.id
936
1140
  Thread.current[:mq_id] ||= "#{`hostname`.strip}-#{Process.pid}-#{Thread.current.object_id}"
937
1141
  end
@@ -956,6 +1160,7 @@ module AMQP
956
1160
 
957
1161
  # @private
958
1162
  def validate_parameters_match!(entity, parameters)
1163
+ parameters.delete(:no_declare)
959
1164
  unless entity.opts == parameters || parameters[:passive]
960
1165
  raise AMQP::IncompatibleOptionsError.new(entity.name, entity.opts, parameters)
961
1166
  end