amqp 0.8.4 → 0.9.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,4 +1,4 @@
1
- = Version 0.8.2 (to be released)
1
+ = Version 0.9.0 (not yet released)
2
2
 
3
3
 
4
4
  = Version 0.8.1
@@ -6,6 +6,7 @@
6
6
  * [BUG] AMQP::Queue#status can no longer result in queue instance @ivars being changed.
7
7
  * [API] AMQP::Channel#reuse allows channel instance to be reused with a different channel id, may be used to recover from channel-level exceptions.
8
8
 
9
+
9
10
  = Version 0.8.0
10
11
 
11
12
  * [API] AMQP::Session#on_skipped_heartbeats callback that can be used to handle skipped heartbeats (for cases when TCP network failure detection is not timely enough)
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # About Ruby amqp gem #
2
2
 
3
- Ruby amqp gem is a widely used, feature-rich, well-maintained asynchronous AMQP 0.9.1 client with batteries included.
3
+ [Ruby amqp gem](http://rubyamqp.info) is a widely used, feature-rich, well-maintained asynchronous AMQP 0.9.1 client with batteries included.
4
4
  This library works with Ruby 1.8.7 (*except for p249*, see the FAQ), Ruby 1.9.2, Ruby 1.9.3, [JRuby](http://jruby.org), [Rubinius](http://rubini.us) as well as [REE](http://www.rubyenterpriseedition.com), and is licensed under the [Ruby License](http://www.ruby-lang.org/en/LICENSE.txt)
5
5
 
6
- 0.8.0.RCs and later versions of amqp gem implement [AMQP 0.9.1](http://bit.ly/amqp-model-explained) (see also [AMQP 0.9.1 spec document](http://bit.ly/hw2ELX)) and support [RabbitMQ extensions to AMQP 0.9.1](http://www.rabbitmq.com/extensions.html).
6
+ 0.8.0 and later versions of amqp gem implement [AMQP 0.9.1](http://bit.ly/amqp-model-explained) (see also [AMQP 0.9.1 spec document](http://bit.ly/amqp091spec)) and support [RabbitMQ extensions to AMQP 0.9.1](http://www.rabbitmq.com/extensions.html).
7
7
 
8
8
  [![Continuous Integration status](https://secure.travis-ci.org/ruby-amqp/amqp.png)](http://travis-ci.org/ruby-amqp/amqp)
9
9
 
@@ -123,7 +123,7 @@ to learn more about AMQP principles & concepts.
123
123
 
124
124
  We believe that in order to be a library our users **really** love, we need to care about documentation as much as (or more)
125
125
  code readability, API beauty and autotomated testing across 5 Ruby implementations on multiple operating systems. We do care
126
- about our [documentation](http://bitly.com/amqp-gem-docs): **if you don't find your answer in documentation, we consider it a high severity bug** that you
126
+ about our [documentation](http://rubyamqp.info): **if you don't find your answer in documentation, we consider it a high severity bug** that you
127
127
  should [file to us](http://github.com/ruby-amqp/amqp/issues). Or just complain to [@rubyamqp](https://twitter.com/rubyamqp) on Twitter.
128
128
 
129
129
 
@@ -150,7 +150,7 @@ There is also a work-in-progress [Messaging Patterns and Use Cases With AMQP](ht
150
150
 
151
151
  ### Documentation guides ###
152
152
 
153
- [Documentation guides](http://bit.ly/amqp-gem-docs) describe the library itself as well as AMQP concepts, usage scenarios, topics like working with exchanges and queues,
153
+ [Documentation guides](http://rubyamqp.info) (also [on rubydoc.info](http://bit.ly/amqp-gem-docs)) describe the library itself as well as AMQP concepts, usage scenarios, topics like working with exchanges and queues,
154
154
  error handing & recovery, broker-specific extensions, TLS support, troubleshooting and so on. Most of the documentation is in these guides.
155
155
 
156
156
 
@@ -172,6 +172,10 @@ Upgrading from amqp gem 0.6.x and 0.7.x to to 0.8.0.RCs is straightforward, plea
172
172
  The same guide explains amqp gem versions history and why you would want to upgrade.
173
173
 
174
174
 
175
+ ## Maintainer Information
176
+
177
+ amqp gem is maintained by [Michael Klishin](https://github.com/michaelklishin).
178
+
175
179
 
176
180
  ## Community
177
181
 
@@ -76,7 +76,13 @@ h2. Why developers should upgrade to 0.8.0
76
76
 
77
77
  h2. AMQP protocol version change
78
78
 
79
- amqp gem before 0.8.0.RCs series implemented (most of) AMQP 0.8 specification.
79
+ amqp gem before 0.8.0 (0.6.x, 0.7.x) series implemented (most of) AMQP 0.8 specification. amqp gem 0.8.0 implements AMQP 0.9.1 and thus
80
+ *requires RabbitMQ version 2.0 or later*. See {file:docs/RabbitMQVersions.textile RabbitMQ versions} for more information about
81
+ RabbitMQ versions support and how obtain up-todate packages for your operating system.
82
+
83
+ <span class="note">
84
+ amqp gem 0.8.0 and later versions implement AMQP 0.9.1 and thus *requires RabbitMQ version 2.0 or later*
85
+ </span>
80
86
 
81
87
 
82
88
 
@@ -262,7 +262,11 @@ To make it possible for a single broker to host multiple isolated "environments"
262
262
  of _virtual hosts_ (vhosts). They are similar to virtual hosts used by many popular Web servers and provide completely isolated environments
263
263
  in which AMQP entities live. AMQP clients specify what vhosts they want to use during AMQP connection negotiation.
264
264
 
265
- An AMQP 0.9.1 vhost name can be any non-blank string.
265
+ An AMQP 0.9.1 vhost name can be any non-blank string. Some most common use cases for vhosts are
266
+
267
+ * To separate AMQP entities used by different groups of applications
268
+ * To separate multiple installations/environments (e.g. production, staging) of one or more applications
269
+ * To implement a multi-tenant environment
266
270
 
267
271
 
268
272
  h2. AMQP is Extensible
@@ -20,7 +20,7 @@ but *most of the content is concentrated in just 3-4 guides* that are about 80%
20
20
  Here is a summary of guides and their content:
21
21
 
22
22
  <dl>
23
- <dt>{file:docs/GettingStarted.textile Getting Started}</dt>
23
+ <dt>{file:docs/GettingStarted.textile Getting Started with Ruby amqp gem and RabbitMQ}</dt>
24
24
  <dd>
25
25
  Walks you through gem installation and 3 applications that demonstrate what AMQP has to offer. Explains how amqp gem should
26
26
  be integrated into rich object-oriented Ruby programs.
@@ -73,6 +73,13 @@ Here is a summary of guides and their content:
73
73
  recovery is hard. How to survive typical problems. What other tools can help (e.g. HAProxy).
74
74
  </dd>
75
75
 
76
+ <dt>{file:docs/TestingWithEventedSpec.textile Unit and integration testing of AMQP applications}</dt>
77
+ <dd>
78
+ Unit testing of asynchronous code: typical problems and ways to solve them. An oviewview of evented-spec, the gem that amqp gem itself uses
79
+ for "its test suite":https://github.com/ruby-amqp/amqp/tree/master/spec.
80
+ </dd>
81
+
82
+
76
83
  <dt>{file:docs/RabbitMQVersions.textile RabbitMQ versions}</dt>
77
84
  <dd>
78
85
  RabbitMQ versions that amqp gem supports. Popular Linux distributions and RabbitMQ versions they ship. How to obtain up-to-date official
@@ -89,7 +96,7 @@ When more than one guide describes the same concept, we make sure to use cross-r
89
96
 
90
97
  h2. Full guide list
91
98
 
92
- * {file:docs/GettingStarted.textile Getting Started}
99
+ * {file:docs/GettingStarted.textile Getting Started with Ruby amqp gem and RabbitMQ}
93
100
  * {file:docs/AMQP091ModelExplained.textile AMQP 0.9.1 Model Explained}
94
101
  * {file:docs/ConnectingToTheBroker.textile Connecting to the Broker}
95
102
  * {file:docs/Queues.textile Working With Queues}
@@ -99,6 +106,7 @@ h2. Full guide list
99
106
  * {file:docs/Durability.textile Durability and Message Persistence}
100
107
  * {file:docs/ErrorHandling.textile Error Handling and Recovery}
101
108
  * {file:docs/08Migration.textile Upgrading from version 0.6.x/0.7.x to 0.8.x and above}
109
+ * {file:docs/TestingWithEventedSpec.textile Unit and integration testing of AMQP applications}
102
110
  * {file:docs/Troubleshooting.textile Troubleshooting and debugging AMQP applications}
103
111
  * {file:docs/Clustering.textile Clustering}
104
112
  * {file:docs/RabbitMQVersions.textile RabbitMQ versions}
@@ -502,6 +502,11 @@ Error handling can be easily integrated into object-oriented Ruby code (in fact,
502
502
  A common technique is to combine {http://rubydoc.info/stdlib/core/1.8.7/Object:method Object#method} and {http://rubydoc.info/stdlib/core/1.8.7/Method:to_proc Method#to_proc}
503
503
  and use object methods as error handlers. For example of this, see section on connection-level exceptions above.
504
504
 
505
+ <span class="note">
506
+ Because channel-level exceptions may be raised because of multiple unrelated reasons and often indicate misconfigurations, how they are handled is
507
+ very specific to particular applications. A common strategy is to log an error and then open and use another channel.
508
+ </span>
509
+
505
510
 
506
511
  h3. Common channel-level exceptions and what they mean
507
512
 
@@ -226,7 +226,7 @@ exchange = channel.direct("nodes.metadata")
226
226
  </code>
227
227
  </pre>
228
228
 
229
- Both methods asynchronously declare a queue. Because the declaration necessitates a network round trip, publishing
229
+ Both methods asynchronously declare an exchange named "nodes.metadata". Because the declaration necessitates a network round trip, publishing
230
230
  operations on {AMQP::Exchange} instances are delayed until a broker reply (`exchange.declare-ok`) is received.
231
231
 
232
232
  Also, both methods let you pass a block to run a piece of code when the broker responds with `exchange.declare-ok`
@@ -47,7 +47,7 @@ On Debian and Ubuntu, you can either "download the RabbitMQ .deb package":http:/
47
47
  For RPM-based distributions like RedHat or CentOS, the RabbitMQ team provides an "RPM package":http://www.rabbitmq.com/install.html#rpm.
48
48
 
49
49
  <span class="note">
50
- The RabbitMQ package that ships with recent Ubuntu 10.10 versions is outdated and *will not work with v0.8.0 and later of the amqp gem* (we need at least RabbitMQ v2.0 for use with this guide).
50
+ The RabbitMQ package that ships with recent Ubuntu versions (for example, 10.10) is outdated and *will not work with v0.8.0 and later of the amqp gem* (we need at least RabbitMQ v2.0 for use with this guide).
51
51
  </span>
52
52
 
53
53
 
@@ -58,7 +58,7 @@ h3. Make sure that you have Ruby and "Rubygems":http://docs.rubygems.org/read/ch
58
58
 
59
59
  This guide assumes that you have installed one of the following supported Ruby implementations:
60
60
 
61
- * Ruby v1.8.7
61
+ * Ruby v1.8.7 [except for 1.8.7-p248 and -p249 that have "a bug that severely affects amqp gem":http://bit.ly/iONBmH]
62
62
  * Ruby v1.9.2
63
63
  * Ruby v1.9.3
64
64
  * JRuby (we recommend v1.6)
@@ -86,7 +86,7 @@ h3. You can also use Bundler to install the gem
86
86
  <code>
87
87
  source :rubygems
88
88
 
89
- gem "amqp", "~> 0.8.0" # optionally: :git => "git://github.com/ruby-amqp/amqp.git", :branch => "0.8.x-stable"
89
+ gem "amqp", "~> 0.8.4" # optionally: :git => "git://github.com/ruby-amqp/amqp.git", :branch => "0.8.x-stable"
90
90
  </code>
91
91
  </pre>
92
92
 
@@ -101,7 +101,7 @@ irb -rubygems
101
101
  :001 > require "amqp"
102
102
  => true
103
103
  :002 > AMQP::VERSION
104
- => "0.8.0"
104
+ => "0.8.4"
105
105
  </code>
106
106
  </pre>
107
107
 
@@ -33,7 +33,8 @@ There are other, more specialized group of messaging patterns that are out of sc
33
33
  This guide demonstrates implementation of several common routing patterns plus explains how built-in AMQP 0.9.1 features
34
34
  can be used to implement message construction and message transformation patterns.
35
35
 
36
- TBD
36
+ Note that guide is a work in progress. There are many messaging patterns and new variations are being discovered every year.
37
+ This guide thus strives to be useful to the 80% of developers instead of being "complete".
37
38
 
38
39
 
39
40
 
@@ -86,63 +87,12 @@ h3. Code example
86
87
 
87
88
  h4. Client code
88
89
 
89
- <pre>
90
- <code>
91
- require "amqp"
92
-
93
- EventMachine.run do
94
- connection = AMQP.connect
95
- channel = AMQP::Channel.new(connection)
96
-
97
- replies_queue = channel.queue("", :exclusive => true, :auto_delete => true)
98
- replies_queue.subscribe do |metadata, payload|
99
- puts "[response] Response for #{metadata.correlation_id}: #{payload.inspect}"
100
- end
101
-
102
- # request time from a peer every 3 seconds
103
- EventMachine.add_periodic_timer(3.0) do
104
- puts "[request] Sending a request..."
105
- channel.default_exchange.publish("get.time",
106
- :routing_key => "amqpgem.examples.services.time",
107
- :message_id => Kernel.rand(10101010).to_s,
108
- :reply_to => replies_queue.name,
109
- :immediate => true)
110
- end
111
-
112
-
113
- Signal.trap("INT") { connection.close { EventMachine.stop } }
114
- end
115
- </code>
116
- </pre>
90
+ <script src="https://gist.github.com/1207763.js"> </script>
117
91
 
118
92
 
119
93
  h4. Server code
120
94
 
121
- <pre>
122
- <code>
123
- require "amqp"
124
-
125
- EventMachine.run do
126
- connection = AMQP.connect
127
- channel = AMQP::Channel.new(connection)
128
-
129
- requests_queue = channel.queue("amqpgem.examples.services.time", :exclusive => true, :auto_delete => true)
130
- requests_queue.subscribe(:ack => true) do |metadata, payload|
131
- puts "[requests] Got a request #{metadata.message_id}. Sending a reply..."
132
- channel.default_exchange.publish(Time.now.to_s,
133
- :routing_key => metadata.reply_to,
134
- :correlation_id => metadata.message_id,
135
- :immediate => true,
136
- :mandatory => true)
137
-
138
- metadata.ack
139
- end
140
-
141
-
142
- Signal.trap("INT") { connection.close { EventMachine.stop } }
143
- end
144
- </code>
145
- </pre>
95
+ <script src="https://gist.github.com/1207764.js"> </script>
146
96
 
147
97
  In the examples above messages are published with the :immediate attribute set. This is not necessary in all
148
98
  cases: sometimes it is OK for requests to sit in the queue without active consumers. Replies, on the other hand,
@@ -153,8 +103,13 @@ server application will log returned messages. More on this in the {file:docs/Ex
153
103
 
154
104
  h3. Related patterns
155
105
 
156
- * Request/Reply
157
- * Event
106
+ Request/Reply demonstrates two common techniques that are sometimes referred to as messaging patterns of its own:
107
+
108
+ * "Correlation Identifier":http://www.eaipatterns.com/CorrelationIdentifier.html (for identifying what request incoming response is for)
109
+ * "Return Address":http://www.eaipatterns.com/ReturnAddress.html (for identifying where replies should be sent)
110
+
111
+ Other related patterns are
112
+
158
113
  * Scatter/Gather
159
114
  * Smart Proxy
160
115
 
@@ -187,86 +142,19 @@ h4. Request message attributes
187
142
 
188
143
  <dl>
189
144
  <dt>:type</dt>
190
- <dd>Message type, as a string. For example: gems.install or commands.shutdown</dd>
145
+ <dd>Message type as a string. For example: gems.install or commands.shutdown</dd>
191
146
  </dl>
192
147
 
193
148
  h3. Code example
194
149
 
195
150
  h4. Producer (Sender)
196
151
 
197
- <pre>
198
- <code>
199
- require "rubygems"
200
- require "amqp"
201
- require "yaml"
202
-
203
- t = Thread.new { EventMachine.run }
204
- sleep(0.5)
205
-
206
-
207
- connection = AMQP.connect
208
- channel = AMQP::Channel.new(connection, :auto_recovery => true)
209
-
210
- channel.prefetch(1)
211
-
212
- # Acknowledgements are good for letting the server know
213
- # that the task is finished. If the consumer doesn't send
214
- # the acknowledgement, then the task is considered to be unfinished
215
- # and will be requeued when consumer closes AMQP connection (because of a crash, for example).
216
- channel.queue("amqpgem.examples.patterns.command", :durable => true, :auto_delete => false).subscribe(:ack => true) do |metadata, payload|
217
- case metadata.type
218
- when "gems.install"
219
- data = YAML.load(payload)
220
- puts "[gems.install] Received a 'gems.install' request with #{data.inspect}"
221
-
222
- # just to demonstrate a realistic example
223
- shellout = "gem install #{data[:gem]} --version '#{data[:version]}'"
224
- puts "[gems.install] Executing #{shellout}"; system(shellout)
225
- puts "[gems.install] Done"
226
- puts
227
- else
228
- puts "[commands] Unknown command: #{metadata.type}"
229
- end
230
-
231
- # message is processed, acknowledge it so that broker discards it
232
- metadata.ack
233
- end
234
-
235
- puts "[boot] Ready. Will be publishing commands every 10 seconds."
236
- Signal.trap("INT") { connection.close { EventMachine.stop } }
237
- t.join
238
- </code>
239
- </pre>
152
+ <script src="https://gist.github.com/1207758.js"> </script>
240
153
 
241
154
 
242
155
  h4. Consumer (Recipient)
243
156
 
244
- <pre>
245
- <code>
246
- require "amqp"
247
- require "yaml"
248
-
249
- t = Thread.new { EventMachine.run }
250
- sleep(0.5)
251
-
252
- connection = AMQP.connect
253
- channel = AMQP::Channel.new(connection)
254
-
255
- # publish new commands every 3 seconds
256
- EventMachine.add_periodic_timer(10.0) do
257
- puts "Publishing a command (gems.install)"
258
- payload = { :gem => "rack", :version => "~> 1.3.0" }.to_yaml
259
-
260
- channel.default_exchange.publish(payload,
261
- :type => "gems.install",
262
- :routing_key => "amqpgem.examples.patterns.command")
263
- end
264
-
265
- puts "[boot] Ready"
266
- Signal.trap("INT") { connection.close { EventMachine.stop } }
267
- t.join
268
- </code>
269
- </pre>
157
+ <script src="https://gist.github.com/1207761.js"> </script>
270
158
 
271
159
 
272
160
  h3. Related patterns
@@ -292,6 +180,12 @@ Some specific use cases of Event pattern are
292
180
  * Live sport score updates
293
181
  * Various "push notifications" for mobile applications
294
182
 
183
+ The Event pattern is very similar to the Command pattern, however, there is typically certain differences between the two:
184
+
185
+ * Event listeners often do not respond back to event producers
186
+ * Event listeners are often concerned with data collection: they update counters, persist event information and so on
187
+ * There may be more than event listener in the system. Commands are often carried out by one particular application
188
+
295
189
 
296
190
  h3. AMQP-based implementation
297
191
 
@@ -300,12 +194,31 @@ then use server-named exclusive queues and all bind to that exchange. Event mess
300
194
  attribute to indicate event type and message body (plus, possibly, message headers) to pass event
301
195
  context information.
302
196
 
197
+ h4. Request message attributes
198
+
199
+ <dl>
200
+ <dt>:type</dt>
201
+ <dd>Message type as a string. For example: files.created, files.indexed or pages.viewed</dd>
202
+ </dl>
203
+
204
+ <span class="note">
205
+ Due to misconfiguration or different upgrade time/policy, applications may receive events they do not know how to handle.
206
+ It is important for developers to handle such cases, otherwise it is likely that consumers may crash.
207
+ </span>
208
+
303
209
  More on fanout exchange type in the {file:docs/Exchanges.textile Working With Exchanges} guide.
304
210
 
305
211
 
306
212
  h3. Code example
307
213
 
308
- TBD
214
+ h4. Producer (Sender)
215
+
216
+ <script src="https://gist.github.com/1207750.js"> </script>
217
+
218
+
219
+ h4. Consumer (Handler)
220
+
221
+ <script src="https://gist.github.com/1207749.js"> </script>
309
222
 
310
223
 
311
224
  h3. Related patterns
@@ -315,19 +228,23 @@ h3. Related patterns
315
228
 
316
229
 
317
230
 
231
+
318
232
  h2. Document Message pattern
319
233
 
320
234
  h3. Description & Use cases
321
235
 
322
- TBD
236
+ Document Message pattern is very similar to Command and Event patterns. The difference is in the intent: whereas a Command message tells
237
+ the receiver to invoke certain behavior, a Document Message just passes data and lets the receiver decide what, if anything, to do with the data.
323
238
 
324
- h3. AMQP-based implementation
239
+ Message payload is a single logical entity, for example, one (or a group of closely related) database rows or documents.
325
240
 
326
- TBD
241
+ Use cases for the Document Message pattern often have something to do with processing of documents:
327
242
 
328
- h3. Code example
243
+ * Indexing
244
+ * Archiving
245
+ * Content extraction
246
+ * Transformation (translation, transcoding and so on) of document data
329
247
 
330
- TBD
331
248
 
332
249
 
333
250
  h2. Competing Consumers pattern
@@ -457,6 +457,8 @@ It can be right after receiving a message, or after persisting it to a data stor
457
457
  processing the message (for example, successfully fetching a Web page, processing and storing it into some persistent
458
458
  data store).
459
459
 
460
+ !https://github.com/ruby-amqp/amqp/raw/master/docs/diagrams/006_amqp_091_message_acknowledgements.png!
461
+
460
462
  If a consumer dies without sending an acknowledgement, the AMQP broker will redeliver it to another consumer, or, if
461
463
  none are available at the time, the broker will wait until at least one consumer is registered for the same queue
462
464
  before attempting redelivery.
@@ -13,6 +13,17 @@ h2. Covered versions
13
13
  This guide covers "Ruby amqp gem":http://github.com/ruby-amqp/amqp v0.8.0 and later.
14
14
 
15
15
 
16
+ h2. RabbitMQ Version Requirement
17
+
18
+ amqp gem before 0.8.0 (0.6.x, 0.7.x) series implemented (most of) AMQP 0.8 specification. amqp gem 0.8.0 implements AMQP 0.9.1 and thus
19
+ *requires RabbitMQ version 2.0 or later*. See {file:docs/RabbitMQVersions.textile RabbitMQ versions} for more information about
20
+ RabbitMQ versions support and how obtain up-todate packages for your operating system.
21
+
22
+ <span class="note">
23
+ amqp gem 0.8.0 and later versions implement AMQP 0.9.1 and thus *requires RabbitMQ version 2.0 or later*
24
+ </span>
25
+
26
+
16
27
 
17
28
  h2. Using recent versions on Debian and Ubuntu
18
29
 
@@ -22,6 +33,9 @@ that only supports AMQP protocol 0.8. Ruby amqp gem 0.8.0 and later *will not wo
22
33
  We strongly recommend that you use "RabbitMQ apt repository":http://www.rabbitmq.com/debian.html#apt that has recent versions of RabbitMQ.
23
34
 
24
35
 
36
+
37
+ h2. OpsCode Chef & Puppet
38
+
25
39
  h3. Chef cookbook for RabbitMQ
26
40
 
27
41
  There is a "Chef cookbook for RabbitMQ":https://github.com/opscode/cookbooks/tree/master/rabbitmq that installs recent versions from
@@ -1,34 +1,36 @@
1
- # @title Ruby AMQP gem: Testing with Evented spec
2
-
3
- h1. Testing you applications with evented-spec
1
+ # @title Ruby AMQP gem: Testing AMQP applications
4
2
 
3
+ h1. Testing AMQP applications
5
4
 
6
5
  h2. About this guide
7
6
 
8
- This guide covers writing tests with evented-spec for amqp-based applications.
7
+ This guide covers unit testing of amqp-based applications, primarily using "evented-spec":http://github.com/ruby-amqp/evented-spec.
8
+
9
9
 
10
10
 
11
11
  h2. Covered versions
12
12
 
13
- This guide covers "Ruby amqp gem":http://github.com/ruby-amqp/amqp v0.8.0 and later.
14
- Also covered is the "evented-spec gem":http://github.com/ruby-amqp/evented-spec v0.4.1 and later.
13
+ This guide covers "Ruby amqp gem":http://github.com/ruby-amqp/amqp v0.8.0 and later as well as
14
+ "evented-spec gem":http://github.com/ruby-amqp/evented-spec v0.9.0 and later.
15
+
15
16
 
16
17
  h2. Rationale
17
18
 
18
- Asynchronous environments are somewhat more difficult to test.
19
- There are two different approaches to testing them:
19
+ The AMQP protocol is inherently asynchronous. Testing of asynchronous code is often more difficult
20
+ than synchronous code. There are two approaches to it:
20
21
 
21
22
  * Stubbing out a big chunk of the environment
22
23
  * Using the "real" environment
23
24
 
24
- First approach is risky because your application becomes divorced from reality and what *really* happens.
25
- Second approach is more "correct", but at the same time much more tedious, because there are a lot of things to wrap your head around: initial setup, error handling,
26
- in case of amqp, also connection starting.
25
+ The former is risky because your application becomes divorced from actual behavior of other applications.
26
+ The latter approach is more reliable but at the same time more tedious, because there is certain amount of incidental complexity
27
+ that "real" environment carries.
27
28
 
28
- However, tediousness for most part can be easily fought with proper helpers and organization. evented-spec gem
29
- (based on arvicco's amqp-spec and tmmm1's em-spec) provides this kind of helpers for your asynchronous applications.
29
+ However, a lot of this complexity can be eliminated with tools and libraries. The evented-spec gem is one of those tools. It grew
30
+ out of necessity to test "amqp Ruby gem":http://github.com/ruby-amqp/amqp and has provent itself to be both very powerful and easy to
31
+ use. This guide covers usage of that gem in context of applications that use amqp gem but can also be useful for testing EventMachine and
32
+ Cool.io-based applications.
30
33
 
31
- This guide covers usage of that gem in context of amqp but there are all the parts for testing EM-based and Cool.io-based applications.
32
34
 
33
35
  h2. Using evented-spec
34
36
 
@@ -39,27 +41,49 @@ calls to your examples:
39
41
 
40
42
  <script src="https://gist.github.com/1027377.js"></script>
41
43
 
42
- h3. Implications of asynchronous environment (or why we need #done)
44
+
45
+
46
+ h3. Testing in the Asynchronous Environment
43
47
 
44
48
  Since we are using callback mechanisms in order to provide asynchronicity, we have to deal with situation when we expect a response,
45
49
  and response never comes. Usual solution includes setting a timeout which makes the given tests fail if they aren't finished in a timely
46
50
  manner. When <code>#done</code> is called, your tests confirm successful ending of specs. Try removing <code>done</code> from the above
47
51
  example and see what happens. (spoiler: <code>EventedSpec::SpecHelper::SpecTimeoutExceededError: Example timed out</code>)
48
52
 
49
- h3. Changing default connection options and default timeout
50
53
 
51
- It is not uncommon to have custom options for your test environment. For example, setting up custom vhost and timeout:
54
+
55
+ h3. The #done method
56
+
57
+ The *#done* method call is a hint for evented-spec to consider the example finished. If this method is not called, example will be forcefully
58
+ terminated after a certain period of time or "time out". This means there are two approaches to testing of asynchronous code:
59
+
60
+ * Have timeout value high enough for all operations to finish (for example, expected number of messages is received).
61
+ * Call #done when some condition holds true (for example, message with a specific property or payload is received).
62
+
63
+ The latter approach is recommended because it makes tests less dependent on machine-specific throughput or timing: it is very
64
+ common for continuous integration environments to use virtual machines that are significantly less powerful than machines developers
65
+ use, so timeouts have to be carefully adjusted to work in both settings.
66
+
67
+
68
+
69
+ h3. Default Connection Options and Timeout
70
+
71
+ It is sometimes desirable to use custom connection settings for your test environment as well as the default timeout value used. evented-spec lets you do
72
+ it:
52
73
 
53
74
  <script src="https://gist.github.com/1027410.js"> </script>
54
75
 
55
- Options are the same as used in <code>AMQP.connect</code>.
76
+ Available options are passed to {AMQP.connect} so it is possible to specify host, port, vhost, username and password your test suite needs.
77
+
78
+
56
79
 
57
- h3. Callbacks
80
+ h3. Lifecycle Callbacks
58
81
 
59
82
  evented-spec provides various callbacks similar to rspec's <code>before(:each)</code> / <code>after(:each)</code>. They are called <code>amqp_before</code> and
60
83
  <code>amqp_after</code> and happen right after connection is established or before connection is closed. It is a good place to put your channel initialization routines.
61
84
 
62
- h3. Example of a meaningful spec
85
+
86
+ h3. Full Example
63
87
 
64
88
  Now that you're filled on theory part, it's time to do something with all this knowledge. Below goes a slightly modified version of one of the integration specs from AMQP
65
89
  suite. It sets up default topic exchange and publishes various messages about sports events:
@@ -69,7 +93,9 @@ suite. It sets up default topic exchange and publishes various messages about sp
69
93
  Couple of things to notice: <code>#done</code> is invoked using an optional callback and optional delay, also instance variables behavior in hooks is the same as in "normal" rspec
70
94
  hooks.
71
95
 
72
- h3. Using #delayed in your specs
96
+
97
+
98
+ h3. Using #delayed
73
99
 
74
100
  AMQP gem uses "EventMachine":http://eventmachine.rubyforge.org/ under hood. If you don't know about eventmachine, you can read more about it on the official site.
75
101
  What's important for us is that you *cannot use <code>sleep</code> for delays*. Why? Because all the specs code is processed directly in the "reactor":http://en.wikipedia.org/wiki/Reactor_pattern thread, if you
@@ -83,16 +109,34 @@ In the following example, we declare two channels, then declare the same queue t
83
109
 
84
110
  If you draw a timeline, various events happen at 0.0s, then at 0.1s, then at 0.3s and eventually at 0.4s.
85
111
 
86
- h3. What happens under hood
87
112
 
88
- When you include <code>EventedSpec::AMQPSpec</code> module, <code>#it</code> calls are wrapped in <code>EM.start</code> + <code>AMQP.connect</code>
89
- calls, so you can start writing your examples as if you're connected. You still need to initialize your own channel though.
113
+ h3. Design For Testability
114
+
115
+ As *Integration With Objects* section of the {file:docs/GettingStarted.textile Getting Started with Ruby amqp gem and RabbitMQ} demonstrates, good object-oriented design
116
+ often makes it possible to test AMQP consumers in isolation without connecting to the broker or even starting EventMachine even loop. All the "Design for testability"
117
+ practices apply fully to AMQP application testing.
118
+
119
+
120
+
121
+ h3. Real worldExamples
122
+
123
+ Please refer to the "amqp gem test suite":https://github.com/ruby-amqp/amqp/tree/master/spec to see evented-spec in action.
124
+
125
+
126
+
127
+ h3. How evented-spec Works
128
+
129
+ When you include <code>EventedSpec::AMQPSpec</code> module, <code>#it</code> calls are wrapped in <code>EventMachine.start</code> + <code>AMQP.connect</code>
130
+ calls, so you can start writing your examples as if you're connected. Please note that you still need to open your own channel(s).
131
+
90
132
 
91
133
 
92
134
  h2. What to read next
93
135
 
94
- There is a lot more to evented-spec than described in this guide. evented-spec has "rdocs":http://rdoc.info/github/ruby-amqp/evented-spec/master,
95
- which I strongly suggest to read in case you don't understand some parts of the gem.
136
+ There is a lot more to evented-spec than described in this guide. "evented-spec documentation":http://rdoc.info/github/ruby-amqp/evented-spec/master
137
+ covers that gem in more detail gem. For more code examples, see "amqp Ruby gem test suite":https://github.com/ruby-amqp/amqp/tree/master/spec.
138
+
139
+
96
140
 
97
141
  h2. Tell us what you think!
98
142
 
@@ -72,6 +72,10 @@ In some situations not a single message can be lost. The only reliable way of do
72
72
  Publisher confirms are similar to message acknowledgements documented in the {file:docs/Queues.textile Working With Queues} guide but involve publisher and AMQP broker
73
73
  instead of consumer and AMQP broker.
74
74
 
75
+ !https://github.com/ruby-amqp/amqp/raw/master/docs/diagrams/006_amqp_091_message_acknowledgements.png!
76
+
77
+ !https://github.com/ruby-amqp/amqp/raw/master/docs/diagrams/007_rabbitmq_publisher_confirms.png!
78
+
75
79
 
76
80
  h3. Public API
77
81
 
@@ -43,7 +43,7 @@ AMQP.start(:host => "localhost") do |connection, open_ok|
43
43
 
44
44
  Signal.trap "TERM", show_stopper
45
45
  Signal.trap "INT", show_stopper
46
- EM.add_timer(45, show_stopper)
46
+ EM.add_timer(ENV.fetch("TIMER", 45), show_stopper)
47
47
 
48
48
 
49
49
  puts "This example needs another script/app to publish messages to amq.fanout. See examples/error_handling/hello_world_producer.rb for example"
@@ -12,7 +12,7 @@ require 'amqp'
12
12
  puts "=> Connection loss is detected and handled"
13
13
  puts
14
14
  AMQP.start(:port => 5672,
15
- :vhost => "/amq_client_testbed",
15
+ :vhost => "amq_client_testbed",
16
16
  :user => "amq_client_gem",
17
17
  :password => "amq_client_gem_password",
18
18
  :timeout => 0.3,
@@ -16,17 +16,29 @@ AMQP.start(:host => "localhost") do |connection, open_ok|
16
16
  raise connection_close.reply_text
17
17
  end
18
18
 
19
- ch1 = AMQP::Channel.new(connection, 2)
19
+ connection.on_tcp_connection_loss do |conn, settings|
20
+ puts "[network failure] Trying to reconnect..."
21
+ conn.reconnect(false, 2)
22
+ end
23
+
24
+
25
+ ch1 = AMQP::Channel.new(connection, 2, :auto_recovery => true)
20
26
  ch1.on_error do |ch, channel_close|
21
27
  raise channel_close.reply_text
22
28
  end
23
29
 
24
30
 
25
31
  exchange = ch1.fanout("amq.fanout", :durable => true)
26
- EventMachine.add_periodic_timer(0.5) do
27
- puts "Publishing..."
32
+ EventMachine.add_periodic_timer(0.9) do
33
+ puts "Publishing via default exchange..."
34
+ # messages must be routable & there must be at least one consumer.
35
+ ch1.default_exchange.publish("Routed via default_exchange", :routing_key => "amqpgem.examples.autorecovery.queue")
36
+ end
37
+
38
+ EventMachine.add_periodic_timer(0.8) do
39
+ puts "Publishing via amq.fanout..."
28
40
  # messages must be routable & there must be at least one consumer.
29
- exchange.publish("Hello", :immediate => true, :mandatory => true)
41
+ exchange.publish("Routed via amq.fanout", :immediate => true, :mandatory => true)
30
42
  end
31
43
 
32
44
 
@@ -36,7 +48,7 @@ AMQP.start(:host => "localhost") do |connection, open_ok|
36
48
 
37
49
  Signal.trap "TERM", show_stopper
38
50
  Signal.trap "INT", show_stopper
39
- EM.add_timer(15, show_stopper)
51
+ EM.add_timer(ENV.fetch("TIMER", 15), show_stopper)
40
52
 
41
53
  puts "This example a helper that publishes messages to amq.fanout. Use together with examples/error_handling/automatically_recovering_hello_world_consumer.rb."
42
54
  puts "This example terminates in 15 seconds and needs MANUAL RESTART when connection fails"
@@ -0,0 +1,67 @@
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
+
12
+ puts "=> Queue redeclaration with different attributes results in a channel exception that is handled"
13
+ puts
14
+ EventMachine.run do
15
+ connection = AMQP.connect
16
+ AMQP::Channel.new(connection, :auto_recovery => true) do |channel, open_ok|
17
+ puts "Channel ##{channel.id} is now open!"
18
+
19
+ connection.on_error do |conn, connection_close|
20
+ puts <<-ERR
21
+ Handling a connection-level exception:
22
+
23
+ connection_close.reply_text: #{connection_close.reply_text}
24
+ ERR
25
+ end
26
+
27
+ channel.on_error do |ch, channel_close|
28
+ puts <<-ERR
29
+ Handling a channel-level exception.
30
+
31
+ AMQP class id : #{channel_close.class_id},
32
+ AMQP method id: #{channel_close.method_id},
33
+ Status code : #{channel_close.reply_code}
34
+ Error message : #{channel_close.reply_text}
35
+ ERR
36
+
37
+ puts "Reusing channel #{ch.id}"
38
+ ch.reuse
39
+ puts "Channel id is now #{ch.id}"
40
+ end
41
+
42
+ EventMachine.add_timer(1.0) do
43
+ # these two definitions result in a race condition. For sake of this example,
44
+ # however, it does not matter. Whatever definition succeeds first, 2nd one will
45
+ # cause a channel-level exception (because attributes are not identical)
46
+ AMQP::Queue.new(channel, "amqpgem.examples.channel_exception", :auto_delete => true, :durable => false) do |queue|
47
+ puts "#{queue.name} is ready to go"
48
+ end
49
+
50
+ AMQP::Queue.new(channel, "amqpgem.examples.channel_exception", :auto_delete => true, :durable => true) do |queue|
51
+ puts "#{queue.name} is ready to go"
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ show_stopper = Proc.new do
58
+ $stdout.puts "Stopping..."
59
+
60
+ connection.close {
61
+ EM.stop { exit }
62
+ }
63
+ end
64
+
65
+ Signal.trap "INT", show_stopper
66
+ # EM.add_timer(2, show_stopper)
67
+ end
@@ -11,7 +11,7 @@ sleep(0.5)
11
11
  connection = AMQP.connect
12
12
  channel = AMQP::Channel.new(connection)
13
13
 
14
- # publish new commands every 3 seconds
14
+ # publish new commands every few seconds
15
15
  EventMachine.add_periodic_timer(10.0) do
16
16
  puts "Publishing a command (gems.install)"
17
17
  payload = { :gem => "rack", :version => "~> 1.3.0" }.to_yaml
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
4
+
5
+ require "amqp"
6
+ require "yaml"
7
+
8
+ t = Thread.new { EventMachine.run }
9
+ sleep(0.5)
10
+
11
+
12
+ connection = AMQP.connect
13
+ channel = AMQP::Channel.new(connection, :auto_recovery => true)
14
+ channel.on_error do |ch, channel_close|
15
+ raise "Channel-level exception: #{channel_close.reply_text}"
16
+ end
17
+
18
+ channel.prefetch(1)
19
+
20
+ channel.queue("", :durable => false, :auto_delete => true).bind("amqpgem.patterns.events").subscribe do |metadata, payload|
21
+ begin
22
+ body = YAML.load(payload)
23
+
24
+ case metadata.type
25
+ when "widgets.created" then
26
+ puts "A widget #{body[:id]} was created"
27
+ when "widgets.destroyed" then
28
+ puts "A widget #{body[:id]} was destroyed"
29
+ when "files.created" then
30
+ puts "A new file (#{body[:filename]}, #{body[:sha1]}) was uploaded"
31
+ when "files.indexed" then
32
+ puts "A new file (#{body[:filename]}, #{body[:sha1]}) was indexed"
33
+ else
34
+ puts "[warn] Do not know how to handle event of type #{metadata.type}"
35
+ end
36
+ rescue Exception => e
37
+ puts "[error] Could not handle event of type #{metadata.type}: #{e.inspect}"
38
+ end
39
+ end
40
+
41
+ puts "[boot] Ready"
42
+ Signal.trap("INT") { connection.close { EventMachine.stop } }
43
+ t.join
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
4
+
5
+ require "amqp"
6
+ require "yaml"
7
+
8
+ t = Thread.new { EventMachine.run }
9
+ sleep(0.5)
10
+
11
+ connection = AMQP.connect
12
+ channel = AMQP::Channel.new(connection)
13
+ exchange = channel.fanout("amqpgem.patterns.events", :durable => true, :auto_delete => false)
14
+
15
+
16
+ EVENTS = {
17
+ "pages.show" => {
18
+ :url => "https://mysite.local/widgets/81772",
19
+ :referrer => "http://www.google.com/search?client=safari&rls=en&q=widgets&ie=UTF-8&oe=UTF-8"
20
+ },
21
+ "widgets.created" => {
22
+ :id => 10,
23
+ :shape => "round",
24
+ :owner_id => 1000
25
+ },
26
+ "widgets.destroyed" => {
27
+ :id => 10,
28
+ :person_id => 1000
29
+ },
30
+ "files.created" => {
31
+ :sha1 => "1a62429f47bc8b405d17e84b648f2fbebc555ee5",
32
+ :filename => "document.pdf"
33
+ },
34
+ "files.indexed" => {
35
+ :sha1 => "1a62429f47bc8b405d17e84b648f2fbebc555ee5",
36
+ :filename => "document.pdf",
37
+ :runtime => 1.7623,
38
+ :shared => "shard02"
39
+ }
40
+ }
41
+
42
+ def generate_event
43
+ n = (EVENTS.size * Kernel.rand).floor
44
+ type = EVENTS.keys[n]
45
+ payload = EVENTS[type]
46
+
47
+ [type, payload]
48
+ end
49
+
50
+ # broadcast events
51
+ EventMachine.add_periodic_timer(2.0) do
52
+ event_type, payload = generate_event
53
+
54
+ puts "Publishing a new event of type #{event_type}"
55
+ exchange.publish(payload.to_yaml, :type => event_type)
56
+ end
57
+
58
+ puts "[boot] Ready. Will be publishing events every few seconds."
59
+ Signal.trap("INT") { connection.close { EventMachine.stop } }
60
+ t.join
@@ -0,0 +1,40 @@
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, :routing_key => "amqpgem.key")
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 "Received a message: #{payload}."
28
+ end
29
+
30
+ EventMachine.add_periodic_timer(1.0) do
31
+ exchange.publish("Hey, what a great view!", :routing_key => "amqpgem.key")
32
+ end
33
+
34
+ EventMachine.add_timer(3.0) do
35
+ queue.unsubscribe do
36
+ puts "Cancelled default consumer..."
37
+ connection.close { EventMachine.stop }
38
+ end
39
+ end
40
+ end
@@ -265,7 +265,7 @@ module AMQP
265
265
  @channel_is_open_deferrable.succeed
266
266
 
267
267
  # exchanges must be recovered first because queue recovery includes recovery of bindings. MK.
268
- @exchanges.each { |name, e| e.auto_recover }
268
+ @exchanges.each { |name, e| puts("Recovering ex #{name}"); e.auto_recover }
269
269
  @queues.each { |name, q| q.auto_recover }
270
270
  end
271
271
  end # auto_recover
@@ -423,7 +423,7 @@ module AMQP
423
423
  # @return [Exchange]
424
424
  # @api public
425
425
  def default_exchange
426
- Exchange.default(self)
426
+ @default_exchange ||= Exchange.default(self)
427
427
  end
428
428
 
429
429
  # Defines, intializes and returns a fanout Exchange instance.
@@ -300,12 +300,12 @@ module AMQP
300
300
  @channel = channel
301
301
  @type = type
302
302
  @opts = self.class.add_default_options(type, name, opts, block)
303
- @default_routing_key = opts[:routing_key] || opts[:key]
303
+ @default_routing_key = opts[:routing_key] || opts[:key] || AMQ::Protocol::EMPTY_STRING
304
304
  @name = name unless name.empty?
305
305
 
306
306
  @status = :unknown
307
307
  @default_publish_options = (opts.delete(:default_publish_options) || {
308
- :routing_key => AMQ::Protocol::EMPTY_STRING,
308
+ :routing_key => @default_routing_key,
309
309
  :mandatory => false,
310
310
  :immediate => false
311
311
  }).freeze
@@ -775,7 +775,7 @@ module AMQP
775
775
  # unsubscription request is acknowledged as complete by the server.
776
776
  #
777
777
  # @option opts [Boolean] :nowait (true) If set, the server will not respond to the method. The client should
778
- # not wait for a reply method. If the server could not complete the
778
+ # not wait for a reply method, the callback (if passed) will be ignored. If the server could not complete the
779
779
  # method it will raise a channel or connection exception.
780
780
  #
781
781
  # @yield [cancel_ok]
@@ -6,5 +6,5 @@ module AMQP
6
6
  #
7
7
  # @see AMQ::Protocol::VERSION
8
8
  # @return [String] AMQP gem version
9
- VERSION = '0.8.4'
9
+ VERSION = '0.9.0.pre1'
10
10
  end
@@ -23,6 +23,75 @@ describe AMQP::Channel do
23
23
  # Examples
24
24
  #
25
25
 
26
+
27
+ describe "default exchange" do
28
+ subject do
29
+ @channel.default_exchange
30
+ end
31
+
32
+ it "is predefined" do
33
+ subject.should be_predefined
34
+ done
35
+ end
36
+ end
37
+
38
+ describe "exchange named amq.direct" do
39
+ subject do
40
+ @channel.direct("amq.direct")
41
+ end
42
+
43
+ it "is predefined" do
44
+ subject.should be_predefined
45
+ done
46
+ end
47
+ end
48
+
49
+ describe "exchange named amq.fanout" do
50
+ subject do
51
+ @channel.direct("amq.fanout")
52
+ end
53
+
54
+ it "is predefined" do
55
+ subject.should be_predefined
56
+ done
57
+ end
58
+ end
59
+
60
+ describe "exchange named amq.topic" do
61
+ subject do
62
+ @channel.direct("amq.topic")
63
+ end
64
+
65
+ it "is predefined" do
66
+ subject.should be_predefined
67
+ done
68
+ end
69
+ end
70
+
71
+ describe "exchange named amq.match" do
72
+ subject do
73
+ @channel.direct("amq.match")
74
+ end
75
+
76
+ it "is predefined" do
77
+ subject.should be_predefined
78
+ done
79
+ end
80
+ end
81
+
82
+ describe "exchange named amq.headers" do
83
+ subject do
84
+ @channel.direct("amq.headers")
85
+ end
86
+
87
+ it "is predefined" do
88
+ subject.should be_predefined
89
+ done
90
+ end
91
+ end
92
+
93
+
94
+
26
95
  describe "#direct" do
27
96
  context "when exchange name is specified" do
28
97
  it 'declares a new direct exchange with that name' do
@@ -61,7 +61,7 @@ if mri?
61
61
  queue = @channel.queue("amqpgem.tests.concurrent_publishing", :auto_delete => true)
62
62
  exchange = @channel.default_exchange
63
63
  exchange.on_return do |method, header, body|
64
- raise "Message was returned: #{method.reply_text}"
64
+ puts "Message was returned: #{method.reply_text}"
65
65
  end
66
66
 
67
67
  queue.subscribe do |metadata, payload|
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amqp
3
3
  version: !ruby/object:Gem::Version
4
- hash: 55
5
- prerelease:
4
+ hash: 1923831867
5
+ prerelease: 6
6
6
  segments:
7
7
  - 0
8
- - 8
9
- - 4
10
- version: 0.8.4
8
+ - 9
9
+ - 0
10
+ - pre
11
+ - 1
12
+ version: 0.9.0.pre1
11
13
  platform: ruby
12
14
  authors:
13
15
  - Aman Gupta
@@ -17,7 +19,7 @@ autorequire:
17
19
  bindir: bin
18
20
  cert_chain: []
19
21
 
20
- date: 2011-12-04 00:00:00 Z
22
+ date: 2012-01-01 00:00:00 Z
21
23
  dependencies:
22
24
  - !ruby/object:Gem::Dependency
23
25
  name: eventmachine
@@ -131,6 +133,8 @@ files:
131
133
  - docs/diagrams/003_weathr_example_routing.png
132
134
  - docs/diagrams/004_fanout_exchange.png
133
135
  - docs/diagrams/005_direct_exchange.png
136
+ - docs/diagrams/006_amqp_091_message_acknowledgements.png
137
+ - docs/diagrams/007_rabbitmq_publisher_confirms.png
134
138
  - docs/diagrams/redhat/direct_exchange.png
135
139
  - docs/diagrams/redhat/fanout_exchange.png
136
140
  - docs/diagrams/redhat/topic_exchange.png
@@ -159,6 +163,7 @@ files:
159
163
  - examples/error_handling/manual_connection_and_channel_recovery.rb
160
164
  - examples/error_handling/queue_exclusivity_violation.rb
161
165
  - examples/error_handling/queue_name_violation.rb
166
+ - examples/error_handling/reopening_a_channel_after_channel_level_exception.rb
162
167
  - examples/error_handling/tcp_connection_failure_handling_with_a_rescue_block.rb
163
168
  - examples/error_handling/tcp_connection_failure_with_a_callback.rb
164
169
  - examples/exchanges/autodeletion_of_exchanges.rb
@@ -210,6 +215,8 @@ files:
210
215
  - examples/legacy/stocks.rb
211
216
  - examples/patterns/command/consumer.rb
212
217
  - examples/patterns/command/producer.rb
218
+ - examples/patterns/event/consumer.rb
219
+ - examples/patterns/event/producer.rb
213
220
  - examples/patterns/request_reply/client.rb
214
221
  - examples/patterns/request_reply/server.rb
215
222
  - examples/publishing/publishing_a_one_off_message.rb
@@ -219,6 +226,7 @@ files:
219
226
  - examples/queues/accessing_message_metadata.rb
220
227
  - examples/queues/automatic_binding_for_default_direct_exchange.rb
221
228
  - examples/queues/basic_get.rb
229
+ - examples/queues/cancel_default_consumer.rb
222
230
  - examples/queues/declare_a_queue_without_assignment.rb
223
231
  - examples/queues/declare_and_bind_a_server_named_queue.rb
224
232
  - examples/queues/queue_status.rb
@@ -348,12 +356,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
348
356
  required_rubygems_version: !ruby/object:Gem::Requirement
349
357
  none: false
350
358
  requirements:
351
- - - ">="
359
+ - - ">"
352
360
  - !ruby/object:Gem::Version
353
- hash: 3
361
+ hash: 25
354
362
  segments:
355
- - 0
356
- version: "0"
363
+ - 1
364
+ - 3
365
+ - 1
366
+ version: 1.3.1
357
367
  requirements: []
358
368
 
359
369
  rubyforge_project: amqp