hot_bunnies 1.3.8-java → 1.4.0-java

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ Gemfile.lock
4
4
  pkg/*
5
5
  .DS_Store
6
6
  .rvmrc
7
+ bin/*
data/.travis.yml CHANGED
@@ -7,3 +7,6 @@ rvm:
7
7
  notifications:
8
8
  recipients:
9
9
  - michaelklishin@me.com
10
+
11
+ services:
12
+ - rabbitmq
data/ChangeLog.md ADDED
@@ -0,0 +1,33 @@
1
+ # Changes Between 1.3.0 and 1.4.0
2
+
3
+ ## RabbitMQ Java Client Upgrade
4
+
5
+ Hot Bunnies now uses RabbitMQ Java client 2.8.7.
6
+
7
+
8
+ ## TLS Support
9
+
10
+ `HotBunnies.connect` now supports a new `:tls` option:
11
+
12
+ ``` ruby
13
+ HotBunnies.connect(:tls => true)
14
+
15
+ HotBunnies.connect(:tls => "SSLv3")
16
+ HotBunnies.connect(:tls => "SSLv2")
17
+
18
+ HotBunnies.connect(:tls => "SSLv3", :trust_manager => custom_trust_manager)
19
+ ```
20
+
21
+
22
+ ## Consumer Back Pressure Improvements
23
+
24
+ * The async consumer will not attempt to add tasks when its executor is shutting down.
25
+
26
+ * The blocking consumer got a buffer size option that makes it create a bounded blocking queue instead of an unbounded.
27
+
28
+
29
+ ## Consumer Improvements
30
+
31
+ `HotBunnies::Queue#subscribe` is now more resilient to exceptions and uses a new
32
+ executor task for each delivery. When a consumer is cancelled, any remaining messages
33
+ will be delivered instead of being ignored.
data/README.md CHANGED
@@ -25,6 +25,22 @@ Hot Bunnies is not
25
25
  * A cure for cancer
26
26
 
27
27
 
28
+ ## Installation, Dependency
29
+
30
+ ### With Rubygems
31
+
32
+ gem install hot_bunnies
33
+
34
+ ### With Bundler
35
+
36
+ gem "hot_bunnies", "~> 1.4.0"
37
+
38
+
39
+ ## Change Log
40
+
41
+ See ChangeLog.md.
42
+
43
+
28
44
  ## Continuous Integration
29
45
 
30
46
  [![Continuous Integration status](https://secure.travis-ci.org/ruby-amqp/hot_bunnies.png)](http://travis-ci.org/ruby-amqp/hot_bunnies)
@@ -39,4 +55,4 @@ MIT, see LICENSE in the repository root
39
55
 
40
56
  ## Copyright
41
57
 
42
- Theo Hultberg, 2011.
58
+ Theo Hultberg, Michael Klishin, 2011-2012.
Binary file
@@ -1,5 +1,15 @@
1
1
  # encoding: utf-8
2
2
 
3
+ module JavaConcurrent
4
+ java_import 'java.lang.Thread'
5
+ java_import 'java.lang.InterruptedException'
6
+ java_import 'java.util.concurrent.Executors'
7
+ java_import 'java.util.concurrent.LinkedBlockingQueue'
8
+ java_import 'java.util.concurrent.ArrayBlockingQueue'
9
+ java_import 'java.util.concurrent.TimeUnit'
10
+ java_import 'java.util.concurrent.atomic.AtomicBoolean'
11
+ end
12
+
3
13
  module HotBunnies
4
14
  class Queue
5
15
  attr_reader :name, :channel
@@ -59,6 +69,8 @@ module HotBunnies
59
69
  end
60
70
 
61
71
  class Subscription
72
+ include JavaConcurrent
73
+
62
74
  attr_reader :channel, :queue_name, :consumer_tag
63
75
 
64
76
  def initialize(channel, queue_name, options={})
@@ -66,28 +78,25 @@ module HotBunnies
66
78
  @queue_name = queue_name
67
79
  @ack = options.fetch(:ack, false)
68
80
 
69
- @cancelled = java.util.concurrent.atomic.AtomicBoolean.new(false)
81
+ @cancelled = AtomicBoolean.new(false)
70
82
  end
71
83
 
72
84
  def each(options={}, &block)
73
- raise 'The subscription already has a message listener' if @subscriber
74
- if options.fetch(:blocking, true)
75
- run(&block)
76
- else
77
- if options[:executor]
78
- @shut_down_executor = false
79
- @executor = options[:executor]
80
- else
81
- @shut_down_executor = true
82
- @executor = java.util.concurrent.Executors.new_single_thread_executor
83
- end
84
- @executor.submit { run(&block) }
85
- end
85
+ raise 'The subscription already has a message listener' if @consumer
86
+ start(create_consumer(options, block))
87
+ nil
88
+ end
89
+ alias_method :each_message, :each
90
+
91
+ def start(consumer)
92
+ @consumer = consumer
93
+ @consumer_tag = @channel.basic_consume(@queue_name, !@ack, @consumer)
94
+ @consumer.start
86
95
  end
87
96
 
88
97
  def cancel
89
98
  raise 'Can\'t cancel: the subscriber haven\'t received an OK yet' if !self.active?
90
- @channel.basic_cancel(@subscriber.consumer_tag)
99
+ @consumer.cancel
91
100
 
92
101
  # RabbitMQ Java client won't clear consumer_tag from cancelled consumers,
93
102
  # so we have to do this. Sharing consumers
@@ -103,7 +112,7 @@ module HotBunnies
103
112
  end
104
113
 
105
114
  def active?
106
- !@cancelled.get && !@subscriber.nil? && !@subscriber.consumer_tag.nil?
115
+ !@cancelled.get && !@consumer.nil? && !@consumer.consumer_tag.nil?
107
116
  end
108
117
 
109
118
  def shutdown!
@@ -111,96 +120,198 @@ module HotBunnies
111
120
  @executor.shutdown_now
112
121
  end
113
122
  end
123
+ alias shut_down! shutdown!
114
124
 
115
125
  private
116
126
 
117
127
  def maybe_shutdown_executor
118
128
  if @executor && @shut_down_executor
119
129
  @executor.shutdown
130
+ unless @executor.await_termination(1, TimeUnit::SECONDS)
131
+ @executor.shutdown_now
132
+ end
120
133
  end
121
134
  end
122
135
 
123
- def run(&block)
124
- @subscriber = BlockingSubscriber.new(@channel, self)
125
- @consumer_tag = @channel.basic_consume(@queue_name, !@ack, @subscriber.consumer)
126
- @subscriber.on_message(&block)
136
+ def create_consumer(options, callback)
137
+ if options.fetch(:blocking, true)
138
+ BlockingCallbackConsumer.new(@channel, options[:buffer_size], callback)
139
+ else
140
+ if options[:executor]
141
+ @shut_down_executor = false
142
+ @executor = options[:executor]
143
+ else
144
+ @shut_down_executor = true
145
+ @executor = Executors.new_single_thread_executor
146
+ end
147
+ AsyncCallbackConsumer.new(@channel, callback, @executor)
148
+ end
127
149
  end
128
150
  end
129
151
 
130
- class Headers
131
- attr_reader :channel, :consumer_tag, :envelope, :properties
152
+ public
132
153
 
133
- def initialize(channel, consumer_tag, envelope, properties)
134
- @channel = channel
135
- @consumer_tag = consumer_tag
136
- @envelope = envelope
137
- @properties = properties
154
+ class BaseConsumer < DefaultConsumer
155
+ def handleDelivery(consumer_tag, envelope, properties, body)
156
+ body = String.from_java_bytes(body)
157
+ headers = Headers.new(channel, consumer_tag, envelope, properties)
158
+ deliver(headers, body)
138
159
  end
139
160
 
140
- def ack(options={})
141
- @channel.basic_ack(delivery_tag, options.fetch(:multiple, false))
161
+ def handleCancel(consumer_tag)
162
+ @cancelled = true
142
163
  end
143
164
 
144
- def reject(options={})
145
- @channel.basic_reject(delivery_tag, options.fetch(:requeue, false))
165
+ def handleCancelOk(consumer_tag)
166
+ @cancelled = true
167
+ end
168
+
169
+ def start
146
170
  end
147
171
 
148
- def delivery_tag
149
- @envelope.delivery_tag
172
+ def deliver(headers, message)
173
+ raise NotImplementedError, 'To be implemented by a subclass'
150
174
  end
151
175
 
152
- def routing_key
153
- @envelope.routing_key
176
+ def cancel
177
+ channel.basic_cancel(consumer_tag)
178
+ @cancelling = true
154
179
  end
155
180
  end
156
181
 
157
- module Subscriber
158
- def start
159
- # to be implemented by the host class
182
+ private
183
+
184
+ class CallbackConsumer < BaseConsumer
185
+ def initialize(channel, callback)
186
+ super(channel)
187
+ @callback = callback
188
+ @callback_arity = @callback.arity
189
+ @cancelled = false
190
+ @cancelling = false
160
191
  end
161
192
 
162
- def on_message(&block)
163
- raise ArgumentError, 'Message listener already registered for this subscriber' if @subscriber
164
- @subscriber = block
165
- start
193
+ def callback(headers, message)
194
+ if @callback_arity == 2
195
+ @callback.call(headers, message)
196
+ else
197
+ @callback.call(message)
198
+ end
199
+ end
200
+ end
201
+
202
+ class AsyncCallbackConsumer < CallbackConsumer
203
+ def initialize(channel, callback, executor)
204
+ super(channel, callback)
205
+ @executor = executor
206
+ @tasks = []
166
207
  end
167
208
 
168
- def handle_message(consumer_tag, envelope, properties, body_bytes)
169
- body = String.from_java_bytes(body_bytes)
170
- case @subscriber.arity
171
- when 2 then @subscriber.call(Headers.new(@channel, consumer_tag, envelope, properties), body)
172
- when 1 then @subscriber.call(body)
173
- else raise ArgumentError, 'Consumer callback wants no arguments'
209
+ def deliver(headers, message)
210
+ unless @executor.shutdown?
211
+ @executor.submit do
212
+ callback(headers, message)
213
+ end
174
214
  end
175
215
  end
176
216
  end
177
217
 
178
- class BlockingSubscriber
179
- include Subscriber
218
+ class BlockingCallbackConsumer < CallbackConsumer
219
+ include JavaConcurrent
220
+
221
+ def initialize(channel, buffer_size, callback)
222
+ super(channel, callback)
223
+ if buffer_size
224
+ @internal_queue = ArrayBlockingQueue.new(buffer_size)
225
+ else
226
+ @internal_queue = LinkedBlockingQueue.new
227
+ end
228
+ end
229
+
230
+ def start
231
+ interrupted = false
232
+ until @cancelled || JavaConcurrent::Thread.current_thread.interrupted?
233
+ begin
234
+ pair = @internal_queue.take
235
+ callback(*pair) if pair
236
+ rescue InterruptedException => e
237
+ interrupted = true
238
+ end
239
+ end
240
+ while (pair = @internal_queue.poll)
241
+ callback(*pair)
242
+ end
243
+ if interrupted
244
+ JavaConcurrent::Thread.current_thread.interrupt
245
+ end
246
+ end
247
+
248
+ def deliver(*pair)
249
+ if @cancelling || @cancelled || JavaConcurrent::Thread.current_thread.interrupted?
250
+ @internal_queue.offer(pair)
251
+ else
252
+ begin
253
+ @internal_queue.put(pair)
254
+ rescue InterruptedException => e
255
+ JavaConcurrent::Thread.current_thread.interrupt
256
+ end
257
+ end
258
+ end
259
+ end
180
260
 
181
- attr_reader :consumer
261
+ class Headers
262
+ attr_reader :channel, :consumer_tag, :envelope, :properties
182
263
 
183
- def initialize(channel, subscription)
264
+ def initialize(channel, consumer_tag, envelope, properties)
184
265
  @channel = channel
185
- @subscription = subscription
186
- @consumer = QueueingConsumer.new(@channel)
266
+ @consumer_tag = consumer_tag
267
+ @envelope = envelope
268
+ @properties = properties
187
269
  end
188
270
 
189
- def consumer_tag
190
- @consumer.consumer_tag
271
+ def ack(options={})
272
+ @channel.basic_ack(delivery_tag, options.fetch(:multiple, false))
191
273
  end
192
274
 
193
- def start
194
- super
195
- while delivery = @consumer.next_delivery
196
- result = handle_message(@consumer.consumer_tag, delivery.envelope, delivery.properties, delivery.body)
197
- if result == :cancel
198
- @subscription.cancel
199
- while delivery = @consumer.next_delivery(0)
200
- handle_message(@consumer.consumer_tag, delivery.envelope, delivery.properties, delivery.body)
201
- end
202
- break
203
- end
275
+ def reject(options={})
276
+ @channel.basic_reject(delivery_tag, options.fetch(:requeue, false))
277
+ end
278
+
279
+ begin :envelope_delegation
280
+ [
281
+ :delivery_tag,
282
+ :routing_key,
283
+ :redeliver,
284
+ :exchange
285
+ ].each do |envelope_property|
286
+ define_method(envelope_property) { @envelope.__send__(envelope_property) }
287
+ end
288
+
289
+ alias_method :redelivered?, :redeliver
290
+ end
291
+
292
+ begin :message_properties_delegation
293
+ [
294
+ :content_encoding,
295
+ :content_type,
296
+ :content_encoding,
297
+ :headers,
298
+ :delivery_mode,
299
+ :priority,
300
+ :correlation_id,
301
+ :reply_to,
302
+ :expiration,
303
+ :message_id,
304
+ :timestamp,
305
+ :type,
306
+ :user_id,
307
+ :app_id,
308
+ :cluster_id
309
+ ].each do |properties_property|
310
+ define_method(properties_property) { @properties.__send__(properties_property) }
311
+ end
312
+
313
+ def persistent?
314
+ persistent == 2
204
315
  end
205
316
  end
206
317
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module HotBunnies
4
- VERSION = '1.3.8'
4
+ VERSION = '1.4.0'
5
5
  end
data/lib/hot_bunnies.rb CHANGED
@@ -6,13 +6,11 @@ require 'ext/rabbitmq-client'
6
6
 
7
7
 
8
8
  module HotBunnies
9
- import com.rabbitmq.client.ConnectionFactory
10
- import com.rabbitmq.client.Connection
11
- import com.rabbitmq.client.Channel
12
- import com.rabbitmq.client.DefaultConsumer
13
- import com.rabbitmq.client.QueueingConsumer
14
-
15
- import com.rabbitmq.client.AMQP
9
+ java_import 'com.rabbitmq.client.ConnectionFactory'
10
+ java_import 'com.rabbitmq.client.Connection'
11
+ java_import 'com.rabbitmq.client.Channel'
12
+ java_import 'com.rabbitmq.client.DefaultConsumer'
13
+ java_import 'com.rabbitmq.client.AMQP'
16
14
 
17
15
  def self.connect(options={})
18
16
  cf = ConnectionFactory.new
@@ -28,10 +26,21 @@ module HotBunnies
28
26
  cf.requested_heartbeat = heartbeat_from(options) if include_heartbeat?(options)
29
27
  cf.connection_timeout = connection_timeout_from(options) if include_connection_timeout?(options)
30
28
 
29
+ tls = (options[:ssl] || options[:tls])
30
+ case tls
31
+ when true then
32
+ cf.use_ssl_protocol
33
+ when String then
34
+ if options[:trust_manager]
35
+ cf.use_ssl_protocol(tls, options[:trust_manager])
36
+ else
37
+ cf.use_ssl_protocol(tls)
38
+ end
39
+ end
40
+
31
41
  cf.new_connection
32
42
  end
33
43
 
34
-
35
44
  protected
36
45
 
37
46
  def self.hostname_from(options)
@@ -1,5 +1,104 @@
1
1
  require "spec_helper"
2
2
 
3
+
4
+ describe 'A consumer of a queue' do
5
+ let(:connection) { HotBunnies.connect }
6
+ let(:channel) { connection.create_channel }
7
+
8
+ after :each do
9
+ channel.close
10
+ connection.close
11
+ end
12
+
13
+ it 'receives messages until cancelled' do
14
+ exchange = connection.create_channel.default_exchange
15
+ queue = connection.create_channel.queue("", :auto_delete => true)
16
+ subscription = queue.subscribe
17
+
18
+ messages = []
19
+ consumer_exited = false
20
+
21
+ consumer_thread = Thread.new do
22
+ subscription.each do |headers, message|
23
+ messages << message
24
+ sleep 0.1
25
+ end
26
+ consumer_exited = true
27
+ end
28
+
29
+ publisher_thread = Thread.new do
30
+ 20.times do
31
+ exchange.publish('hello world', :routing_key => queue.name)
32
+ sleep 0.01
33
+ end
34
+ end
35
+
36
+ sleep 0.2
37
+
38
+ subscription.cancel
39
+
40
+ consumer_thread.join
41
+ publisher_thread.join
42
+
43
+ messages.should_not be_empty
44
+ consumer_exited.should be_true
45
+ end
46
+ end
47
+
48
+ describe "Multiple non-exclusive consumers per queue" do
49
+ let(:connection) { HotBunnies.connect }
50
+ let(:channel) { connection.create_channel }
51
+
52
+ after :each do
53
+ channel.close
54
+ connection.close
55
+ end
56
+
57
+ context "on the same channel (so prefetch levels won't affect message distribution)" do
58
+ it "have messages distributed to them in the round robin manner" do
59
+ n = 100
60
+ mailbox1 = []
61
+ mailbox2 = []
62
+ mailbox3 = []
63
+
64
+ all_received = java.util.concurrent.CountDownLatch.new(n)
65
+ consumer_channel = connection.create_channel
66
+
67
+ queue = channel.queue("", :auto_delete => true)
68
+
69
+ consumer1 = queue.subscribe(:blocking => false) do |metadata, payload|
70
+ mailbox1 << payload
71
+ all_received.count_down
72
+ end
73
+ consumer2 = queue.subscribe(:blocking => false) do |metadata, payload|
74
+ mailbox2 << payload
75
+ all_received.count_down
76
+ end
77
+ consumer3 = queue.subscribe(:blocking => false) do |metadata, payload|
78
+ mailbox3 << payload
79
+ all_received.count_down
80
+ end
81
+
82
+
83
+ sleep 2.0 # let consumers in other threads start.
84
+ n.times do |i|
85
+ channel.default_exchange.publish("Message #{i}", :routing_key => queue.name)
86
+ end
87
+
88
+ all_received.await
89
+
90
+ mailbox1.size.should >= 33
91
+ mailbox2.size.should >= 33
92
+ mailbox3.size.should >= 33
93
+
94
+ consumer1.shutdown!
95
+ consumer2.shutdown!
96
+ consumer3.shutdown!
97
+ end
98
+ end
99
+ end
100
+
101
+
3
102
  describe "Queue consumer" do
4
103
  let(:connection) { HotBunnies.connect }
5
104
  let(:channel) { connection.create_channel }
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
 
4
- describe "HotBunnies" do
4
+ describe "HotBunnies.connect" do
5
5
 
6
6
  #
7
7
  # Examples
@@ -16,4 +16,11 @@ describe "HotBunnies" do
16
16
  c1 = HotBunnies.connect(:connection_timeout => 3)
17
17
  c1.close
18
18
  end
19
+
20
+ if !ENV["CI"] && ENV["TLS_TESTS"]
21
+ it "supports TLS w/o custom protocol or trust manager" do
22
+ c1 = HotBunnies.connect(:tls => true, :port => 5671)
23
+ c1.close
24
+ end
25
+ end
19
26
  end
@@ -0,0 +1,97 @@
1
+ require "spec_helper"
2
+
3
+ describe "An AMQP consumer that catches exceptions" do
4
+ let(:connection) { HotBunnies.connect }
5
+ let(:channel) { connection.create_channel }
6
+
7
+ after :each do
8
+ channel.close
9
+ connection.close
10
+ end
11
+
12
+ it "stays up" do
13
+ mailbox = []
14
+ exchange = channel.exchange("hot_bunnies.exchanges.fanout#{Time.now.to_i}", :type => :fanout, :auto_delete => true)
15
+ queue = channel.queue("", :auto_delete => true)
16
+
17
+ queue.bind(exchange)
18
+ consumer = queue.subscribe(:blocking => false) do |meta, payload|
19
+ n = meta.properties.headers['X-Number']
20
+
21
+ begin
22
+ if n.odd?
23
+ raise "A failure"
24
+ else
25
+ mailbox << payload
26
+ end
27
+ rescue Exception => e
28
+ # no-op
29
+ end
30
+ end
31
+
32
+ 25.times do |i|
33
+ exchange.publish("Message ##{i}", :routing_key => "xyz", :properties => {
34
+ :headers => {
35
+ 'X-Number' => i
36
+ }
37
+ })
38
+ end
39
+
40
+ sleep(0.5)
41
+
42
+ mc, cc = queue.status
43
+ mc.should == 0
44
+
45
+ mailbox.size.should == 13
46
+ consumer.shutdown!
47
+ end
48
+ end
49
+
50
+
51
+
52
+
53
+ describe "An AMQP consumer that DOES NOT catch exceptions" do
54
+ let(:connection) { HotBunnies.connect }
55
+ let(:channel) { connection.create_channel }
56
+
57
+ after :each do
58
+ channel.close
59
+ connection.close
60
+ end
61
+
62
+ it "becomes inactive when the channels prefetch is filled with unacked messages" do
63
+ mailbox = []
64
+ exchange = channel.exchange("hot_bunnies.exchanges.fanout#{Time.now.to_i}#{rand}", :type => :fanout, :auto_delete => true)
65
+ queue = channel.queue("", :auto_delete => true)
66
+
67
+ channel.prefetch = 5
68
+
69
+ queue.bind(exchange)
70
+ consumer = queue.subscribe(:blocking => false, :ack => true) do |meta, payload|
71
+ n = meta.properties.headers['X-Number']
72
+
73
+ if n.odd?
74
+ raise "A failure"
75
+ else
76
+ mailbox << payload
77
+ meta.ack
78
+ end
79
+ end
80
+
81
+ 25.times do |i|
82
+ exchange.publish("Message ##{i}", :routing_key => "xyz", :properties => {
83
+ :headers => {
84
+ 'X-Number' => i
85
+ }
86
+ })
87
+ end
88
+
89
+ sleep(0.5)
90
+
91
+ message_count, _ = queue.status
92
+ message_count.should == 15
93
+
94
+ mailbox.size.should == 5
95
+ consumer.shutdown!
96
+ end
97
+ end
@@ -0,0 +1,94 @@
1
+ require "spec_helper"
2
+
3
+ describe "An AMQP consumer" do
4
+ let(:connection) { HotBunnies.connect }
5
+ let(:channel) { connection.create_channel }
6
+
7
+ after :each do
8
+ channel.close
9
+ connection.close
10
+ end
11
+
12
+ it "can access message metadata (both message properties and delivery information)" do
13
+ latch = java.util.concurrent.CountDownLatch.new(1)
14
+ queue = channel.queue("", :auto_delete => true)
15
+ exchange = channel.exchange("amq.fanout", :type => :fanout)
16
+
17
+ queue.bind(exchange, :routing_key => "hotbunnies.key")
18
+
19
+ @now = Time.now
20
+ @payload = "Hello, world!"
21
+ @meta = nil
22
+
23
+ consumer = queue.subscribe(:blocking => false) do |metadata, payload|
24
+ begin
25
+ # we will run assertions on the main thread because RSpec uses exceptions
26
+ # for its purposes every once in a while. MK.
27
+ @meta = metadata
28
+ rescue Exception => e
29
+ e.print_stack_trace
30
+ ensure
31
+ latch.count_down
32
+ end
33
+ end
34
+
35
+ exchange.publish(@payload,
36
+ :properties => { :app_id => "hotbunnies.tests",
37
+ :priority => 8,
38
+ :type => "kinda.checkin",
39
+ # headers table keys can be anything
40
+ :headers => {
41
+ "coordinates" => {
42
+ "latitude" => 59.35,
43
+ "longitude" => 18.066667
44
+ },
45
+ "time" => @now,
46
+ "participants" => 11,
47
+ "venue" => "Stockholm",
48
+ "true_field" => true,
49
+ "false_field" => false,
50
+ "nil_field" => nil,
51
+ "ary_field" => ["one", 2.0, 3, [{ "abc" => 123 }]]
52
+ },
53
+ :timestamp => @now,
54
+ :reply_to => "a.sender",
55
+ :correlation_id => "r-1",
56
+ :message_id => "m-1",
57
+ :content_type => "application/octet-stream",
58
+ # just an example. MK.
59
+ :content_encoding => "zip/zap"
60
+ },
61
+ :routing_key => "hotbunnies.key")
62
+ latch.await
63
+
64
+ @meta.routing_key.should == "hotbunnies.key"
65
+ @meta.content_type.should == "application/octet-stream"
66
+ @meta.content_encoding.should == "zip/zap"
67
+ @meta.priority.should == 8
68
+
69
+ time = Time.at(@meta.headers["time"].getTime/1000)
70
+ time.to_i.should == @now.to_i
71
+
72
+ @meta.headers["coordinates"]["latitude"].should == 59.35
73
+ @meta.headers["participants"].should == 11
74
+ @meta.headers["true_field"].should == true
75
+ @meta.headers["false_field"].should == false
76
+ @meta.headers["nil_field"].should be_nil
77
+
78
+ @meta.timestamp.should == Time.at(@now.to_i)
79
+ @meta.type.should == "kinda.checkin"
80
+ @meta.consumer_tag.should_not be_nil
81
+ @meta.consumer_tag.should_not be_empty
82
+ @meta.delivery_tag.should == 1
83
+ @meta.reply_to.should == "a.sender"
84
+ @meta.correlation_id.should == "r-1"
85
+ @meta.message_id.should == "m-1"
86
+ @meta.should_not be_redelivered
87
+
88
+ @meta.app_id.should == "hotbunnies.tests"
89
+ @meta.exchange.should == "amq.fanout"
90
+
91
+ consumer.shut_down!
92
+ end
93
+ end
94
+
data/spec/spec_helper.rb CHANGED
@@ -13,4 +13,6 @@ when "1.8.7" then
13
13
  end
14
14
 
15
15
 
16
- require "hot_bunnies"
16
+ require "hot_bunnies"
17
+
18
+ puts "Running on Ruby #{RUBY_VERSION}."
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: hot_bunnies
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.3.8
5
+ version: 1.4.0
6
6
  platform: java
7
7
  authors:
8
8
  - Theo Hultberg
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2012-06-07 00:00:00 Z
14
+ date: 2012-11-14 00:00:00 Z
15
15
  dependencies: []
16
16
 
17
17
  description: A object oriented interface to RabbitMQ that uses the Java driver under the hood
@@ -27,6 +27,7 @@ files:
27
27
  - .gitignore
28
28
  - .rvmrc
29
29
  - .travis.yml
30
+ - ChangeLog.md
30
31
  - Gemfile
31
32
  - LICENSE
32
33
  - README.md
@@ -45,8 +46,10 @@ files:
45
46
  - spec/integration/alternate_exchanges_spec.rb
46
47
  - spec/integration/basic_consume_spec.rb
47
48
  - spec/integration/connection_spec.rb
49
+ - spec/integration/error_handling_by_consumers_spec.rb
48
50
  - spec/integration/exchange_bind_spec.rb
49
51
  - spec/integration/exchange_declare_spec.rb
52
+ - spec/integration/message_metadata_access_spec.rb
50
53
  - spec/integration/publisher_confirmations_spec.rb
51
54
  - spec/integration/queue_bind_spec.rb
52
55
  - spec/integration/queue_declare_spec.rb