amqp 0.8.0.rc2 → 0.8.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.gitignore +2 -3
  2. data/.travis.yml +5 -2
  3. data/.yardopts +2 -0
  4. data/CHANGELOG +17 -20
  5. data/Gemfile +7 -5
  6. data/README.textile +67 -29
  7. data/Rakefile +6 -0
  8. data/amqp.gemspec +5 -5
  9. data/docs/08Migration.textile +27 -0
  10. data/docs/Bindings.textile +27 -0
  11. data/docs/ConnectingToTheBroker.textile +277 -0
  12. data/docs/DocumentationGuidesIndex.textile +25 -0
  13. data/docs/Durability.textile +27 -0
  14. data/docs/ErrorHandling.textile +84 -0
  15. data/docs/Exchanges.textile +27 -0
  16. data/docs/GettingStarted.textile +585 -0
  17. data/docs/Queues.textile +27 -0
  18. data/docs/RabbitMQVersions.textile +12 -2
  19. data/docs/Routing.textile +27 -0
  20. data/docs/TLS.textile +27 -0
  21. data/docs/VendorSpecificExtensions.textile +11 -1
  22. data/examples/{various → channels}/open_channel_without_assignment.rb +0 -4
  23. data/examples/channels/prefetch_as_constructor_argument.rb +31 -0
  24. data/examples/channels/qos_aka_prefetch.rb +34 -0
  25. data/examples/channels/qos_aka_prefetch_without_callback.rb +32 -0
  26. data/examples/error_handling/channel_level_exception.rb +47 -0
  27. data/examples/error_handling/channel_level_exception_with_multiple_channels_involved.rb +54 -0
  28. data/examples/error_handling/connection_loss_handler.rb +39 -0
  29. data/examples/error_handling/global_channel_level_exception_handler.rb +65 -0
  30. data/examples/error_handling/handling_authentication_failure_with_a_callback.rb +33 -0
  31. data/examples/error_handling/tcp_connection_failure_handling_with_a_rescue_block.rb +30 -0
  32. data/examples/error_handling/tcp_connection_failure_with_a_callback.rb +28 -0
  33. data/examples/{various → exchanges}/declare_an_exchange_without_assignment.rb +0 -4
  34. data/examples/guides/getting_started/01_hello_world.rb +24 -0
  35. data/examples/guides/getting_started/02_hello_world_dslified.rb +23 -0
  36. data/examples/guides/getting_started/03_babblr.rb +33 -0
  37. data/examples/guides/getting_started/04_weathr.rb +56 -0
  38. data/examples/hello_world.rb +12 -13
  39. data/examples/hello_world_with_eventmachine_in_a_separate_thread.rb +37 -0
  40. data/examples/{various → legacy}/ack.rb +0 -0
  41. data/examples/{various → legacy}/callbacks.rb +0 -0
  42. data/examples/{various → legacy}/clock.rb +0 -0
  43. data/examples/{various → legacy}/hashtable.rb +0 -0
  44. data/examples/{various → legacy}/logger.rb +0 -0
  45. data/examples/{various → legacy}/multiclock.rb +0 -0
  46. data/examples/{various → legacy}/pingpong.rb +0 -2
  47. data/examples/{various → legacy}/primes-simple.rb +0 -0
  48. data/examples/{various → legacy}/primes.rb +0 -2
  49. data/examples/{various → legacy}/stocks.rb +0 -0
  50. data/examples/{various → queues}/automatic_binding_for_default_direct_exchange.rb +4 -0
  51. data/examples/{various → queues}/basic_get.rb +0 -2
  52. data/examples/{various → queues}/declare_a_queue_without_assignment.rb +0 -4
  53. data/examples/queues/declare_and_bind_a_server_named_queue.rb +43 -0
  54. data/examples/{various → queues}/queue_status.rb +3 -8
  55. data/examples/{various → routing}/pubsub.rb +0 -0
  56. data/examples/{various → routing}/weather_updates.rb +1 -1
  57. data/lib/amqp/channel.rb +231 -52
  58. data/lib/amqp/client.rb +6 -3
  59. data/lib/amqp/connection.rb +9 -10
  60. data/lib/amqp/deprecated/fork.rb +3 -3
  61. data/lib/amqp/deprecated/logger.rb +1 -0
  62. data/lib/amqp/deprecated/mq.rb +23 -1
  63. data/lib/amqp/deprecated/rpc.rb +1 -0
  64. data/lib/amqp/exceptions.rb +45 -3
  65. data/lib/amqp/exchange.rb +29 -35
  66. data/lib/amqp/ext/em.rb +0 -7
  67. data/lib/amqp/ext/emfork.rb +3 -2
  68. data/lib/amqp/header.rb +4 -0
  69. data/lib/amqp/queue.rb +96 -33
  70. data/lib/amqp/session.rb +140 -0
  71. data/lib/amqp/version.rb +6 -1
  72. data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +7 -7
  73. data/spec/integration/channel_level_exception_with_multiple_channels_spec.rb +69 -0
  74. data/spec/integration/declare_and_immediately_bind_a_server_named_queue_spec.rb +42 -0
  75. data/spec/integration/queue_declaration_spec.rb +8 -24
  76. data/spec/integration/queue_redeclaration_with_incompatible_attributes_spec.rb +43 -0
  77. data/spec/unit/amqp/connection_spec.rb +1 -1
  78. metadata +200 -182
  79. data/lib/amqp/basic_client.rb +0 -27
@@ -75,15 +75,16 @@ module AMQP
75
75
  @connection.closing?
76
76
  end
77
77
 
78
-
78
+ # @return [Boolean] Current global logging value
79
79
  # @api public
80
80
  def self.logging
81
81
  self.settings[:logging]
82
82
  end
83
83
 
84
+ # @return [Boolean] Sets current global logging value
84
85
  # @api public
85
86
  def self.logging=(value)
86
- self.settings[:logging] = !! value
87
+ self.settings[:logging] = !!value
87
88
  end
88
89
 
89
90
  # Default connection. When you do not pass connection instance to methods like
@@ -94,6 +95,7 @@ module AMQP
94
95
  @connection
95
96
  end
96
97
 
98
+ # Sets global connection object.
97
99
  # @api public
98
100
  def self.connection=(value)
99
101
  @connection = value
@@ -144,7 +146,7 @@ module AMQP
144
146
  # # ...
145
147
  # end
146
148
  # end
147
- #
149
+ #
148
150
  #
149
151
  # @overload connect(connection_string, options = {})
150
152
  # Used to pass connection parameters as a connection string
@@ -161,7 +163,7 @@ module AMQP
161
163
  # @option connection_options_or_string [String] :username ("guest") Username to use. Also can be specified as :user.
162
164
  # @option connection_options_or_string [String] :password ("guest") Password to use. Also can be specified as :pass.
163
165
  # @option connection_options_or_string [Hash] :ssl TLS (SSL) parameters to use.
164
- # @option connection_options_or_string [#call] :on_possible_authentication_failure A callable object that will be run if authentication fails (see Authentication failure section)
166
+ # @option connection_options_or_string [#call] :on_possible_authentication_failure A callable object that will be run if authentication fails (see Authentication failure section)
165
167
  #
166
168
  #
167
169
  # h2. Handling authentication failures
@@ -175,18 +177,15 @@ module AMQP
175
177
  # with :on_possible_authentication_failure option.
176
178
  #
177
179
  # @note This method assumes that EventMachine even loop is already running. If it is not the case or you are not sure, we recommend you use {AMQP.start} instead. It takes exactly the same parameters.
180
+ # @return [AMQP::Session]
178
181
  # @api public
179
182
  def self.connect(connection_options_or_string = {}, other_options = {}, &block)
180
183
  Client.connect(connection_options_or_string, other_options, &block)
181
184
  end
182
185
 
183
- # @return [Hash] If default connection is set up, returns settings it was set up with. Otherwise, returns default settings.
186
+ # @return [Hash] Default AMQP connection settings. This hash may be modified.
184
187
  # @api public
185
188
  def self.settings
186
- if @connection
187
- @connection.settings
188
- else
189
- AMQ::Client::Settings.default
190
- end
189
+ @settings ||= AMQ::Client::Settings.default
191
190
  end
192
191
  end # AMQP
@@ -2,7 +2,7 @@ require "amqp/ext/em"
2
2
 
3
3
  module AMQP
4
4
  # @deprecated
5
- # @api public
5
+ # @private
6
6
  def self.fork(workers)
7
7
  EM.fork(workers) do
8
8
  # clean up globals in the fork
@@ -11,5 +11,5 @@ module AMQP
11
11
 
12
12
  yield
13
13
  end
14
- end
15
- end
14
+ end
15
+ end
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module AMQP
4
+ # @private
4
5
  class Logger
5
6
  def initialize(*args, &block)
6
7
  opts = args.pop if args.last.is_a? Hash
@@ -17,4 +17,26 @@ class MQ
17
17
  # @note This class will be removed before 1.0 release.
18
18
  # @deprecated
19
19
  class Queue < ::AMQP::Queue; end
20
- end
20
+
21
+
22
+ #
23
+ # Backwards compatibility with 0.6.x
24
+ #
25
+
26
+ # unique identifier
27
+ def MQ.id
28
+ Thread.current[:mq_id] ||= "#{`hostname`.strip}-#{Process.pid}-#{Thread.current.object_id}"
29
+ end
30
+
31
+ # @private
32
+ def MQ.default
33
+ # TODO: clear this when connection is closed
34
+ Thread.current[:mq] ||= MQ.new
35
+ end
36
+
37
+ # Allows for calls to all MQ instance methods. This implicitly calls
38
+ # MQ.new so that a new channel is allocated for subsequent operations.
39
+ def MQ.method_missing(meth, *args, &blk)
40
+ MQ.default.__send__(meth, *args, &blk)
41
+ end
42
+ end
@@ -36,6 +36,7 @@ module AMQP
36
36
  #
37
37
  # @note This class will be removed before 1.0 release.
38
38
  # @deprecated
39
+ # @private
39
40
  class RPC < ::AMQP::BlankSlate
40
41
 
41
42
  #
@@ -1,18 +1,60 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module AMQP
4
- # Raised whenever an illegal operation is attempted.
5
- class Error < StandardError; end
4
+ # Base class for AMQP connection lifecycle exceptions.
5
+ # @api public
6
+ class Error < StandardError
7
+ # An exception in one of the underlying libraries that caused this
8
+ # exception to be re-thrown. May be nil.
9
+ attr_reader :cause
10
+ end
6
11
 
12
+
13
+ # All the exceptions below are new in 0.8.0. Previous versions
14
+ # used AMQP::Error for everything. We have to carry this baggage but
15
+ # there is a way out: simply subclass AMQP::Error and provide a backwards-compatible
16
+ # way of attaching root cause exception (like AMQ::Client::TCPConnectionFailed) instance
17
+ # to it.
18
+ #
19
+ # In other words: AMQP::Error is here to stay for a long time. MK.
20
+
21
+
22
+
23
+ # Raised when initial TCP connection to the broker fails.
24
+ # @api public
25
+ class TCPConnectionFailed < Error
26
+
27
+ #
28
+ # API
29
+ #
30
+
31
+ # @return [Hash] connection settings that were used
32
+ attr_reader :settings
33
+
34
+ def initialize(settings, cause = nil)
35
+ @settings = settings
36
+ @cause = cause
37
+
38
+ super("Could not estabilish TCP connection to #{@settings[:host]}:#{@settings[:port]}")
39
+ end # TCPConnectionFailed
40
+ end
41
+
42
+
43
+ # Raised when queue (or exchange) declaration fails because another queue with the same
44
+ # name but different attributes already exists in the channel object cache.
45
+ # @api public
7
46
  class IncompatibleOptionsError < Error
8
47
  def initialize(name, opts_1, opts_2)
9
48
  super("There is already an instance called #{name} with options #{opts_1.inspect}, you can't define the same instance with different options (#{opts_2.inspect})!")
10
49
  end
11
50
  end # IncompatibleOptionsError
12
51
 
52
+ # Raised on attempt to use a channel that was previously closed
53
+ # (either due to channel-level exception or intentionally via AMQP::Channel#close).
54
+ # @api public
13
55
  class ChannelClosedError < Error
14
56
  def initialize(instance)
15
- super("The channel #{instance.channel} was closed, you can't use it anymore!")
57
+ super("Channel with id = #{instance.channel} is closed, you can't use it anymore!")
16
58
  end
17
59
  end # ChannelClosedError
18
60
  end # AMQP
@@ -12,18 +12,10 @@ module AMQP
12
12
  # type and message attributes, determines how to deliver the message.
13
13
  #
14
14
  #
15
- # h2. Exchange types
16
- #
17
- # There are 4 supported exchange types: direct, fanout, topic and headers.
18
- #
19
- # As part of the standard, the server _must_ predeclare the direct exchange
20
- # 'amq.direct' and the fanout exchange 'amq.fanout'. All exchange names
21
- # starting with 'amq.' are reserved: attempts to declare an exchange using
22
- # 'amq.' as the name will raise an AMQP::Error and fail.
23
- #
24
- # Note that durability of exchanges and durability of messages published to exchanges
25
- # are different concepts. Sending messages to durable exchanges does not make
26
- # messages themselves persistent.
15
+ # Entities that forward messages to consumers (or consumers fetch messages
16
+ # from on demand) are called {Queue queues}. Exchanges are associated with
17
+ # queues via bindings. Roughly speaking, bindings determine messages placed
18
+ # in what exchange end up in what queues.
27
19
  #
28
20
  #
29
21
  # h2. AMQP bindings
@@ -36,16 +28,10 @@ module AMQP
36
28
  # queue.
37
29
  #
38
30
  #
39
- # Defines, intializes and returns an Exchange to act as an ingress
40
- # point for all published messages.
41
- #
42
- # There are three (3) supported Exchange types: direct, fanout and topic.
31
+ # h2. Exchange types
43
32
  #
44
- # As part of the standard, the server _must_ predeclare the direct exchange
45
- # 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
46
- # starting with 'amq.' are reserved). Attempts to declare an exchange using
47
- # 'amq.' as the name will raise an AMQP::Error and fail. In practice these
48
- # default exchanges are never used directly by client code.
33
+ # There are 4 supported exchange types: direct, fanout, topic and headers.
34
+ # Exchange type determines how exchange processes and routes messages.
49
35
  #
50
36
  #
51
37
  # h2. Direct exchanges
@@ -61,6 +47,11 @@ module AMQP
61
47
  # queue names. In other words, messages delivered to default exchange are routed to queues when
62
48
  # message routing key equals queue name. Default exchange name is an empty string.
63
49
  #
50
+ # As part of the standard, the server _must_ predeclare the direct exchange
51
+ # 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
52
+ # starting with 'amq.' are reserved). Attempts to declare an exchange using
53
+ # 'amq.' as the name will raise an AMQP::Error and fail. In practice these
54
+ # default exchanges are never used directly by client code.
64
55
  #
65
56
  #
66
57
  # h2. Fanout exchanges
@@ -71,7 +62,6 @@ module AMQP
71
62
  # Each queue gets it's own copy of the message.
72
63
  #
73
64
  #
74
- #
75
65
  # h2. Topic exchanges
76
66
  #
77
67
  # Topic exchanges are used for 1:n and n:m communication scenarios.
@@ -96,10 +86,7 @@ module AMQP
96
86
  #
97
87
  # h2. Headers exchanges
98
88
  #
99
- # As part of the AMQP standard, each server _should_ predeclare a headers
100
- # exchange named 'amq.match'.
101
- #
102
- # When publishing data to the exchange, bound queues subscribing to the
89
+ # When publishing data to exchange of type headers, bound queues subscribing to the
103
90
  # exchange indicate which data interests them by passing arguments
104
91
  # for matching against the headers in published messages. The
105
92
  # form of the matching can be controlled by the 'x-match' argument, which
@@ -109,6 +96,19 @@ module AMQP
109
96
  # it does an AND of the headers ), while a value of 'any' implies that
110
97
  # at least one should match (ie. it does an OR).
111
98
  #
99
+ # As part of the AMQP standard, each server _should_ predeclare a headers
100
+ # exchange named 'amq.match'.
101
+ #
102
+ #
103
+ # h2. Key methods
104
+ #
105
+ # Key methods of Exchange class are
106
+ #
107
+ # * {Exchange#publish}
108
+ # * {Exchange#delete}
109
+ # * {Exchange.default}
110
+ #
111
+ #
112
112
  #
113
113
  # h2. Exchange durability and persistence of messages.
114
114
  #
@@ -124,7 +124,8 @@ module AMQP
124
124
  # Publishing messages as persistent affects performance (just like with data stores, durability comes at a certain cost
125
125
  # in performance and vise versa). Pass :persistent => true to {Exchange#publish} to publish your message as persistent.
126
126
  #
127
- # Note that *only durable queues can be bound to durable exchanges*.
127
+ # Note that *only durable queues can be bound to durable exchanges*. Learn more in our {file:docs/Durability.textile Durability guide}.
128
+ #
128
129
  #
129
130
  #
130
131
  # h2. RabbitMQ extensions.
@@ -133,14 +134,6 @@ module AMQP
133
134
  # Learn more in {file:docs/VendorSpecificExtensions.textile}
134
135
  #
135
136
  #
136
- # h2. Key methods
137
- #
138
- # Key methods of Exchange class are
139
- #
140
- # * {Exchange#publish}
141
- # * {Exchange#delete}
142
- # * {Exchange.default}
143
- #
144
137
  #
145
138
  # @note Please make sure you read a section on exchanges durability vs. messages
146
139
  # persistence.
@@ -517,6 +510,7 @@ module AMQP
517
510
 
518
511
  protected
519
512
 
513
+ # @private
520
514
  def self.add_default_options(type, name, opts, block)
521
515
  { :exchange => name, :type => type, :nowait => block.nil? }.merge(opts)
522
516
  end
@@ -1,10 +1,3 @@
1
1
  # encoding: utf-8
2
2
 
3
- begin
4
- require 'eventmachine'
5
- rescue LoadError
6
- require 'rubygems'
7
- require 'eventmachine'
8
- end
9
-
10
3
  require 'amqp/ext/emfork'
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # @private
3
4
  EMFORK = $0 == __FILE__
4
5
 
5
6
  if EMFORK
@@ -8,9 +9,8 @@ end
8
9
 
9
10
  require 'eventmachine'
10
11
 
11
- #:stopdoc:
12
-
13
12
  # helper to fork off EM reactors
13
+ # @private
14
14
  def EM.fork num = 1, &blk
15
15
  unless @forks
16
16
  trap('CHLD') {
@@ -47,6 +47,7 @@ def EM.fork num = 1, &blk
47
47
  end
48
48
  end
49
49
 
50
+ # @private
50
51
  def EM.forks
51
52
  @forks ? @forks.keys : []
52
53
  end
@@ -38,6 +38,8 @@ module AMQP
38
38
  @channel.reject(@method.delivery_tag, opts.fetch(:requeue, false))
39
39
  end
40
40
 
41
+ # @return [Hash] AMQP message header w/o method-specific information.
42
+ # @api public
41
43
  def to_hash
42
44
  @header
43
45
  end # to_hash
@@ -46,6 +48,8 @@ module AMQP
46
48
  (@header && args.empty? && blk.nil? && @header.has_key?(meth)) || @method.respond_to?(meth)
47
49
  end
48
50
 
51
+ # Returns AMQP message attributes.
52
+ # @api public
49
53
  def method_missing(meth, *args, &blk)
50
54
  if @header && args.empty? && blk.nil? && @header.has_key?(meth)
51
55
  @header[meth]
@@ -10,27 +10,83 @@ module AMQP
10
10
  # to queues and finally queues deliver them to consumer applications (or consumer
11
11
  # applications fetch messages as needed).
12
12
  #
13
+ # Note that unlike some other messaging protocols/systems, messages are not delivered directly
14
+ # to queues. They are delivered to exchanges that route messages to queues using rules
15
+ # knows as *bindings*.
16
+ #
13
17
  #
14
18
  # h2. Concept of bindings
15
19
  #
20
+ # Binding is an association between a queue and an exchange.
16
21
  # Queues must be bound to at least one exchange in order to receive messages from publishers.
17
22
  # Learn more about bindings in {Exchange Exchange class documentation}.
18
23
  #
19
24
  #
25
+ # h2. Key methods
26
+ #
27
+ # Key methods of Queue class are
28
+ #
29
+ # * {Queue#bind}
30
+ # * {Queue#subscribe}
31
+ # * {Queue#pop}
32
+ # * {Queue#delete}
33
+ # * {Queue#purge}
34
+ # * {Queue#unbind}
35
+ #
36
+ #
20
37
  # h2. Queue names. Server-named queues. Predefined queues.
21
38
  #
22
- # Like an Exchange, queue names starting with 'amq.' are reserved for
23
- # internal use. Attempts to create queue names in violation of this
24
- # reservation will raise AMQP::Error (ACCESS_REFUSED).
39
+ # Every queue has a name that identifies it. Queue names often contain several segments separated by a dot (.), similarly to how URI
40
+ # path segments are separated by a slash (/), although it may be almost any string, with some limitations (see below).
41
+ # Applications may pick queue names or ask broker to generate a name for them. To do so, pass *empty string* as queue name argument.
42
+ #
43
+ # Here is an example:
44
+ #
45
+ # @example Declaring a server-named queue using AMQP::Queue constructor
46
+ # AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
47
+ # AMQP::Channel.new do |channel, open_ok|
48
+ # AMQP::Queue.new(channel, "", :auto_delete => true) do |queue, declare_ok|
49
+ # puts "#{queue.name} is ready to go. AMQP method: #{declare_ok.inspect}"
50
+ #
51
+ # connection.close {
52
+ # EM.stop { exit }
53
+ # }
54
+ # end
55
+ # end
56
+ # end
57
+ #
58
+ # <script src="https://gist.github.com/939596.js?file=gistfile1.rb"></script>
59
+ #
60
+ # If you want to declare a queue with a particular name, for example, "images.resize", pass it to Queue class constructor:
61
+ #
62
+ # @example Declaring a server-named queue using AMQP::Queue constructor
63
+ # AMQP.start("amqp://guest:guest@dev.rabbitmq.com:5672/") do |connection, open_ok|
64
+ # AMQP::Channel.new do |channel, open_ok|
65
+ # AMQP::Queue.new(channel, "images.resize", :auto_delete => true) do |queue, declare_ok|
66
+ # puts "#{queue.name} is ready to go."
67
+ #
68
+ # connection.close {
69
+ # EM.stop { exit }
70
+ # }
71
+ # end
72
+ # end
73
+ # end
74
+ #
75
+ # <script src="https://gist.github.com/939600.js?file=gistfile1.rb"></script>
76
+ #
77
+ # Queue names starting with 'amq.' are reserved for internal use by the broker. Attempts to declare queue with a name that violates this
78
+ # rule will result in AMQP::IncompatibleOptionsError to be thrown (when
79
+ # queue is re-declared on the same channel object) or channel-level exception (when originally queue
80
+ # was declared on one channel and re-declaration with different attributes happens on another channel).
81
+ # Learn more in {file:docs/Queues.textile Queues guide} and {file:docs/ErrorHandling.textile Error Handling guide}.
82
+ #
25
83
  #
26
- # When a queue is created without a name, the server will generate a
27
- # unique name internally (not currently supported in this library).
28
84
  #
29
85
  # h2. Queue life-cycles. When use of server-named queues is optimal and when it isn't.
30
86
  #
31
87
  # To quote AMQP 0.9.1 spec, there are two common message queue life-cycles:
32
88
  #
33
- # * Durable message queues that are shared by many consumers and have an independent existence: i.e. they
89
+ # * Durable message queues that are shared by many consumers and have an independent existence: i.e. they
34
90
  # will continue to exist and collect messages whether or not there are consumers to receive them.
35
91
  # * Temporary message queues that are private to one consumer and are tied to that consumer. When the
36
92
  # consumer disconnects, the message queue is deleted.
@@ -78,7 +134,7 @@ module AMQP
78
134
  # Publishing messages as persistent affects performance (just like with data stores, durability comes at a certain cost
79
135
  # in performance and vise versa). Pass :persistent => true to {Exchange#publish} to publish your message as persistent.
80
136
  #
81
- # Note that *only durable queues can be bound to durable exchanges*.
137
+ # Note that *only durable queues can be bound to durable exchanges*. Learn more in our {file:docs/Durability.textile Durability guide}.
82
138
  #
83
139
  #
84
140
  # h2. Message ordering
@@ -86,16 +142,11 @@ module AMQP
86
142
  # RabbitMQ FAQ explains {http://www.rabbitmq.com/faq.html#message-ordering ordering of messages in AMQP queues}
87
143
  #
88
144
  #
89
- # h2. Key methods
145
+ # h2. Error handling
90
146
  #
91
- # Key methods of Queue class are
92
- #
93
- # * {Queue#bind}
94
- # * {Queue#subscribe}
95
- # * {Queue#pop}
96
- # * {Queue#delete}
97
- # * {Queue#purge}
98
- # * {Queue#unbind}
147
+ # When channel-level error occurs, queues associated with that channel are reset: internal state and callbacks
148
+ # are cleared. Recommended strategy is to open a new channel and re-declare all the entities you need.
149
+ # Learn more in {file:docs/ErrorHandling.textile Error Handling guide}.
99
150
  #
100
151
  #
101
152
  # @note Please make sure you read a section on queue durability vs. messages
@@ -112,11 +163,8 @@ module AMQP
112
163
 
113
164
  # Name of this queue
114
165
  attr_reader :name
115
- attr_reader :sync_bind
116
166
  # Options this queue object was instantiated with
117
167
  attr_accessor :opts
118
- attr_accessor :on_declare
119
- attr_accessor :on_bind
120
168
 
121
169
 
122
170
 
@@ -163,8 +211,14 @@ module AMQP
163
211
  @channel = channel
164
212
  name = AMQ::Protocol::EMPTY_STRING if name.nil?
165
213
  @name = name unless name.empty?
166
- @opts = self.class.add_default_options(name, opts, block)
167
- @bindings = Hash.new
214
+ @server_named = name.empty?
215
+ @opts = self.class.add_default_options(name, opts, block)
216
+ @bindings = Hash.new
217
+
218
+ # a deferrable that we use to delay operations until this queue is actually declared.
219
+ # one reason for this is to support a case when a server-named queue is immediately bound.
220
+ # it's crazy, but 0.7.x supports it, so... MK.
221
+ @declaration_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
168
222
 
169
223
  if @opts[:nowait]
170
224
  @status = :opened
@@ -176,6 +230,8 @@ module AMQP
176
230
  super(channel.connection, channel, name)
177
231
 
178
232
  shim = Proc.new do |q, declare_ok|
233
+ @declaration_deferrable.succeed
234
+
179
235
  case block.arity
180
236
  when 1 then block.call(q)
181
237
  else
@@ -187,11 +243,19 @@ module AMQP
187
243
  if block
188
244
  self.declare(@opts[:passive], @opts[:durable], @opts[:exclusive], @opts[:auto_delete], @opts[:nowait], @opts[:arguments], &shim)
189
245
  else
190
- self.declare(@opts[:passive], @opts[:durable], @opts[:exclusive], @opts[:auto_delete], @opts[:nowait], @opts[:arguments])
246
+ injected_callback = Proc.new { @declaration_deferrable.succeed }
247
+ # we cannot pass :nowait as true here, AMQ::Client::Queue will (rightfully) raise an exception because
248
+ # it has no idea about crazy edge cases we are trying to support for sake of backwards compatibility. MK.
249
+ self.declare(@opts[:passive], @opts[:durable], @opts[:exclusive], @opts[:auto_delete], false, @opts[:arguments], &injected_callback)
191
250
  end
192
251
  end
193
252
  end
194
253
 
254
+ # @return [Boolean] true if this queue is server-named
255
+ def server_named?
256
+ @server_named
257
+ end # server_named?
258
+
195
259
 
196
260
  # This method binds a queue to an exchange. Until a queue is
197
261
  # bound it will not receive any messages. In a classic messaging
@@ -234,14 +298,21 @@ module AMQP
234
298
  # @see Queue#unbind
235
299
  def bind(exchange, opts = {}, &block)
236
300
  @status = :unbound
237
- @sync_bind = !opts[:nowait]
238
301
  # amq-client's Queue already does exchange.respond_to?(:name) ? exchange.name : exchange
239
302
  # for us
240
303
  exchange = exchange
241
304
  @bindings[exchange] = opts
242
305
 
243
- @channel.once_open do
244
- super(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), (opts[:nowait] || block.nil?), opts[:arguments], &block)
306
+ if self.server_named?
307
+ @channel.once_open do
308
+ @declaration_deferrable.callback do
309
+ super(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), (opts[:nowait] || block.nil?), opts[:arguments], &block)
310
+ end
311
+ end
312
+ else
313
+ @channel.once_open do
314
+ super(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), (opts[:nowait] || block.nil?), opts[:arguments], &block)
315
+ end
245
316
  end
246
317
 
247
318
  self
@@ -599,14 +670,6 @@ module AMQP
599
670
  @on_declare
600
671
  end
601
672
 
602
- # Compatibility alias for #on_bind.
603
- #
604
- # @api public
605
- # @deprecated
606
- def bind_callback
607
- @on_bind
608
- end
609
-
610
673
 
611
674
 
612
675