hot_bunnies 1.3.8-java → 1.4.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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