message-driver 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +18 -0
  3. data/.rubocop_todo.yml +160 -0
  4. data/.travis.yml +5 -4
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +9 -8
  7. data/Guardfile +14 -7
  8. data/README.md +2 -0
  9. data/Rakefile +16 -10
  10. data/examples/basic_producer_and_consumer/Gemfile +2 -2
  11. data/examples/basic_producer_and_consumer/common.rb +3 -3
  12. data/examples/basic_producer_and_consumer/consumer.rb +5 -5
  13. data/examples/basic_producer_and_consumer/producer.rb +7 -7
  14. data/features/CHANGELOG.md +5 -0
  15. data/features/message_consumers/transactional_ack_consumers.feature +1 -0
  16. data/features/rabbitmq_specific_features/publisher_acknowledgements.feature +51 -0
  17. data/features/step_definitions/error_handling_steps.rb +2 -2
  18. data/features/step_definitions/logging_steps.rb +4 -4
  19. data/features/step_definitions/message_consumers_steps.rb +8 -8
  20. data/features/step_definitions/rabbitmq_specific_steps.rb +10 -0
  21. data/features/step_definitions/steps.rb +15 -15
  22. data/features/support/env.rb +3 -0
  23. data/features/support/firewall_helper.rb +5 -6
  24. data/features/support/message_table_matcher.rb +3 -4
  25. data/features/support/no_error_matcher.rb +3 -3
  26. data/features/support/test_runner.rb +11 -3
  27. data/features/support/transforms.rb +1 -1
  28. data/lib/message-driver.rb +1 -1
  29. data/lib/message_driver.rb +0 -1
  30. data/lib/message_driver/adapters/base.rb +10 -10
  31. data/lib/message_driver/adapters/bunny_adapter.rb +57 -30
  32. data/lib/message_driver/adapters/in_memory_adapter.rb +4 -5
  33. data/lib/message_driver/adapters/stomp_adapter.rb +4 -6
  34. data/lib/message_driver/broker.rb +5 -4
  35. data/lib/message_driver/client.rb +6 -6
  36. data/lib/message_driver/destination.rb +2 -2
  37. data/lib/message_driver/errors.rb +1 -4
  38. data/lib/message_driver/logging.rb +1 -1
  39. data/lib/message_driver/message.rb +2 -2
  40. data/lib/message_driver/subscription.rb +1 -1
  41. data/lib/message_driver/version.rb +1 -1
  42. data/lib/{message_driver/vendor → vendor}/.document +0 -0
  43. data/lib/vendor/nesty.rb +1 -0
  44. data/lib/vendor/nesty/nested_error.rb +28 -0
  45. data/message-driver.gemspec +15 -14
  46. data/spec/integration/bunny/amqp_integration_spec.rb +43 -43
  47. data/spec/integration/bunny/bunny_adapter_spec.rb +117 -101
  48. data/spec/integration/in_memory/in_memory_adapter_spec.rb +35 -35
  49. data/spec/integration/stomp/stomp_adapter_spec.rb +42 -42
  50. data/spec/spec_helper.rb +4 -1
  51. data/spec/support/shared/adapter_examples.rb +7 -7
  52. data/spec/support/shared/client_ack_examples.rb +6 -6
  53. data/spec/support/shared/context_examples.rb +4 -4
  54. data/spec/support/shared/destination_examples.rb +10 -10
  55. data/spec/support/shared/subscription_examples.rb +29 -29
  56. data/spec/support/shared/transaction_examples.rb +10 -10
  57. data/spec/units/message_driver/adapters/base_spec.rb +19 -19
  58. data/spec/units/message_driver/broker_spec.rb +57 -58
  59. data/spec/units/message_driver/client_spec.rb +84 -84
  60. data/spec/units/message_driver/destination_spec.rb +4 -4
  61. data/spec/units/message_driver/message_spec.rb +19 -19
  62. data/spec/units/message_driver/subscription_spec.rb +4 -4
  63. data/test_lib/broker_config.rb +2 -2
  64. metadata +27 -6
  65. data/lib/message_driver/vendor/nesty.rb +0 -1
  66. data/lib/message_driver/vendor/nesty/nested_error.rb +0 -26
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 - 2014-07-03
4
+
5
+ * require bunny 1.2.2 or later
6
+ * add support for publish confirmations
7
+
3
8
  ## 0.3.0 - 2014-02-26
4
9
 
5
10
  * Support for handling multiple broker connections
@@ -32,6 +32,7 @@ Feature: Transactional Consumers
32
32
  | Test Message 1 |
33
33
  | Test Message 2 |
34
34
 
35
+ @no_ci
35
36
  Scenario: When an error occurs
36
37
  Given I have a message consumer
37
38
  """ruby
@@ -0,0 +1,51 @@
1
+ @bunny
2
+ Feature: Publisher Acknowledgements
3
+
4
+ RabbitMQ supports confirmation of published messages
5
+ See http://www.rabbitmq.com/confirms.html for details.
6
+
7
+ Verifying the publish of a single message and of a group of messages is
8
+ supported as described below.
9
+
10
+ Background:
11
+ Given I am connected to the broker
12
+ And I have a destination :publish_ack with no messages on it
13
+
14
+ Scenario: Publishing a single message with confirmations turned on
15
+ When I execute the following code
16
+ """"ruby
17
+ publish(:publish_ack, "Test Message", {}, confirm: true)
18
+ """
19
+
20
+ Then I expect all the publishes to have been acknowledged
21
+ And I expect to find the following message on :publish_ack
22
+ | body |
23
+ | Test Message |
24
+
25
+
26
+ Scenario: Publishing a single message where confirmations are turned on by the destination
27
+ When I execute the following code
28
+ """"ruby
29
+ my_new_destination = MessageDriver::Client.dynamic_destination(:publish_ack, {}, {confirm: true})
30
+ my_new_destination.publish("Test Message")
31
+ """
32
+
33
+ Then I expect all the publishes to have been acknowledged
34
+ And I expect to find the following message on :publish_ack
35
+ | body |
36
+ | Test Message |
37
+
38
+
39
+ Scenario: Publishing a batch of messages with confirmations turned on
40
+ When I execute the following code
41
+ """"ruby
42
+ with_message_transaction(type: :confirm_and_wait) do
43
+ 50.times do |i|
44
+ publish(:publish_ack, "Test Message #{i}")
45
+ end
46
+ end
47
+ """
48
+
49
+ Then I expect all the publishes to have been acknowledged
50
+ And I expect that we are not in transaction mode
51
+ And I expect to find 50 messages on :publish_ack
@@ -1,9 +1,9 @@
1
- When "the broker goes down" do
1
+ When 'the broker goes down' do
2
2
  block_broker_port
3
3
  sleep 2
4
4
  end
5
5
 
6
- When "the broker comes up" do
6
+ When 'the broker comes up' do
7
7
  unblock_broker_port
8
8
  sleep 20
9
9
  end
@@ -1,22 +1,22 @@
1
1
  require 'logger'
2
2
 
3
- LOG_FILE_NAME = "cucumber_log_file.log"
3
+ LOG_FILE_NAME = 'cucumber_log_file.log'
4
4
 
5
5
  Given(/^I am logging to a log file(?: at the (#{STRING_OR_SYM}) level)?$/) do |level|
6
6
  step "an empty file named \"#{LOG_FILE_NAME}\""
7
7
  in_current_dir do
8
8
  @logger = Logger.new(LOG_FILE_NAME)
9
9
  end
10
- step "I set the log level to #{level || "info"}"
10
+ step "I set the log level to #{level || 'info'}"
11
11
  @orig_logger, MessageDriver.logger = MessageDriver.logger, @logger
12
12
  end
13
13
 
14
14
  Given(/^I set the log level to (#{STRING_OR_SYM})$/) do |level|
15
- level = level ? level.to_s.upcase : "INFO"
15
+ level = level ? level.to_s.upcase : 'INFO'
16
16
  @logger.level = Logger::SEV_LABEL.find_index(level)
17
17
  end
18
18
 
19
- Then "the log file should contain:" do |string|
19
+ Then 'the log file should contain:' do |string|
20
20
  step "the file \"#{LOG_FILE_NAME}\" should contain:", string
21
21
  end
22
22
 
@@ -1,4 +1,4 @@
1
- Given "I have a message consumer" do |src|
1
+ Given 'I have a message consumer' do |src|
2
2
  test_runner.run_config_code(src)
3
3
  expect(test_runner).to have_no_errors
4
4
  end
@@ -7,18 +7,18 @@ Given(/^I subscribe to (#{STRING_OR_SYM}) with (#{STRING_OR_SYM})$/) do |destina
7
7
  MessageDriver::Client[test_runner.broker_name].subscribe(destination, consumer)
8
8
  end
9
9
 
10
- Given "I create a subscription" do |src|
10
+ Given 'I create a subscription' do |src|
11
11
  test_runner.run_test_code("@subscription = #{src}")
12
12
  expect(test_runner).to have_no_errors
13
13
  end
14
14
 
15
- When "I cancel the subscription" do
16
- test_runner.run_test_code("@subscription.unsubscribe")
17
- step "I allow for processing"
15
+ When 'I cancel the subscription' do
16
+ test_runner.run_test_code('@subscription.unsubscribe')
17
+ step 'I allow for processing'
18
18
  end
19
19
 
20
- When "I let the subscription process" do
21
- step "I allow for processing"
22
- step "I cancel the subscription"
20
+ When 'I let the subscription process' do
21
+ step 'I allow for processing'
22
+ step 'I cancel the subscription'
23
23
  expect(test_runner).to have_no_errors
24
24
  end
@@ -0,0 +1,10 @@
1
+ Then 'I expect all the publishes to have been acknowledged' do
2
+ ctx = test_runner.fetch_current_adapter_context
3
+ expect(ctx.channel).to be_using_publisher_confirms
4
+ expect(ctx.channel.unconfirmed_set).to be_empty
5
+ end
6
+
7
+ Then 'I expect that we are not in transaction mode' do
8
+ ctx = test_runner.fetch_current_adapter_context
9
+ expect(ctx).not_to be_transactional
10
+ end
@@ -1,18 +1,18 @@
1
- Given "I am connected to the broker" do
1
+ Given 'I am connected to the broker' do
2
2
  MessageDriver::Broker.configure(test_runner.broker_name, broker_config)
3
3
  end
4
4
 
5
5
  Given(/^I am connected to a broker named (#{STRING_OR_SYM})$/) do |broker_name|
6
6
  test_runner.broker_name = broker_name
7
- step "I am connected to the broker"
7
+ step 'I am connected to the broker'
8
8
  end
9
9
 
10
- Given "the following broker configuration" do |src|
11
- step "I am connected to the broker"
10
+ Given 'the following broker configuration' do |src|
11
+ step 'I am connected to the broker'
12
12
  test_runner.run_config_code(src)
13
13
  end
14
14
 
15
- Given "I configure my broker as follows" do |src|
15
+ Given 'I configure my broker as follows' do |src|
16
16
  test_runner.run_config_code(src)
17
17
  end
18
18
 
@@ -23,14 +23,14 @@ Given(/^I have a destination (#{STRING_OR_SYM})$/) do |destination|
23
23
  end
24
24
 
25
25
  Given(/^I have a destination (#{STRING_OR_SYM}) with no messages on it$/) do |destination|
26
- dest = destination.kind_of?(Symbol) ? destination.inspect : destination.to_s
26
+ dest = destination.is_a?(Symbol) ? destination.inspect : destination.to_s
27
27
  step "I have a destination #{dest}"
28
28
  test_runner.purge_destination(destination)
29
29
  end
30
30
 
31
31
  Given(/^I have the following messages? on (#{STRING_OR_SYM})$/) do |destination, table|
32
32
  test_runner.purge_destination(destination)
33
- dest = destination.kind_of?(Symbol) ? destination.inspect : destination.to_s
33
+ dest = destination.is_a?(Symbol) ? destination.inspect : destination.to_s
34
34
  step "I send the following messages to #{dest}", table
35
35
  end
36
36
 
@@ -44,15 +44,15 @@ When(/^I send the following messages? to (#{STRING_OR_SYM})$/) do |destination,
44
44
  end
45
45
  end
46
46
 
47
- When "I execute the following code" do |src|
47
+ When 'I execute the following code' do |src|
48
48
  test_runner.run_test_code(src)
49
49
  end
50
50
 
51
- When "I reset the context" do
51
+ When 'I reset the context' do
52
52
  MessageDriver::Client[test_runner.broker_name].current_adapter_context.invalidate
53
53
  end
54
54
 
55
- When "I allow for processing" do
55
+ When 'I allow for processing' do
56
56
  test_runner.pause_if_needed
57
57
  end
58
58
 
@@ -70,7 +70,7 @@ Then(/^I expect to find the following (#{NUMBER}) messages? on (#{STRING_OR_SYM}
70
70
  end
71
71
 
72
72
  Then(/^I expect to find the following message on (#{STRING_OR_SYM})$/) do |destination, table|
73
- dest = destination.kind_of?(Symbol) ? destination.inspect : destination.to_s
73
+ dest = destination.is_a?(Symbol) ? destination.inspect : destination.to_s
74
74
  step "I expect to find the following 1 message on #{dest}", table
75
75
  end
76
76
 
@@ -87,13 +87,13 @@ Then(/^I expect it to raise a (.*?) error$/) do |error_type|
87
87
  test_runner.raised_error = nil
88
88
  end
89
89
 
90
- Then "I expect to have no errors" do
90
+ Then 'I expect to have no errors' do
91
91
  expect(test_runner).to have_no_errors
92
92
  end
93
93
 
94
- Then "I expect the following check to pass" do |src|
95
- step "I execute the following code", src
96
- step "I expect to have no errors"
94
+ Then 'I expect the following check to pass' do |src|
95
+ step 'I execute the following code', src
96
+ step 'I expect to have no errors'
97
97
  end
98
98
 
99
99
  Before do |current_scenario|
@@ -1,3 +1,6 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
1
4
  require File.join(File.dirname(__FILE__), '..', '..', 'test_lib', 'broker_config')
2
5
 
3
6
  require 'aruba/cucumber'
@@ -1,5 +1,4 @@
1
1
  module FirewallHelper
2
-
3
2
  def self.port
4
3
  BrokerConfig.current_adapter_port
5
4
  end
@@ -7,18 +6,18 @@ module FirewallHelper
7
6
  COMMANDS = {
8
7
  darwin: {
9
8
  setup: [
10
- "lunchy stop rabbit"
9
+ 'lunchy stop rabbit'
11
10
  ],
12
11
  teardown: [
13
- "lunchy start rabbit"
12
+ 'lunchy start rabbit'
14
13
  ]
15
14
  },
16
15
  linux: {
17
16
  setup: [
18
- "sudo service rabbitmq-server stop"
17
+ 'sudo service rabbitmq-server stop'
19
18
  ],
20
19
  teardown: [
21
- "sudo service rabbitmq-server start"
20
+ 'sudo service rabbitmq-server start'
22
21
  ]
23
22
  }
24
23
  }
@@ -49,7 +48,7 @@ module FirewallHelper
49
48
  end
50
49
 
51
50
  def darwin?
52
- system("uname | grep Darwin")
51
+ system('uname | grep Darwin')
53
52
  end
54
53
  end
55
54
 
@@ -4,10 +4,9 @@ RSpec::Matchers.define :match_message_table do |expected_tbl|
4
4
  end
5
5
 
6
6
  define_method :messages_to_hash do |messages|
7
- messages.collect do |msg|
8
- expected_tbl.headers.inject({ }) do |memo, obj|
9
- memo[obj] = msg.send(obj)
10
- memo
7
+ messages.map do |msg|
8
+ expected_tbl.headers.each_with_object({}) do |method, hash|
9
+ hash[method] = msg.send(method)
11
10
  end
12
11
  end
13
12
  end
@@ -1,13 +1,13 @@
1
1
  RSpec::Matchers.define :have_no_errors do
2
2
  match do |test_runner|
3
- test_runner.raised_error == nil
3
+ test_runner.raised_error.nil?
4
4
  end
5
5
 
6
6
  failure_message_for_should do |test_runner|
7
7
  err = test_runner.raised_error
8
8
  filtered = (err.backtrace || []).reject do |line|
9
- Cucumber::Ast::StepInvocation::BACKTRACE_FILTER_PATTERNS.detect { |p| line =~ p }
9
+ Cucumber::Ast::StepInvocation::BACKTRACE_FILTER_PATTERNS.find { |p| line =~ p }
10
10
  end
11
- (["#{err.class}: #{err.to_s}"]+filtered).join("\n ")
11
+ (["#{err.class}: #{err}"]+filtered).join("\n ")
12
12
  end
13
13
  end
@@ -26,10 +26,14 @@ class TestRunner
26
26
  destination = fetch_destination(destination_name)
27
27
  pause_if_needed
28
28
  result = []
29
- begin
29
+ loop do
30
30
  msg = destination.pop_message
31
- result << msg unless msg.nil?
32
- end until msg.nil?
31
+ if msg.nil?
32
+ break
33
+ else
34
+ result << msg
35
+ end
36
+ end
33
37
  result
34
38
  end
35
39
 
@@ -53,6 +57,10 @@ class TestRunner
53
57
  end
54
58
  end
55
59
 
60
+ def fetch_current_adapter_context
61
+ MessageDriver::Client[self.broker_name].current_adapter_context
62
+ end
63
+
56
64
  def publish_table_to_destination(destination, table)
57
65
  table.hashes.each do |msg|
58
66
  destination.publish(msg[:body], msg[:headers]||{}, msg[:properties]||{})
@@ -1,6 +1,6 @@
1
1
  NUMBER = Transform(/^\d+|no$/) do |num|
2
2
  case num
3
- when "no"
3
+ when 'no'
4
4
  0
5
5
  else
6
6
  num.to_i
@@ -1 +1 @@
1
- require "message_driver"
1
+ require 'message_driver'
@@ -23,5 +23,4 @@ module MessageDriver
23
23
  def logger=(logger)
24
24
  @__logger = logger
25
25
  end
26
-
27
26
  end
@@ -9,8 +9,8 @@ module MessageDriver
9
9
  @contexts ||= []
10
10
  end
11
11
 
12
- def initialize(broker, configuration)
13
- raise "Must be implemented in subclass"
12
+ def initialize(_broker, _configuration)
13
+ raise 'Must be implemented in subclass'
14
14
  end
15
15
 
16
16
  def new_context
@@ -20,7 +20,7 @@ module MessageDriver
20
20
  end
21
21
 
22
22
  def build_context
23
- raise "Must be implemented in subclass"
23
+ raise 'Must be implemented in subclass'
24
24
  end
25
25
 
26
26
  def reset_after_tests
@@ -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
+ raise '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
+ raise 'Must be implemented in subclass'
56
56
  end
57
57
 
58
- def subscribe(destination, options={}, &consumer)
58
+ def subscribe(_destination, _options={}, &_consumer)
59
59
  raise "#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
+ raise 'Must be implemented in subclass'
64
64
  end
65
65
 
66
66
  def valid?
@@ -46,13 +46,13 @@ module MessageDriver
46
46
 
47
47
  class QueueDestination < Destination
48
48
  def after_initialize(adapter_context)
49
- unless @dest_options[:no_declare]
49
+ 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]
52
+ else
50
53
  adapter_context.with_channel(false) do |ch|
51
54
  bunny_queue(ch, true)
52
55
  end
53
- else
54
- raise MessageDriver::Error, "server-named queues must be declared, but you provided :no_declare => true" if @name.empty?
55
- raise MessageDriver::Error, "queues with bindings must be declared, but you provided :no_declare => true" if @dest_options[:bindings]
56
56
  end
57
57
  end
58
58
 
@@ -60,7 +60,7 @@ module MessageDriver
60
60
  queue = channel.queue(@name, @dest_options)
61
61
  if initialize
62
62
  @name = queue.name
63
- if bindings = @dest_options[:bindings]
63
+ if (bindings = @dest_options[:bindings])
64
64
  bindings.each do |bnd|
65
65
  raise MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
66
66
  queue.bind(bnd[:source], bnd[:args]||{})
@@ -71,10 +71,10 @@ module MessageDriver
71
71
  end
72
72
 
73
73
  def exchange_name
74
- ""
74
+ ''
75
75
  end
76
76
 
77
- def routing_key(properties)
77
+ def routing_key(_properties)
78
78
  @name
79
79
  end
80
80
 
@@ -93,14 +93,14 @@ module MessageDriver
93
93
 
94
94
  class ExchangeDestination < Destination
95
95
  def after_initialize(adapter_context)
96
- if declare = @dest_options[:declare]
96
+ if (declare = @dest_options[:declare])
97
97
  adapter_context.with_channel(false) do |ch|
98
98
  type = declare.delete(:type)
99
- raise MessageDriver::Error, "you must provide a valid exchange type" unless type
99
+ raise MessageDriver::Error, 'you must provide a valid exchange type' unless type
100
100
  ch.exchange_declare(@name, type, declare)
101
101
  end
102
102
  end
103
- if bindings = @dest_options[:bindings]
103
+ if (bindings = @dest_options[:bindings])
104
104
  adapter_context.with_channel(false) do |ch|
105
105
  bindings.each do |bnd|
106
106
  raise MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
@@ -113,7 +113,7 @@ module MessageDriver
113
113
 
114
114
  class Subscription < Subscription::Base
115
115
  def start
116
- raise MessageDriver::Error, "subscriptions are only supported with QueueDestinations" unless destination.is_a? QueueDestination
116
+ raise MessageDriver::Error, 'subscriptions are only supported with QueueDestinations' unless destination.is_a? QueueDestination
117
117
  @sub_ctx = adapter.new_subscription_context(self)
118
118
  @error_handler = options[:error_handler]
119
119
  @ack_mode = case options[:ack]
@@ -141,10 +141,11 @@ module MessageDriver
141
141
  end
142
142
 
143
143
  private
144
+
144
145
  def start_subscription
145
146
  @sub_ctx.with_channel do |ch|
146
147
  queue = destination.bunny_queue(@sub_ctx.channel)
147
- if options.has_key? :prefetch_size
148
+ if options.key? :prefetch_size
148
149
  ch.prefetch(options[:prefetch_size])
149
150
  end
150
151
  @bunny_consumer = queue.subscribe(options.merge(manual_ack: true)) do |delivery_info, properties, payload|
@@ -213,7 +214,7 @@ module MessageDriver
213
214
  def stop
214
215
  begin
215
216
  super
216
- @connection.close if !@connection.nil?
217
+ @connection.close unless @connection.nil?
217
218
  rescue => e
218
219
  logger.error "error while attempting connection close\n#{exception_to_str(e)}"
219
220
  ensure
@@ -263,34 +264,48 @@ module MessageDriver
263
264
  def begin_transaction(options={})
264
265
  raise MessageDriver::TransactionError, "you can't begin another transaction, you are already in one!" if in_transaction?
265
266
  @in_transaction = true
267
+ @in_confirms_transaction = true if options[:type] == :confirm_and_wait
266
268
  end
267
269
 
268
270
  def commit_transaction(channel_commit=false)
269
271
  raise MessageDriver::TransactionError, "you can't finish the transaction unless you already in one!" if !in_transaction? && !channel_commit
270
272
  begin
271
- if is_transactional? && valid? && !@need_channel_reset
272
- handle_errors do
273
- if @rollback_only
274
- @channel.tx_rollback
275
- else
276
- @channel.tx_commit
273
+ if @in_confirms_transaction
274
+ wait_for_confirms(@channel) unless @rollback_only
275
+ else
276
+ if is_transactional? && valid? && !@need_channel_reset
277
+ handle_errors do
278
+ if @rollback_only
279
+ @channel.tx_rollback
280
+ else
281
+ @channel.tx_commit
282
+ end
277
283
  end
278
284
  end
279
285
  end
280
286
  ensure
281
287
  @rollback_only = false
282
288
  @in_transaction = false
289
+ @in_confirms_transaction = false
290
+ end
291
+ end
292
+
293
+ def wait_for_confirms(channel)
294
+ until channel.unconfirmed_set.empty?
295
+ channel.wait_for_confirms
283
296
  end
284
297
  end
298
+ private :wait_for_confirms
285
299
 
286
300
  def rollback_transaction
287
301
  @rollback_only = true
288
302
  commit_transaction
289
303
  end
290
304
 
291
- def is_transactional?
305
+ def transactional?
292
306
  @is_transactional
293
307
  end
308
+ alias_method :is_transactional?, :transactional?
294
309
 
295
310
  def in_transaction?
296
311
  @in_transaction
@@ -298,8 +313,14 @@ module MessageDriver
298
313
 
299
314
  def publish(destination, body, headers={}, properties={})
300
315
  exchange, routing_key, props = *destination.publish_params(headers, properties)
316
+ confirm = props.delete(:confirm)
317
+ confirm = false if confirm.nil?
301
318
  with_channel(true) do |ch|
319
+ if confirm == true
320
+ ch.confirm_select unless ch.using_publisher_confirms?
321
+ end
302
322
  ch.basic_publish(body, exchange, routing_key, props)
323
+ ch.wait_for_confirms if confirm == true
303
324
  end
304
325
  end
305
326
 
@@ -309,7 +330,7 @@ module MessageDriver
309
330
  with_channel(false) do |ch|
310
331
  queue = ch.queue(destination.name, passive: true)
311
332
 
312
- message = queue.pop(ack: !!options[:client_ack])
333
+ message = queue.pop(ack: options.fetch(:client_ack, false))
313
334
  if message.nil? || message[0].nil?
314
335
  nil
315
336
  else
@@ -322,14 +343,14 @@ module MessageDriver
322
343
  true
323
344
  end
324
345
 
325
- def ack_message(message, options={})
346
+ def ack_message(message, _options={})
326
347
  with_channel(true) do |ch|
327
348
  ch.ack(message.delivery_tag)
328
349
  end
329
350
  end
330
351
 
331
352
  def nack_message(message, options={})
332
- requeue = options[:requeue].kind_of?(FalseClass) ? false : true
353
+ requeue = options[:requeue].is_a?(FalseClass) ? false : true
333
354
  with_channel(true) do |ch|
334
355
  ch.reject(message.delivery_tag, requeue)
335
356
  end
@@ -371,7 +392,7 @@ module MessageDriver
371
392
  rescue Bunny::ChannelLevelException => e
372
393
  @need_channel_reset = true
373
394
  @rollback_only = true if in_transaction?
374
- if e.kind_of? Bunny::NotFound
395
+ if e.is_a? Bunny::NotFound
375
396
  raise MessageDriver::QueueNotFound.new(e.to_s, e)
376
397
  else
377
398
  raise MessageDriver::WrappedError.new(e.to_s, e)
@@ -389,12 +410,18 @@ module MessageDriver
389
410
 
390
411
  def with_channel(require_commit=true)
391
412
  raise MessageDriver::TransactionRollbackOnly if @rollback_only
392
- raise MessageDriver::Error, "this adapter context is not valid!" if !valid?
413
+ raise MessageDriver::Error, 'this adapter context is not valid!' unless valid?
393
414
  @channel = adapter.connection.create_channel if @channel.nil?
394
415
  reset_channel if @need_channel_reset
395
- if in_transaction? && !is_transactional?
396
- @channel.tx_select
397
- @is_transactional = true
416
+ if in_transaction?
417
+ if @in_confirms_transaction
418
+ @channel.confirm_select unless @channel.using_publisher_confirmations?
419
+ else
420
+ unless is_transactional?
421
+ @channel.tx_select
422
+ @is_transactional = true
423
+ end
424
+ end
398
425
  end
399
426
  handle_errors do
400
427
  result = yield @channel
@@ -430,10 +457,10 @@ module MessageDriver
430
457
  end
431
458
 
432
459
  def validate_bunny_version
433
- required = Gem::Requirement.create('>= 1.1.3')
460
+ required = Gem::Requirement.create('>= 1.2.2')
434
461
  current = Gem::Version.create(Bunny::VERSION)
435
462
  unless required.satisfied_by? current
436
- raise MessageDriver::Error, "bunny 1.1.3 or later is required for the bunny adapter"
463
+ raise MessageDriver::Error, 'bunny 1.2.2 or later is required for the bunny adapter'
437
464
  end
438
465
  end
439
466
  end