hutch 1.1.1 → 1.3.0

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