message-driver 0.4.0 → 0.5.0

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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +2 -0
  3. data/.rubocop.yml +26 -2
  4. data/.rubocop_todo.yml +15 -123
  5. data/.travis.yml +2 -1
  6. data/CHANGELOG.md +10 -1
  7. data/Gemfile +15 -6
  8. data/Rakefile +23 -12
  9. data/ci/reset_vhost +8 -3
  10. data/ci/travis_setup +0 -3
  11. data/features/.nav +6 -1
  12. data/features/CHANGELOG.md +10 -1
  13. data/features/amqp_specific_features/binding_amqp_destinations.feature +1 -0
  14. data/features/amqp_specific_features/declaring_amqp_exchanges.feature +1 -0
  15. data/features/amqp_specific_features/server_named_destinations.feature +1 -0
  16. data/features/destination_metadata.feature +26 -0
  17. data/features/logging.feature +1 -1
  18. data/features/middleware/middleware_basics.feature +91 -0
  19. data/features/middleware/middleware_ordering.feature +60 -0
  20. data/features/middleware/middleware_parameters.feature +43 -0
  21. data/features/middleware/middleware_with_blocks.feature +85 -0
  22. data/features/step_definitions/dynamic_destinations_steps.rb +1 -1
  23. data/features/step_definitions/message_consumers_steps.rb +5 -0
  24. data/features/step_definitions/middleware_steps.rb +10 -0
  25. data/features/step_definitions/steps.rb +10 -2
  26. data/features/support/env.rb +4 -3
  27. data/features/support/firewall_helper.rb +1 -1
  28. data/features/support/message_table_matcher.rb +3 -2
  29. data/features/support/no_error_matcher.rb +2 -2
  30. data/features/support/test_runner.rb +11 -57
  31. data/features/support/transforms.rb +12 -10
  32. data/lib/message_driver.rb +3 -1
  33. data/lib/message_driver/adapters/base.rb +11 -11
  34. data/lib/message_driver/adapters/bunny_adapter.rb +189 -132
  35. data/lib/message_driver/adapters/in_memory_adapter.rb +51 -34
  36. data/lib/message_driver/adapters/stomp_adapter.rb +22 -23
  37. data/lib/message_driver/broker.rb +21 -16
  38. data/lib/message_driver/client.rb +15 -16
  39. data/lib/message_driver/destination.rb +26 -8
  40. data/lib/message_driver/message.rb +5 -4
  41. data/lib/message_driver/middleware.rb +8 -0
  42. data/lib/message_driver/middleware/base.rb +19 -0
  43. data/lib/message_driver/middleware/block_middleware.rb +33 -0
  44. data/lib/message_driver/middleware/middleware_stack.rb +61 -0
  45. data/lib/message_driver/subscription.rb +2 -2
  46. data/lib/message_driver/version.rb +1 -1
  47. data/message-driver.gemspec +3 -4
  48. data/spec/integration/bunny/amqp_integration_spec.rb +21 -82
  49. data/spec/integration/bunny/bunny_adapter_spec.rb +288 -268
  50. data/spec/integration/in_memory/in_memory_adapter_spec.rb +93 -90
  51. data/spec/integration/stomp/stomp_adapter_spec.rb +126 -93
  52. data/spec/spec_helper.rb +11 -9
  53. data/spec/support/shared/adapter_examples.rb +1 -1
  54. data/spec/support/shared/client_ack_examples.rb +4 -4
  55. data/spec/support/shared/context_examples.rb +6 -4
  56. data/spec/support/shared/destination_examples.rb +54 -14
  57. data/spec/support/shared/subscription_examples.rb +33 -26
  58. data/spec/support/shared/transaction_examples.rb +12 -12
  59. data/spec/support/utils.rb +1 -1
  60. data/spec/units/message_driver/adapters/base_spec.rb +42 -40
  61. data/spec/units/message_driver/broker_spec.rb +38 -38
  62. data/spec/units/message_driver/client_spec.rb +87 -87
  63. data/spec/units/message_driver/destination_spec.rb +16 -11
  64. data/spec/units/message_driver/message_spec.rb +96 -70
  65. data/spec/units/message_driver/middleware/base_spec.rb +30 -0
  66. data/spec/units/message_driver/middleware/block_middleware_spec.rb +82 -0
  67. data/spec/units/message_driver/middleware/middleware_stack_spec.rb +165 -0
  68. data/spec/units/message_driver/subscription_spec.rb +18 -16
  69. data/test_lib/broker_config.rb +21 -5
  70. data/test_lib/coverage.rb +11 -0
  71. data/test_lib/provider/base.rb +59 -0
  72. data/test_lib/provider/in_memory.rb +6 -0
  73. data/test_lib/provider/rabbitmq.rb +67 -0
  74. metadata +46 -35
@@ -3,11 +3,11 @@ RSpec::Matchers.define :have_no_errors do
3
3
  test_runner.raised_error.nil?
4
4
  end
5
5
 
6
- failure_message_for_should do |test_runner|
6
+ failure_message do |test_runner|
7
7
  err = test_runner.raised_error
8
8
  filtered = (err.backtrace || []).reject do |line|
9
9
  Cucumber::Ast::StepInvocation::BACKTRACE_FILTER_PATTERNS.find { |p| line =~ p }
10
10
  end
11
- (["#{err.class}: #{err}"]+filtered).join("\n ")
11
+ (["#{err.class}: #{err}"] + filtered).join("\n ")
12
12
  end
13
13
  end
@@ -1,78 +1,32 @@
1
+ require 'forwardable'
2
+
1
3
  class TestRunner
2
4
  include MessageDriver::Client
3
5
  include RSpec::Matchers
6
+ extend Forwardable
4
7
 
5
8
  attr_accessor :raised_error
6
9
  attr_accessor :current_feature_file
7
- attr_accessor :broker_name
8
10
 
9
- def broker_name
10
- @broker_name ||= MessageDriver::Broker::DEFAULT_BROKER_NAME
11
+ def provider
12
+ @provider ||= Provider.new
11
13
  end
12
14
 
15
+ def_delegators :provider, :broker_name, :broker_name=, :fetch_messages, :fetch_destination, :fetch_current_adapter_context, :purge_destination, :pause_if_needed
16
+
13
17
  def run_config_code(src)
14
18
  instance_eval(src, current_feature_file)
15
19
  end
16
20
 
17
21
  def run_test_code(src)
18
- begin
19
- instance_eval(src, current_feature_file)
20
- rescue => e
21
- @raised_error = e
22
- end
23
- end
24
-
25
- def fetch_messages(destination_name)
26
- destination = fetch_destination(destination_name)
27
- pause_if_needed
28
- result = []
29
- loop do
30
- msg = destination.pop_message
31
- if msg.nil?
32
- break
33
- else
34
- result << msg
35
- end
36
- end
37
- result
38
- end
39
-
40
- def purge_destination(destination_name)
41
- destination = fetch_destination(destination_name)
42
- if destination.respond_to? :purge
43
- destination.purge
44
- else
45
- fetch_messages(destination)
46
- end
47
- end
48
-
49
- def fetch_destination(destination)
50
- case destination
51
- when String, Symbol
52
- MessageDriver::Client[self.broker_name].find_destination(destination)
53
- when MessageDriver::Destination::Base
54
- destination
55
- else
56
- raise "didn't understand destination #{destination.inspect}"
57
- end
58
- end
59
-
60
- def fetch_current_adapter_context
61
- MessageDriver::Client[self.broker_name].current_adapter_context
22
+ instance_eval(src, current_feature_file)
23
+ rescue => e
24
+ @raised_error = e
62
25
  end
63
26
 
64
27
  def publish_table_to_destination(destination, table)
65
28
  table.hashes.each do |msg|
66
- destination.publish(msg[:body], msg[:headers]||{}, msg[:properties]||{})
67
- end
68
- end
69
-
70
- def pause_if_needed(seconds=0.1)
71
- seconds *= 10 if ENV['CI'] == 'true'
72
- case BrokerConfig.current_adapter
73
- when :in_memory
74
- else
75
- sleep seconds
29
+ destination.publish(msg[:body], msg[:headers] || {}, msg[:properties] || {})
76
30
  end
77
31
  end
78
32
  end
@@ -1,17 +1,19 @@
1
- NUMBER = Transform(/^\d+|no$/) do |num|
2
- case num
3
- when 'no'
4
- 0
1
+ STRING_OR_SYM = Transform(/^:?[A-Za-z]\w*$/) do |str|
2
+ case str
3
+ when /^:/
4
+ str.slice(1, str.length - 1).to_sym
5
5
  else
6
- num.to_i
6
+ str
7
7
  end
8
8
  end
9
9
 
10
- STRING_OR_SYM = Transform(/^:?\w+$/) do |str|
11
- case str
12
- when /^:/
13
- str.slice(1, str.length-1).to_sym
10
+ NUMBER = Transform(/^(?:\d+|a|an|no)$/) do |num|
11
+ case num
12
+ when 'no'
13
+ 0
14
+ when 'a', 'an'
15
+ 1
14
16
  else
15
- str
17
+ Integer(num)
16
18
  end
17
19
  end
@@ -5,6 +5,7 @@ require 'message_driver/logging'
5
5
  require 'message_driver/errors'
6
6
  require 'message_driver/broker'
7
7
  require 'message_driver/message'
8
+ require 'message_driver/middleware'
8
9
  require 'message_driver/destination'
9
10
  require 'message_driver/subscription'
10
11
  require 'message_driver/adapters/base'
@@ -12,12 +13,13 @@ require 'message_driver/client'
12
13
 
13
14
  module MessageDriver
14
15
  module_function
16
+
15
17
  def configure(broker_name = Broker::DEFAULT_BROKER_NAME, options)
16
18
  Broker.configure(broker_name, options)
17
19
  end
18
20
 
19
21
  def logger
20
- @__logger ||= Logger.new(STDOUT).tap{|l| l.level = Logger::INFO}
22
+ @__logger ||= Logger.new(STDOUT).tap { |l| l.level = Logger::INFO }
21
23
  end
22
24
 
23
25
  def logger=(logger)
@@ -10,7 +10,7 @@ module MessageDriver
10
10
  end
11
11
 
12
12
  def initialize(_broker, _configuration)
13
- raise 'Must be implemented in subclass'
13
+ fail 'Must be implemented in subclass'
14
14
  end
15
15
 
16
16
  def new_context
@@ -20,11 +20,11 @@ module MessageDriver
20
20
  end
21
21
 
22
22
  def build_context
23
- raise 'Must be implemented in subclass'
23
+ fail 'Must be implemented in subclass'
24
24
  end
25
25
 
26
26
  def reset_after_tests
27
- #does nothing, can be overridden by adapters that want to support testing scenarios
27
+ # does nothing, can be overridden by adapters that want to support testing scenarios
28
28
  end
29
29
 
30
30
  def stop
@@ -47,20 +47,20 @@ module MessageDriver
47
47
  @valid = true
48
48
  end
49
49
 
50
- def publish(_destination, _body, _headers={}, _properties={})
51
- raise 'Must be implemented in subclass'
50
+ def publish(_destination, _body, _headers = {}, _properties = {})
51
+ fail 'Must be implemented in subclass'
52
52
  end
53
53
 
54
- def pop_message(_destination, _options={})
55
- raise 'Must be implemented in subclass'
54
+ def pop_message(_destination, _options = {})
55
+ fail 'Must be implemented in subclass'
56
56
  end
57
57
 
58
- def subscribe(_destination, _options={}, &_consumer)
59
- raise "#subscribe is not supported by #{adapter.class}"
58
+ def subscribe(_destination, _options = {}, &_consumer)
59
+ fail "#subscribe is not supported by #{adapter.class}"
60
60
  end
61
61
 
62
- def create_destination(_name, _dest_options={}, _message_props={})
63
- raise 'Must be implemented in subclass'
62
+ def create_destination(_name, _dest_options = {}, _message_props = {})
63
+ fail 'Must be implemented in subclass'
64
64
  end
65
65
 
66
66
  def valid?
@@ -1,4 +1,5 @@
1
1
  require 'bunny'
2
+ require 'forwardable'
2
3
 
3
4
  module MessageDriver
4
5
  class Broker
@@ -9,13 +10,22 @@ module MessageDriver
9
10
 
10
11
  module Adapters
11
12
  class BunnyAdapter < Base
12
- NETWORK_ERRORS = [Bunny::TCPConnectionFailed, Bunny::ConnectionClosedError, Bunny::ConnectionLevelException, Bunny::NetworkErrorWrapper, Bunny::NetworkFailure, IOError].freeze
13
+ NETWORK_ERRORS = [Bunny::TCPConnectionFailed,
14
+ Bunny::ConnectionClosedError,
15
+ Bunny::ConnectionLevelException,
16
+ Bunny::NetworkErrorWrapper,
17
+ Bunny::NetworkFailure,
18
+ IOError].freeze
13
19
 
14
20
  class Message < MessageDriver::Message::Base
15
21
  attr_reader :delivery_info
16
22
 
17
- def initialize(ctx, delivery_info, properties, payload)
18
- super(ctx, payload, properties[:headers]||{}, properties)
23
+ def initialize(ctx, delivery_info, properties, payload, destination)
24
+ raw_body = payload
25
+ raw_headers = properties[:headers]
26
+ raw_headers = {} if raw_headers.nil?
27
+ b, h, p = destination.middleware.on_consume(payload, raw_headers, properties)
28
+ super(ctx, b, h, p, raw_body)
19
29
  @delivery_info = delivery_info
20
30
  end
21
31
 
@@ -29,10 +39,11 @@ module MessageDriver
29
39
  end
30
40
 
31
41
  class Destination < MessageDriver::Destination::Base
32
- def publish_params(headers, properties)
33
- props = @message_props.merge(properties)
34
- props[:headers] = headers if headers
35
- [exchange_name, routing_key(properties), props]
42
+ def publish_params(body, headers, properties)
43
+ b, h, p = middleware.on_publish(body, headers, properties)
44
+ props = @message_props.merge(p)
45
+ props[:headers] = h if h
46
+ [b, exchange_name, routing_key(properties), props]
36
47
  end
37
48
 
38
49
  def exchange_name
@@ -47,27 +58,35 @@ module MessageDriver
47
58
  class QueueDestination < Destination
48
59
  def after_initialize(adapter_context)
49
60
  if @dest_options[:no_declare]
50
- raise MessageDriver::Error, 'server-named queues must be declared, but you provided :no_declare => true' if @name.empty?
51
- raise MessageDriver::Error, 'queues with bindings must be declared, but you provided :no_declare => true' if @dest_options[:bindings]
61
+ if @name.empty?
62
+ fail MessageDriver::Error, 'server-named queues must be declared, but you provided :no_declare => true'
63
+ end
64
+ if @dest_options[:bindings]
65
+ fail MessageDriver::Error, 'queues with bindings must be declared, but you provided :no_declare => true'
66
+ end
52
67
  else
53
68
  adapter_context.with_channel(false) do |ch|
54
- bunny_queue(ch, true)
69
+ bunny_queue(ch, init: true)
55
70
  end
56
71
  end
57
72
  end
58
73
 
59
- def bunny_queue(channel, initialize=false)
60
- queue = channel.queue(@name, @dest_options)
61
- if initialize
62
- @name = queue.name
63
- if (bindings = @dest_options[:bindings])
64
- bindings.each do |bnd|
65
- raise MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
66
- queue.bind(bnd[:source], bnd[:args]||{})
67
- end
74
+ def bunny_queue(channel, options = {})
75
+ opts = @dest_options.dup
76
+ opts.merge!(passive: options[:passive]) if options.key? :passive
77
+ queue = channel.queue(@name, opts)
78
+ handle_queue_init(queue) if options.fetch(:init, false)
79
+ queue
80
+ end
81
+
82
+ def handle_queue_init(queue)
83
+ @name = queue.name
84
+ if (bindings = @dest_options[:bindings])
85
+ bindings.each do |bnd|
86
+ fail MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
87
+ queue.bind(bnd[:source], bnd[:args] || {})
68
88
  end
69
89
  end
70
- queue
71
90
  end
72
91
 
73
92
  def exchange_name
@@ -80,7 +99,17 @@ module MessageDriver
80
99
 
81
100
  def message_count
82
101
  adapter.broker.client.current_adapter_context.with_channel(false) do |ch|
83
- ch.queue(@name, @dest_options.merge(passive: true)).message_count
102
+ bunny_queue(ch, passive: true).message_count
103
+ end
104
+ end
105
+
106
+ def subscribe(options = {}, &consumer)
107
+ adapter.broker.client.current_adapter_context.subscribe(self, options, &consumer)
108
+ end
109
+
110
+ def consumer_count
111
+ adapter.broker.client.current_adapter_context.with_channel(false) do |ch|
112
+ bunny_queue(ch, passive: true).consumer_count
84
113
  end
85
114
  end
86
115
 
@@ -96,15 +125,15 @@ module MessageDriver
96
125
  if (declare = @dest_options[:declare])
97
126
  adapter_context.with_channel(false) do |ch|
98
127
  type = declare.delete(:type)
99
- raise MessageDriver::Error, 'you must provide a valid exchange type' unless type
128
+ fail MessageDriver::Error, 'you must provide a valid exchange type' unless type
100
129
  ch.exchange_declare(@name, type, declare)
101
130
  end
102
131
  end
103
132
  if (bindings = @dest_options[:bindings])
104
133
  adapter_context.with_channel(false) do |ch|
105
134
  bindings.each do |bnd|
106
- raise MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
107
- ch.exchange_bind(bnd[:source], @name, bnd[:args]||{})
135
+ fail MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
136
+ ch.exchange_bind(bnd[:source], @name, bnd[:args] || {})
108
137
  end
109
138
  end
110
139
  end
@@ -112,20 +141,25 @@ module MessageDriver
112
141
  end
113
142
 
114
143
  class Subscription < Subscription::Base
144
+ attr_reader :sub_ctx, :error_handler
145
+
115
146
  def start
116
- raise MessageDriver::Error, 'subscriptions are only supported with QueueDestinations' unless destination.is_a? QueueDestination
147
+ unless destination.is_a? QueueDestination
148
+ fail MessageDriver::Error,
149
+ 'subscriptions are only supported with QueueDestinations'
150
+ end
117
151
  @sub_ctx = adapter.new_subscription_context(self)
118
152
  @error_handler = options[:error_handler]
119
- @ack_mode = case options[:ack]
120
- when :auto, nil
121
- :auto
122
- when :manual
123
- :manual
124
- when :transactional
125
- :transactional
126
- else
127
- raise MessageDriver::Error, "unrecognized :ack option #{options[:ack]}"
128
- end
153
+ @message_handler = case options[:ack]
154
+ when :auto, nil
155
+ AutoAckHandler.new(self)
156
+ when :manual
157
+ ManualAckHandler.new(self)
158
+ when :transactional
159
+ TransactionalAckHandler.new(self)
160
+ else
161
+ fail MessageDriver::Error, "unrecognized :ack option #{options[:ack]}"
162
+ end
129
163
  start_subscription
130
164
  end
131
165
 
@@ -142,50 +176,74 @@ module MessageDriver
142
176
 
143
177
  private
144
178
 
145
- def start_subscription
146
- @sub_ctx.with_channel do |ch|
147
- queue = destination.bunny_queue(@sub_ctx.channel)
148
- if options.key? :prefetch_size
149
- ch.prefetch(options[:prefetch_size])
179
+ class MessageHandler
180
+ extend Forwardable
181
+ include Logging
182
+
183
+ attr_accessor :subscription
184
+ def_delegators :subscription, :adapter, :sub_ctx, :consumer, :error_handler, :options
185
+
186
+ def initialize(subscription)
187
+ @subscription = subscription
188
+ end
189
+
190
+ def call(message)
191
+ consumer.call(message)
192
+ rescue => e
193
+ error_handler.call(e, message) unless error_handler.nil?
194
+ end
195
+
196
+ def nack_message(e, message)
197
+ requeue = true
198
+ if e.is_a?(DontRequeue) || (options[:retry_redelivered] == false && message.redelivered?)
199
+ requeue = false
150
200
  end
151
- @bunny_consumer = queue.subscribe(options.merge(manual_ack: true)) do |delivery_info, properties, payload|
152
- adapter.broker.client.with_adapter_context(@sub_ctx) do
153
- message = @sub_ctx.args_to_message(delivery_info, properties, payload)
154
- handle_message(message)
201
+ if sub_ctx.valid?
202
+ begin
203
+ sub_ctx.nack_message(message, requeue: requeue)
204
+ rescue => e
205
+ logger.error exception_to_str(e)
155
206
  end
156
207
  end
157
208
  end
158
209
  end
159
210
 
160
- def handle_message(message)
161
- begin
162
- case @ack_mode
163
- when :auto
164
- consumer.call(message)
165
- @sub_ctx.ack_message(message)
166
- when :manual
211
+ class ManualAckHandler < MessageHandler
212
+ # all functionality implemented in super class
213
+ end
214
+
215
+ class AutoAckHandler < MessageHandler
216
+ def call(message)
217
+ consumer.call(message)
218
+ sub_ctx.ack_message(message)
219
+ rescue => e
220
+ nack_message(e, message)
221
+ error_handler.call(e, message) unless error_handler.nil?
222
+ end
223
+ end
224
+
225
+ class TransactionalAckHandler < MessageHandler
226
+ def call(message)
227
+ adapter.broker.client.with_message_transaction do
167
228
  consumer.call(message)
168
- when :transactional
169
- adapter.broker.client.with_message_transaction do
170
- consumer.call(message)
171
- @sub_ctx.ack_message(message)
172
- end
229
+ sub_ctx.ack_message(message)
173
230
  end
174
231
  rescue => e
175
- if [:auto, :transactional].include? @ack_mode
176
- requeue = true
177
- if e.is_a?(DontRequeue) || (options[:retry_redelivered] == false && message.redelivered?)
178
- requeue = false
179
- end
180
- if @sub_ctx.valid?
181
- begin
182
- @sub_ctx.nack_message(message, requeue: requeue)
183
- rescue => e
184
- logger.error exception_to_str(e)
185
- end
232
+ nack_message(e, message)
233
+ error_handler.call(e, message) unless error_handler.nil?
234
+ end
235
+ end
236
+
237
+ def start_subscription
238
+ @sub_ctx.with_channel do |ch|
239
+ queue = destination.bunny_queue(@sub_ctx.channel)
240
+ ch.prefetch(options[:prefetch_size]) if options.key? :prefetch_size
241
+ @bunny_consumer = queue.subscribe(options.merge(manual_ack: true)) do |delivery_info, properties, payload|
242
+ adapter.broker.client.with_adapter_context(@sub_ctx) do
243
+ message = @sub_ctx.args_to_message(delivery_info, properties, payload, destination)
244
+ @message_handler.call(message)
186
245
  end
187
246
  end
188
- @error_handler.call(e, message) unless @error_handler.nil?
189
247
  end
190
248
  end
191
249
  end
@@ -196,7 +254,7 @@ module MessageDriver
196
254
  @config = config
197
255
  end
198
256
 
199
- def connection(ensure_started=true)
257
+ def connection(ensure_started = true)
200
258
  if ensure_started
201
259
  begin
202
260
  @connection ||= Bunny::Session.new(@config)
@@ -212,14 +270,12 @@ module MessageDriver
212
270
  end
213
271
 
214
272
  def stop
215
- begin
216
- super
217
- @connection.close unless @connection.nil?
218
- rescue => e
219
- logger.error "error while attempting connection close\n#{exception_to_str(e)}"
220
- ensure
221
- @connection = nil
222
- end
273
+ super
274
+ @connection.close unless @connection.nil?
275
+ rescue => e
276
+ logger.error "error while attempting connection close\n#{exception_to_str(e)}"
277
+ ensure
278
+ @connection = nil
223
279
  end
224
280
 
225
281
  def build_context
@@ -244,15 +300,15 @@ module MessageDriver
244
300
  @in_transaction = false
245
301
  end
246
302
 
247
- def create_destination(name, dest_options={}, message_props={})
248
- dest = case type = dest_options.delete(:type)
249
- when :exchange
250
- ExchangeDestination.new(self.adapter, name, dest_options, message_props)
251
- when :queue, nil
252
- QueueDestination.new(self.adapter, name, dest_options, message_props)
253
- else
254
- raise MessageDriver::Error, "invalid destination type #{type}"
255
- end
303
+ def create_destination(name, dest_options = {}, message_props = {})
304
+ dest = case type = dest_options.delete(:type)
305
+ when :exchange
306
+ ExchangeDestination.new(adapter, name, dest_options, message_props)
307
+ when :queue, nil
308
+ QueueDestination.new(adapter, name, dest_options, message_props)
309
+ else
310
+ fail MessageDriver::Error, "invalid destination type #{type}"
311
+ end
256
312
  dest.after_initialize(self)
257
313
  dest
258
314
  end
@@ -261,14 +317,20 @@ module MessageDriver
261
317
  true
262
318
  end
263
319
 
264
- def begin_transaction(options={})
265
- raise MessageDriver::TransactionError, "you can't begin another transaction, you are already in one!" if in_transaction?
320
+ def begin_transaction(options = {})
321
+ if in_transaction?
322
+ fail MessageDriver::TransactionError,
323
+ "you can't begin another transaction, you are already in one!"
324
+ end
266
325
  @in_transaction = true
267
326
  @in_confirms_transaction = true if options[:type] == :confirm_and_wait
268
327
  end
269
328
 
270
- def commit_transaction(channel_commit=false)
271
- raise MessageDriver::TransactionError, "you can't finish the transaction unless you already in one!" if !in_transaction? && !channel_commit
329
+ def commit_transaction(channel_commit = false)
330
+ if !in_transaction? && !channel_commit
331
+ fail MessageDriver::TransactionError,
332
+ "you can't finish the transaction unless you already in one!"
333
+ end
272
334
  begin
273
335
  if @in_confirms_transaction
274
336
  wait_for_confirms(@channel) unless @rollback_only
@@ -291,9 +353,8 @@ module MessageDriver
291
353
  end
292
354
 
293
355
  def wait_for_confirms(channel)
294
- until channel.unconfirmed_set.empty?
295
- channel.wait_for_confirms
296
- end
356
+ # FIXME: make the thread-safety of this better once https://github.com/ruby-amqp/bunny/issues/227 is fixed
357
+ channel.wait_for_confirms until channel.unconfirmed_set.empty?
297
358
  end
298
359
  private :wait_for_confirms
299
360
 
@@ -311,8 +372,8 @@ module MessageDriver
311
372
  @in_transaction
312
373
  end
313
374
 
314
- def publish(destination, body, headers={}, properties={})
315
- exchange, routing_key, props = *destination.publish_params(headers, properties)
375
+ def publish(destination, body, headers = {}, properties = {})
376
+ body, exchange, routing_key, props = *destination.publish_params(body, headers, properties)
316
377
  confirm = props.delete(:confirm)
317
378
  confirm = false if confirm.nil?
318
379
  with_channel(true) do |ch|
@@ -324,8 +385,8 @@ module MessageDriver
324
385
  end
325
386
  end
326
387
 
327
- def pop_message(destination, options={})
328
- raise MessageDriver::Error, "You can't pop a message off an exchange" if destination.is_a? ExchangeDestination
388
+ def pop_message(destination, options = {})
389
+ fail MessageDriver::Error, "You can't pop a message off an exchange" if destination.is_a? ExchangeDestination
329
390
 
330
391
  with_channel(false) do |ch|
331
392
  queue = ch.queue(destination.name, passive: true)
@@ -334,7 +395,7 @@ module MessageDriver
334
395
  if message.nil? || message[0].nil?
335
396
  nil
336
397
  else
337
- args_to_message(*message)
398
+ args_to_message(*message, destination)
338
399
  end
339
400
  end
340
401
  end
@@ -343,13 +404,13 @@ module MessageDriver
343
404
  true
344
405
  end
345
406
 
346
- def ack_message(message, _options={})
407
+ def ack_message(message, _options = {})
347
408
  with_channel(true) do |ch|
348
409
  ch.ack(message.delivery_tag)
349
410
  end
350
411
  end
351
412
 
352
- def nack_message(message, options={})
413
+ def nack_message(message, options = {})
353
414
  requeue = options[:requeue].is_a?(FalseClass) ? false : true
354
415
  with_channel(true) do |ch|
355
416
  ch.reject(message.delivery_tag, requeue)
@@ -360,13 +421,13 @@ module MessageDriver
360
421
  true
361
422
  end
362
423
 
363
- def subscribe(destination, options={}, &consumer)
424
+ def subscribe(destination, options = {}, &consumer)
364
425
  sub = Subscription.new(adapter, destination, consumer, options)
365
426
  sub.start
366
427
  sub
367
428
  end
368
429
 
369
- def invalidate(in_unsubscribe=false)
430
+ def invalidate(in_unsubscribe = false)
370
431
  super()
371
432
  unless @subscription.nil? || in_unsubscribe
372
433
  begin
@@ -387,30 +448,28 @@ module MessageDriver
387
448
  end
388
449
 
389
450
  def handle_errors
390
- begin
391
- yield
392
- rescue Bunny::ChannelLevelException => e
393
- @need_channel_reset = true
394
- @rollback_only = true if in_transaction?
395
- if e.is_a? Bunny::NotFound
396
- raise MessageDriver::QueueNotFound.new(e.to_s, e)
397
- else
398
- raise MessageDriver::WrappedError.new(e.to_s, e)
399
- end
400
- rescue Bunny::ChannelAlreadyClosed => e
401
- @need_channel_reset = true
402
- @rollback_only = true if in_transaction?
451
+ yield
452
+ rescue Bunny::ChannelLevelException => e
453
+ @need_channel_reset = true
454
+ @rollback_only = true if in_transaction?
455
+ if e.is_a? Bunny::NotFound
456
+ raise MessageDriver::QueueNotFound.new(e.to_s, e)
457
+ else
403
458
  raise MessageDriver::WrappedError.new(e.to_s, e)
404
- rescue *NETWORK_ERRORS => e
405
- @need_channel_reset = true
406
- @rollback_only = true if in_transaction?
407
- raise MessageDriver::ConnectionError.new(e.to_s, e)
408
459
  end
409
- end
410
-
411
- def with_channel(require_commit=true)
412
- raise MessageDriver::TransactionRollbackOnly if @rollback_only
413
- raise MessageDriver::Error, 'this adapter context is not valid!' unless valid?
460
+ rescue Bunny::ChannelAlreadyClosed => e
461
+ @need_channel_reset = true
462
+ @rollback_only = true if in_transaction?
463
+ raise MessageDriver::WrappedError.new(e.to_s, e)
464
+ rescue *NETWORK_ERRORS => e
465
+ @need_channel_reset = true
466
+ @rollback_only = true if in_transaction?
467
+ raise MessageDriver::ConnectionError.new(e.to_s, e)
468
+ end
469
+
470
+ def with_channel(require_commit = true)
471
+ fail MessageDriver::TransactionRollbackOnly if @rollback_only
472
+ fail MessageDriver::Error, 'this adapter context is not valid!' unless valid?
414
473
  @channel = adapter.connection.create_channel if @channel.nil?
415
474
  reset_channel if @need_channel_reset
416
475
  if in_transaction?
@@ -430,8 +489,8 @@ module MessageDriver
430
489
  end
431
490
  end
432
491
 
433
- def args_to_message(delivery_info, properties, payload)
434
- Message.new(self, delivery_info, properties, payload)
492
+ def args_to_message(delivery_info, properties, payload, destination)
493
+ Message.new(self, delivery_info, properties, payload, destination)
435
494
  end
436
495
 
437
496
  private
@@ -449,18 +508,16 @@ module MessageDriver
449
508
  private
450
509
 
451
510
  def log_errors
452
- begin
453
- yield
454
- rescue => e
455
- logger.error exception_to_str(e)
456
- end
511
+ yield
512
+ rescue => e
513
+ logger.error exception_to_str(e)
457
514
  end
458
515
 
459
516
  def validate_bunny_version
460
517
  required = Gem::Requirement.create('>= 1.2.2')
461
518
  current = Gem::Version.create(Bunny::VERSION)
462
519
  unless required.satisfied_by? current
463
- raise MessageDriver::Error, 'bunny 1.2.2 or later is required for the bunny adapter'
520
+ fail MessageDriver::Error, 'bunny 1.2.2 or later is required for the bunny adapter'
464
521
  end
465
522
  end
466
523
  end