hutch 1.1.1 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9725ddb3e39e305bad95abd6c8605c1d9828691904bb2ae31430fd22d88bd180
4
- data.tar.gz: db1d87d81de0f89c84b1ec387c66443aa9ead6be2d71b50b6cee32e9146dd080
3
+ metadata.gz: 6d1384ceb1b05cbdb706ac6e0f4cd4bfbdd86a49cbc2b6f61dabf96c1ca80ab5
4
+ data.tar.gz: d83a7db8184ed5baa8c00649de3d62a711ee07c04a598802193c1f6d4f7c97c8
5
5
  SHA512:
6
- metadata.gz: 296138c5cb4c08340f4a72bb8d30009402272f9843efffd0dc1d647328ca1531d188fd3b5a9a15f891f4a8447dbf5f72a7a583aa702f1c999fc557c1ab4ac024
7
- data.tar.gz: 2c0406261d2535d5e5d00037e20e29f5cb781e337732491cf36195231e5d647b75c0d96d74c0861afa36cfb24575a0e2b34ce06f59693a98b84342cf1d52ad95
6
+ metadata.gz: 703c57b0c1e7ab3774d2fae904b6e7aa84f98b8f512ce8676c8456e63a4da4b97ab8dd77a460353d88c39d95997c70ca19695f6710bbde1674e6a03aa70a222b
7
+ data.tar.gz: 9fea55e47720b5cf78f834d714a709ada538cb851edcb5f17fc9ad2b587f68dffcc5c5524c2855d9673da322378d4fda19d85554a90c3f1d993726f7790da912
@@ -1,10 +1,10 @@
1
1
  name: Test
2
2
 
3
- on:
4
- push:
5
- branches: [ master ]
6
- pull_request:
7
- branches: [ master ]
3
+ concurrency:
4
+ group: ${{ github.ref }}
5
+ cancel-in-progress: true
6
+
7
+ on: [push,pull_request,workflow_dispatch]
8
8
 
9
9
  jobs:
10
10
  test:
@@ -22,10 +22,15 @@ jobs:
22
22
  strategy:
23
23
  fail-fast: false
24
24
  matrix:
25
- ruby-version: ['jruby-9.2.19.0', 'jruby-9.3.0.0', 'jruby-head']
25
+ ruby-version:
26
+ - '2.7'
27
+ - '3.0'
28
+ - '3.1'
29
+ - '3.2'
30
+ - '3.3'
26
31
 
27
32
  steps:
28
- - uses: actions/checkout@v2
33
+ - uses: actions/checkout@v4
29
34
  - name: Set up Ruby
30
35
  # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
31
36
  # change this to (see https://github.com/ruby/setup-ruby#versioning):
@@ -40,3 +45,12 @@ jobs:
40
45
  ./bin/ci/before_build_docker.sh
41
46
  - name: Run tests
42
47
  run: bundle exec rspec spec
48
+ test_all:
49
+ if: ${{ always() }}
50
+ runs-on: ubuntu-latest
51
+ name: Test (matrix)
52
+ needs: test
53
+ steps:
54
+ - name: Check test matrix status
55
+ if: ${{ needs.test.result != 'success' }}
56
+ run: exit 1
data/CHANGELOG.md CHANGED
@@ -1,4 +1,24 @@
1
+ ## 1.3.0 (Nov 11, 2024)
2
+
3
+ ### Ruby 3.2 Compatibility
4
+
5
+ GitHub issue: [#392](https://github.com/ruby-amqp/hutch/pull/392)
6
+
7
+ ### Relaxed ActiveSupport Dependency Constraints
8
+
9
+ Contributed by drobny.
10
+
11
+ GitHub issue: [#402](https://github.com/ruby-amqp/hutch/pull/402)
12
+
13
+ ### Client-Provided Connection Name
14
+
15
+ Contributed by @sharshenov.
16
+
17
+ GitHub issue: [#399](https://github.com/ruby-amqp/hutch/pull/399)
18
+
19
+
1
20
  ## 1.1.1 (March 18th, 2022)
21
+
2
22
  ### Dependency Bump
3
23
 
4
24
  Hutch now allows ActiveSupport 7.x.
data/Gemfile CHANGED
@@ -1,13 +1,13 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- ruby '>= 2.3.0'
3
+ ruby '>= 2.7.0'
4
4
 
5
5
  gemspec
6
6
 
7
7
  group :development do
8
8
  gem "rake"
9
- gem "guard", "~> 2.14", platform: :mri_23
10
- gem "guard-rspec", "~> 4.7", platform: :mri_23
9
+ gem "guard", "~> 2.14", platform: :mri
10
+ gem "guard-rspec", "~> 4.7", platform: :mri
11
11
 
12
12
  gem "yard", "~> 0.9"
13
13
  gem 'kramdown', "> 0", platform: :jruby
@@ -16,21 +16,20 @@ group :development do
16
16
  end
17
17
 
18
18
  group :development, :test do
19
- gem "rspec", "~> 3.5"
20
- gem "simplecov", "~> 0.12"
19
+ gem "rspec", "~> 3.12"
20
+ gem "simplecov", "~> 0.21"
21
21
 
22
22
  gem "sentry-raven"
23
23
  gem "sentry-ruby"
24
24
  gem "honeybadger"
25
- gem "coveralls", "~> 0.8.15", require: false
26
25
  gem "newrelic_rpm"
27
- gem "ddtrace"
28
- gem "airbrake", "~> 10.0"
26
+ gem "ddtrace", "~> 1.8"
27
+ gem "airbrake", "~> 13.0"
29
28
  gem "rollbar"
30
29
  gem "bugsnag"
31
30
  end
32
31
 
33
32
  group :development, :darwin do
34
- gem "rb-fsevent", "~> 0.9"
33
+ gem "rb-fsevent", "~> 0.11.2"
35
34
  gem "growl", "~> 1.0.3"
36
35
  end
data/README.md CHANGED
@@ -406,6 +406,7 @@ Known configuration parameters are:
406
406
  * `error_acknowledgements`: a chain of responsibility of objects that acknowledge/reject/requeue messages when an
407
407
  exception happens, see classes in `Hutch::Acknowledgements`.
408
408
  * `mq_exchange`: exchange to use for publishing (default: `hutch`)
409
+ * `mq_client_properties`: Bunny's [client properties](https://www.rabbitmq.com/docs/connections#capabilities) (default: `{}`)
409
410
  * `heartbeat`: [RabbitMQ heartbeat timeout](http://rabbitmq.com/heartbeats.html) (default: `30`)
410
411
  * `connection_timeout`: Bunny's socket open timeout (default: `11`)
411
412
  * `read_timeout`: Bunny's socket read timeout (default: `11`)
@@ -542,6 +543,13 @@ Generate with
542
543
  <td><tt>HUTCH_CHANNEL_PREFETCH</tt></td>
543
544
  <td><p>The <tt>basic.qos</tt> prefetch value to use.</p></td>
544
545
  </tr>
546
+ <tr>
547
+ <td><tt>connection_name</tt></td>
548
+ <td>nil</td>
549
+ <td>String</td>
550
+ <td><tt>HUTCH_CONNECTION_NAME</tt></td>
551
+ <td><p><a href="https://www.rabbitmq.com/docs/connections#client-provided-names">Client-Provided Connection Name</a></p></td>
552
+ </tr>
545
553
  <tr>
546
554
  <td><tt>connection_timeout</tt></td>
547
555
  <td>11</td>
File without changes
data/hutch.gemspec CHANGED
@@ -3,20 +3,20 @@ require File.expand_path('../lib/hutch/version', __FILE__)
3
3
  Gem::Specification.new do |gem|
4
4
  if defined?(JRUBY_VERSION)
5
5
  gem.platform = 'java'
6
- gem.add_runtime_dependency 'march_hare', '>= 3.0.0'
6
+ gem.add_runtime_dependency 'march_hare', '>= 4.5.0'
7
7
  else
8
8
  gem.platform = Gem::Platform::RUBY
9
- gem.add_runtime_dependency 'bunny', '>= 2.19', '< 3.0'
9
+ gem.add_runtime_dependency 'bunny', '>= 2.23', '< 3.0'
10
10
  end
11
11
  gem.add_runtime_dependency 'carrot-top', '~> 0.0.7'
12
12
  gem.add_runtime_dependency 'multi_json', '~> 1.15'
13
- gem.add_runtime_dependency 'activesupport', '>= 4.2', '< 8'
13
+ gem.add_runtime_dependency 'activesupport', '>= 4.2', '<= 8'
14
14
 
15
15
  gem.name = 'hutch'
16
16
  gem.summary = 'Opinionated asynchronous inter-service communication using RabbitMQ'
17
17
  gem.description = 'Hutch is a Ruby library for enabling asynchronous inter-service communication using RabbitMQ'
18
18
  gem.version = Hutch::VERSION.dup
19
- gem.required_ruby_version = '>= 2.2'
19
+ gem.required_ruby_version = '>= 2.6'
20
20
  gem.authors = ['Harry Marr', 'Michael Klishin']
21
21
  gem.homepage = 'https://github.com/ruby-amqp/hutch'
22
22
  gem.require_paths = ['lib']
data/lib/hutch/broker.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'active_support/core_ext/object/blank'
2
2
 
3
3
  require 'carrot-top'
4
+ require 'ostruct'
4
5
  require 'hutch/logging'
5
6
  require 'hutch/exceptions'
6
7
  require 'hutch/publisher'
@@ -174,8 +175,8 @@ module Hutch
174
175
  def queue(name, options = {})
175
176
  with_bunny_precondition_handler('queue') do
176
177
  namespace = @config[:namespace].to_s.downcase.gsub(/[^-_:\.\w]/, "")
177
- name = name.prepend(namespace + ":") if namespace.present?
178
- channel.queue(name, **options)
178
+ queue_name = namespace.present? ? "#{namespace}:#{name}" : name
179
+ channel.queue(queue_name, **options)
179
180
  end
180
181
  end
181
182
 
@@ -287,6 +288,7 @@ module Hutch
287
288
  params[:host] = @config[:mq_host]
288
289
  params[:port] = @config[:mq_port]
289
290
  params[:vhost] = @config[:mq_vhost].presence || Hutch::Adapter::DEFAULT_VHOST
291
+ params[:auth_mechanism] = @config[:mq_auth_mechanism]
290
292
  params[:username] = @config[:mq_username]
291
293
  params[:password] = @config[:mq_password]
292
294
  params[:tls] = @config[:mq_tls]
@@ -297,6 +299,8 @@ module Hutch
297
299
  params[:tls_ca_certificates] = @config[:mq_tls_ca_certificates]
298
300
  end
299
301
  params[:heartbeat] = @config[:heartbeat]
302
+ params[:client_properties] = @config[:mq_client_properties]
303
+ params[:connection_name] = @config[:connection_name]
300
304
  params[:connection_timeout] = @config[:connection_timeout]
301
305
  params[:read_timeout] = @config[:read_timeout]
302
306
  params[:write_timeout] = @config[:write_timeout]
data/lib/hutch/cli.rb CHANGED
@@ -65,7 +65,7 @@ module Hutch
65
65
  return false
66
66
  ensure
67
67
  # Clean up load path
68
- $LOAD_PATH.pop
68
+ $LOAD_PATH.delete('.')
69
69
  end
70
70
  end
71
71
  end
@@ -234,7 +234,7 @@ module Hutch
234
234
  end
235
235
 
236
236
  def abort_without_file(file, file_description, &block)
237
- abort_with_message("#{file_description} '#{file}' not found") unless File.exists?(file)
237
+ abort_with_message("#{file_description} '#{file}' not found") unless File.exist?(file)
238
238
 
239
239
  yield
240
240
  end
data/lib/hutch/config.rb CHANGED
@@ -63,6 +63,9 @@ module Hutch
63
63
  # RabbitMQ password
64
64
  string_setting :mq_password, 'guest'
65
65
 
66
+ # RabbitMQ Auth Mechanism
67
+ string_setting :mq_auth_mechanism, 'PLAIN'
68
+
66
69
  # RabbitMQ URI (takes precedence over MQ username, password, host, port and vhost settings)
67
70
  string_setting :uri, nil
68
71
 
@@ -83,6 +86,9 @@ module Hutch
83
86
  # Default: `0`, no limit. See Bunny and RabbitMQ documentation.
84
87
  number_setting :channel_prefetch, 0
85
88
 
89
+ # [Client-Provided Connection Name](https://www.rabbitmq.com/docs/connections#client-provided-names)
90
+ string_setting :connection_name, nil
91
+
86
92
  # Bunny's socket open timeout
87
93
  number_setting :connection_timeout, 11
88
94
 
@@ -168,6 +174,7 @@ module Hutch
168
174
  # @return [Hash]
169
175
  def self.default_config
170
176
  @settings_defaults.merge({
177
+ mq_client_properties: {},
171
178
  mq_exchange_options: {},
172
179
  mq_tls_cert: nil,
173
180
  mq_tls_key: nil,
@@ -222,7 +229,12 @@ module Hutch
222
229
  end
223
230
 
224
231
  def self.to_bool(value)
225
- !(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i || value == false)
232
+ case value
233
+ when nil, false, '', /^(false|f|no|n|0)$/i
234
+ false
235
+ else
236
+ true
237
+ end
226
238
  end
227
239
 
228
240
  def self.is_num(attr)
@@ -13,13 +13,19 @@ module Hutch
13
13
  end
14
14
 
15
15
  def reject!
16
+ @message_rejected = true
16
17
  broker.reject(delivery_info.delivery_tag)
17
18
  end
18
19
 
19
20
  def requeue!
21
+ @message_rejected = true
20
22
  broker.requeue(delivery_info.delivery_tag)
21
23
  end
22
24
 
25
+ def message_rejected?
26
+ !!@message_rejected
27
+ end
28
+
23
29
  def logger
24
30
  Hutch::Logging.logger
25
31
  end
@@ -11,7 +11,7 @@ module Hutch
11
11
  logger.error "#{prefix} Logging event to Sentry"
12
12
  logger.error "#{prefix} #{ex.class} - #{ex.message}"
13
13
  ::Sentry.configure_scope do |scope|
14
- scope.set_context("payload", payload)
14
+ scope.set_context("payload", JSON.parse(payload))
15
15
  end
16
16
  ::Sentry.capture_exception(ex)
17
17
  end
@@ -1,4 +1,5 @@
1
1
  require 'ddtrace'
2
+ require 'ddtrace/auto_instrument'
2
3
 
3
4
  module Hutch
4
5
  module Tracers
@@ -8,7 +9,7 @@ module Hutch
8
9
  end
9
10
 
10
11
  def handle(message)
11
- ::Datadog.tracer.trace(@klass.class.name, service: 'hutch', span_type: 'rabbitmq') do
12
+ ::Datadog::Tracing.trace(@klass.class.name, continue_from: nil, service: 'hutch', type: 'rabbitmq') do
12
13
  @klass.process(message)
13
14
  end
14
15
  end
data/lib/hutch/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Hutch
2
- VERSION = '1.1.1'.freeze
2
+ VERSION = '1.3.0'.freeze
3
3
  end
data/lib/hutch/worker.rb CHANGED
@@ -71,10 +71,10 @@ module Hutch
71
71
  message = Message.new(delivery_info, properties, payload, serializer)
72
72
  consumer_instance = consumer.new.tap { |c| c.broker, c.delivery_info = @broker, delivery_info }
73
73
  with_tracing(consumer_instance).handle(message)
74
- @broker.ack(delivery_info.delivery_tag)
74
+ @broker.ack(delivery_info.delivery_tag) unless consumer_instance.message_rejected?
75
75
  rescue => ex
76
76
  acknowledge_error(delivery_info, properties, @broker, ex)
77
- handle_error(properties, payload, consumer, ex)
77
+ handle_error(properties, payload, consumer, ex, delivery_info)
78
78
  end
79
79
 
80
80
  def with_tracing(klass)
@@ -83,7 +83,7 @@ module Hutch
83
83
 
84
84
  def handle_error(*args)
85
85
  Hutch::Config[:error_handlers].each do |backend|
86
- backend.handle(*args)
86
+ backend.handle *args.first(backend.method(:handle).arity)
87
87
  end
88
88
  end
89
89
 
@@ -119,7 +119,7 @@ describe Hutch::Broker do
119
119
 
120
120
  it 'utilises TLS' do
121
121
  expect(Hutch::Adapter).to receive(:new).with(
122
- hash_including(tls: true, port: 5671)
122
+ hash_including(tls: true)
123
123
  ).and_return(instance_double('Hutch::Adapter', start: nil))
124
124
 
125
125
  broker.open_connection
@@ -245,9 +245,9 @@ describe Hutch::Broker do
245
245
  context 'when given invalid details' do
246
246
  before { config[:mq_api_host] = 'notarealhost' }
247
247
  after { broker.disconnect }
248
- let(:set_up_api_connection) { ->{ broker.set_up_api_connection } }
248
+ let(:set_up_api_connection) { broker.set_up_api_connection }
249
249
 
250
- specify { expect(set_up_api_connection).to raise_error(StandardError) }
250
+ specify { expect { broker.set_up_api_connection }.to raise_error(StandardError) }
251
251
  end
252
252
  end
253
253
 
@@ -262,7 +262,7 @@ describe Hutch::Broker do
262
262
  args.first == ''
263
263
  args.last == arguments
264
264
  end
265
- broker.queue('test', arguments: arguments)
265
+ broker.queue('test'.freeze, arguments: arguments)
266
266
  end
267
267
  end
268
268
 
@@ -31,12 +31,10 @@ describe Hutch::Config do
31
31
  end
32
32
 
33
33
  context 'for invalid attributes' do
34
- let(:invalid_get) do
35
- -> { Hutch::Config.get(:invalid_attr) }
36
- end
34
+ let(:invalid_get) { Hutch::Config.get(:invalid_attr) }
37
35
 
38
36
  specify do
39
- expect(invalid_get).to raise_error Hutch::UnknownAttributeError
37
+ expect { invalid_get }.to raise_error Hutch::UnknownAttributeError
40
38
  end
41
39
  end
42
40
  end
@@ -65,14 +63,52 @@ describe Hutch::Config do
65
63
  end
66
64
 
67
65
  context 'boolean attributes' do
68
- before { Hutch::Config.set(:autoload_rails, new_value) }
69
- subject(:value) { Hutch::Config.user_config[:autoload_rails] }
66
+ context 'from non-empty string' do
67
+ before { Hutch::Config.set(:autoload_rails, new_value) }
68
+ subject(:value) { Hutch::Config.user_config[:autoload_rails] }
70
69
 
71
- let(:new_value) { "t" }
70
+ let(:new_value) { "t" }
72
71
 
73
72
 
74
- specify 'casts values to booleans' do
75
- expect(value).to eq true
73
+ specify 'casts values to booleans' do
74
+ expect(value).to eq true
75
+ end
76
+ end
77
+
78
+ context 'from empty string' do
79
+ before { Hutch::Config.set(:autoload_rails, new_value) }
80
+ subject(:value) { Hutch::Config.user_config[:autoload_rails] }
81
+
82
+ let(:new_value) { "" }
83
+
84
+
85
+ specify 'casts values to booleans' do
86
+ expect(value).to eq false
87
+ end
88
+ end
89
+
90
+ context 'from boolean' do
91
+ before { Hutch::Config.set(:autoload_rails, new_value) }
92
+ subject(:value) { Hutch::Config.user_config[:autoload_rails] }
93
+
94
+ let(:new_value) { true }
95
+
96
+
97
+ specify 'casts values to booleans' do
98
+ expect(value).to eq true
99
+ end
100
+ end
101
+
102
+ context 'from nil' do
103
+ before { Hutch::Config.set(:autoload_rails, new_value) }
104
+ subject(:value) { Hutch::Config.user_config[:autoload_rails] }
105
+
106
+ let(:new_value) { nil }
107
+
108
+
109
+ specify 'casts values to booleans' do
110
+ expect(value).to eq false
111
+ end
76
112
  end
77
113
  end
78
114
 
@@ -90,12 +126,10 @@ describe Hutch::Config do
90
126
  end
91
127
 
92
128
  context 'for invalid attributes' do
93
- let(:invalid_set) do
94
- -> { Hutch::Config.set(:invalid_attr, new_value) }
95
- end
129
+ let(:invalid_set) { Hutch::Config.set(:invalid_attr, new_value) }
96
130
 
97
131
  specify do
98
- expect(invalid_set).to raise_error Hutch::UnknownAttributeError
132
+ expect { invalid_set }.to raise_error Hutch::UnknownAttributeError
99
133
  end
100
134
  end
101
135
  end
@@ -109,8 +143,8 @@ describe Hutch::Config do
109
143
  end
110
144
 
111
145
  context 'for an invalid attribute' do
112
- let(:invalid_getter) { -> { Hutch::Config.invalid_attr } }
113
- specify { expect(invalid_getter).to raise_error NoMethodError }
146
+ let(:invalid_getter) { Hutch::Config.invalid_attr }
147
+ specify { expect { invalid_getter }.to raise_error NoMethodError }
114
148
  end
115
149
 
116
150
  context 'for an ENV-overriden value attribute' do
@@ -147,8 +181,8 @@ describe Hutch::Config do
147
181
  end
148
182
 
149
183
  context 'for an invalid attribute' do
150
- let(:invalid_setter) { -> { Hutch::Config.invalid_attr = new_value } }
151
- specify { expect(invalid_setter).to raise_error NoMethodError }
184
+ let(:invalid_setter) { Hutch::Config.invalid_attr = new_value }
185
+ specify { expect { invalid_setter }.to raise_error NoMethodError }
152
186
  end
153
187
  end
154
188
 
@@ -8,6 +8,7 @@ describe Hutch::ErrorHandlers::Bugsnag do
8
8
  before do
9
9
  Bugsnag.configure do |bugsnag|
10
10
  bugsnag.api_key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
11
+ bugsnag.logger = Logger.new(File::NULL) # suppress logging
11
12
  end
12
13
  end
13
14
 
@@ -15,7 +15,7 @@ describe Hutch::ErrorHandlers::SentryRaven do
15
15
  end
16
16
 
17
17
  it "logs the error to Sentry" do
18
- expect(Raven).to receive(:capture_exception).with(error, extra: { payload: payload })
18
+ expect(Raven).to receive(:capture_exception).with(error, {extra: { payload: payload }})
19
19
  error_handler.handle(properties, payload, double, error)
20
20
  end
21
21
  end
@@ -3,6 +3,14 @@ require 'spec_helper'
3
3
  describe Hutch::ErrorHandlers::Sentry do
4
4
  let(:error_handler) { Hutch::ErrorHandlers::Sentry.new }
5
5
 
6
+ before do
7
+ Sentry.init do
8
+ # initialize Sentry so that the integration acutally works
9
+ # otherwise, all its methods are going to return early
10
+ # so it will be impossible to check if it actually works
11
+ end
12
+ end
13
+
6
14
  describe '#handle' do
7
15
  let(:properties) { OpenStruct.new(message_id: "1") }
8
16
  let(:payload) { "{}" }
@@ -1,6 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe Hutch::Tracers::Datadog do
4
+ ::Datadog.logger.level = Logger::FATAL # suppress logging
5
+
4
6
  describe "#handle" do
5
7
  subject(:handle) { tracer.handle(message) }
6
8
 
@@ -25,14 +27,14 @@ RSpec.describe Hutch::Tracers::Datadog do
25
27
  let(:message) { double(:message) }
26
28
 
27
29
  before do
28
- allow(Datadog.tracer).to receive(:trace).and_call_original
30
+ allow(::Datadog::Tracing).to receive(:trace).and_call_original
29
31
  end
30
32
 
31
33
  it 'uses Datadog tracer' do
32
34
  handle
33
35
 
34
- expect(Datadog.tracer).to have_received(:trace).with('ClassName',
35
- hash_including(service: 'hutch', span_type: 'rabbitmq'))
36
+ expect(::Datadog::Tracing).to have_received(:trace).with('ClassName',
37
+ hash_including(service: 'hutch', type: 'rabbitmq'))
36
38
  end
37
39
 
38
40
  it 'processes the message' do
@@ -69,27 +69,32 @@ describe Hutch::Worker do
69
69
  end
70
70
 
71
71
  describe '#handle_message' do
72
+ subject { worker.handle_message(consumer, delivery_info, properties, payload) }
72
73
  let(:payload) { '{}' }
73
74
  let(:consumer_instance) { double('Consumer instance') }
74
75
  let(:delivery_info) { double('Delivery Info', routing_key: '',
75
76
  delivery_tag: 'dt') }
76
77
  let(:properties) { double('Properties', message_id: nil, content_type: "application/json") }
78
+ let(:log) { StringIO.new }
77
79
  before { allow(consumer).to receive_messages(new: consumer_instance) }
78
80
  before { allow(broker).to receive(:ack) }
79
81
  before { allow(broker).to receive(:nack) }
80
82
  before { allow(consumer_instance).to receive(:broker=) }
81
83
  before { allow(consumer_instance).to receive(:delivery_info=) }
84
+ before { allow(Hutch::Logging).to receive(:logger).and_return(Logger.new(log)) }
82
85
 
83
86
  it 'passes the message to the consumer' do
84
87
  expect(consumer_instance).to receive(:process).
85
88
  with(an_instance_of(Hutch::Message))
86
- worker.handle_message(consumer, delivery_info, properties, payload)
89
+ expect(consumer_instance).to receive(:message_rejected?).and_return(false)
90
+ subject
87
91
  end
88
92
 
89
93
  it 'acknowledges the message' do
90
94
  allow(consumer_instance).to receive(:process)
91
95
  expect(broker).to receive(:ack).with(delivery_info.delivery_tag)
92
- worker.handle_message(consumer, delivery_info, properties, payload)
96
+ expect(consumer_instance).to receive(:message_rejected?).and_return(false)
97
+ subject
93
98
  end
94
99
 
95
100
  context 'when the consumer fails and a requeue is configured' do
@@ -106,40 +111,79 @@ describe Hutch::Worker do
106
111
  expect(broker).to_not receive(:nack)
107
112
  expect(broker).to receive(:requeue)
108
113
 
109
- worker.handle_message(consumer, delivery_info, properties, payload)
114
+ subject
110
115
  end
111
116
  end
112
117
 
113
118
 
114
119
  context 'when the consumer raises an exception' do
120
+ let(:expected_log) { /ERROR .+ error in consumer .+ RuntimeError .+ backtrace:/m }
115
121
  before { allow(consumer_instance).to receive(:process).and_raise('a consumer error') }
116
122
 
117
123
  it 'logs the error' do
118
- Hutch::Config[:error_handlers].each do |backend|
119
- expect(backend).to receive(:handle)
120
- end
121
- worker.handle_message(consumer, delivery_info, properties, payload)
124
+ expect { subject }.to change { log.tap(&:rewind).read }.from("").to(expected_log)
122
125
  end
123
126
 
124
127
  it 'rejects the message' do
125
128
  expect(broker).to receive(:nack).with(delivery_info.delivery_tag)
126
- worker.handle_message(consumer, delivery_info, properties, payload)
129
+ subject
130
+ end
131
+
132
+ context 'when a custom error handler supports delivery info' do
133
+ let(:error_handler) do
134
+ Class.new(Hutch::ErrorHandlers::Base) do
135
+ def handle(_properties, _payload, _consumer, _ex, delivery_info)
136
+ raise unless delivery_info.delivery_tag == 'dt'
137
+ puts 'handled!'
138
+ end
139
+ end
140
+ end
141
+
142
+ around do |example|
143
+ original = Hutch::Config[:error_handlers]
144
+ Hutch::Config[:error_handlers] = [error_handler.new]
145
+ example.run
146
+ Hutch::Config[:error_handlers] = original
147
+ end
148
+
149
+ it 'calls the custom handler with delivery info' do
150
+ expect { subject }.to output("handled!\n").to_stdout
151
+ end
152
+ end
153
+
154
+ context 'when a custom error handler does not support delivery info' do
155
+ let(:error_handler) do
156
+ Class.new(Hutch::ErrorHandlers::Base) do
157
+ def handle(_properties, _payload, _consumer, _ex)
158
+ puts 'handled!'
159
+ end
160
+ end
161
+ end
162
+
163
+ around do |example|
164
+ original = Hutch::Config[:error_handlers]
165
+ Hutch::Config[:error_handlers] = [error_handler.new]
166
+ example.run
167
+ Hutch::Config[:error_handlers] = original
168
+ end
169
+
170
+ it 'calls the custom handler with delivery info' do
171
+ expect { subject }.to output("handled!\n").to_stdout
172
+ end
127
173
  end
128
174
  end
129
175
 
130
176
  context "when the payload is not valid json" do
131
177
  let(:payload) { "Not Valid JSON" }
178
+ let(:expected_log) { /ERROR .+ error in consumer .+ MultiJson::ParseError .+ backtrace:/m }
132
179
 
133
180
  it 'logs the error' do
134
- Hutch::Config[:error_handlers].each do |backend|
135
- expect(backend).to receive(:handle)
136
- end
137
- worker.handle_message(consumer, delivery_info, properties, payload)
181
+ expect { subject }.to change { log.tap(&:rewind).read }.from("").to(expected_log)
138
182
  end
139
183
 
140
184
  it 'rejects the message' do
141
185
  expect(broker).to receive(:nack).with(delivery_info.delivery_tag)
142
- worker.handle_message(consumer, delivery_info, properties, payload)
186
+ subject
143
187
  end
144
188
  end
145
189
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hutch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Marr
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-03-18 00:00:00.000000000 Z
12
+ date: 2024-11-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bunny
@@ -17,7 +17,7 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '2.19'
20
+ version: '2.23'
21
21
  - - "<"
22
22
  - !ruby/object:Gem::Version
23
23
  version: '3.0'
@@ -27,7 +27,7 @@ dependencies:
27
27
  requirements:
28
28
  - - ">="
29
29
  - !ruby/object:Gem::Version
30
- version: '2.19'
30
+ version: '2.23'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '3.0'
@@ -66,7 +66,7 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '4.2'
69
- - - "<"
69
+ - - "<="
70
70
  - !ruby/object:Gem::Version
71
71
  version: '8'
72
72
  type: :runtime
@@ -76,7 +76,7 @@ dependencies:
76
76
  - - ">="
77
77
  - !ruby/object:Gem::Version
78
78
  version: '4.2'
79
- - - "<"
79
+ - - "<="
80
80
  - !ruby/object:Gem::Version
81
81
  version: '8'
82
82
  description: Hutch is a Ruby library for enabling asynchronous inter-service communication
@@ -178,14 +178,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
178
  requirements:
179
179
  - - ">="
180
180
  - !ruby/object:Gem::Version
181
- version: '2.2'
181
+ version: '2.6'
182
182
  required_rubygems_version: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - ">="
185
185
  - !ruby/object:Gem::Version
186
186
  version: '0'
187
187
  requirements: []
188
- rubygems_version: 3.1.4
188
+ rubygems_version: 3.5.22
189
189
  signing_key:
190
190
  specification_version: 4
191
191
  summary: Opinionated asynchronous inter-service communication using RabbitMQ