message-driver 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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