message-driver 0.3.0 → 0.4.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 (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