hutch 1.2.0 → 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: 9c376d628987a9352a8ff9e49cd48ae0c12364cc3f9393185e8c811ff3052213
4
- data.tar.gz: 3aa07c8285817c1fefa10b7dc2b65e3fccf0b62ffbaf7a89e4b09f9a6db67035
3
+ metadata.gz: 6d1384ceb1b05cbdb706ac6e0f4cd4bfbdd86a49cbc2b6f61dabf96c1ca80ab5
4
+ data.tar.gz: d83a7db8184ed5baa8c00649de3d62a711ee07c04a598802193c1f6d4f7c97c8
5
5
  SHA512:
6
- metadata.gz: 0c0ba8155050290ebe63628ce10f9fdbd93e4ec483ce8e465564f9635913082d662f2b9f05ca3227f5ad6f5d9c773e9e9653abfbc867f816db0ddf589093d388
7
- data.tar.gz: 2ef54abbde9959378b15d1e60062b62ae0b55e8299029fa4fade407f6f9006b6df327f547dc2ba771638fa8d1e512860f0a78f4dc1fe145b337afb4d4000ea64
6
+ metadata.gz: 703c57b0c1e7ab3774d2fae904b6e7aa84f98b8f512ce8676c8456e63a4da4b97ab8dd77a460353d88c39d95997c70ca19695f6710bbde1674e6a03aa70a222b
7
+ data.tar.gz: 9fea55e47720b5cf78f834d714a709ada538cb851edcb5f17fc9ad2b587f68dffcc5c5524c2855d9673da322378d4fda19d85554a90c3f1d993726f7790da912
@@ -1,13 +1,10 @@
1
1
  name: Test
2
2
 
3
- concurrency:
3
+ concurrency:
4
4
  group: ${{ github.ref }}
5
5
  cancel-in-progress: true
6
6
 
7
- on:
8
- push:
9
- branches: [ master ]
10
- pull_request:
7
+ on: [push,pull_request,workflow_dispatch]
11
8
 
12
9
  jobs:
13
10
  test:
@@ -25,13 +22,15 @@ jobs:
25
22
  strategy:
26
23
  fail-fast: false
27
24
  matrix:
28
- ruby-version:
29
- - 2.7
30
- - 3.0
31
- - 3.1
25
+ ruby-version:
26
+ - '2.7'
27
+ - '3.0'
28
+ - '3.1'
29
+ - '3.2'
30
+ - '3.3'
32
31
 
33
32
  steps:
34
- - uses: actions/checkout@v3
33
+ - uses: actions/checkout@v4
35
34
  - name: Set up Ruby
36
35
  # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
37
36
  # change this to (see https://github.com/ruby/setup-ruby#versioning):
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", "~> 0.54.2"
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>
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'
@@ -298,6 +299,8 @@ module Hutch
298
299
  params[:tls_ca_certificates] = @config[:mq_tls_ca_certificates]
299
300
  end
300
301
  params[:heartbeat] = @config[:heartbeat]
302
+ params[:client_properties] = @config[:mq_client_properties]
303
+ params[:connection_name] = @config[:connection_name]
301
304
  params[:connection_timeout] = @config[:connection_timeout]
302
305
  params[:read_timeout] = @config[:read_timeout]
303
306
  params[:write_timeout] = @config[:write_timeout]
data/lib/hutch/cli.rb CHANGED
@@ -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
@@ -86,6 +86,9 @@ module Hutch
86
86
  # Default: `0`, no limit. See Bunny and RabbitMQ documentation.
87
87
  number_setting :channel_prefetch, 0
88
88
 
89
+ # [Client-Provided Connection Name](https://www.rabbitmq.com/docs/connections#client-provided-names)
90
+ string_setting :connection_name, nil
91
+
89
92
  # Bunny's socket open timeout
90
93
  number_setting :connection_timeout, 11
91
94
 
@@ -171,6 +174,7 @@ module Hutch
171
174
  # @return [Hash]
172
175
  def self.default_config
173
176
  @settings_defaults.merge({
177
+ mq_client_properties: {},
174
178
  mq_exchange_options: {},
175
179
  mq_tls_cert: nil,
176
180
  mq_tls_key: nil,
@@ -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.2.0'.freeze
2
+ VERSION = '1.3.0'.freeze
3
3
  end
data/lib/hutch/worker.rb CHANGED
@@ -74,7 +74,7 @@ module Hutch
74
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
 
@@ -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
@@ -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,29 +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
89
  expect(consumer_instance).to receive(:message_rejected?).and_return(false)
87
- worker.handle_message(consumer, delivery_info, properties, payload)
90
+ subject
88
91
  end
89
92
 
90
93
  it 'acknowledges the message' do
91
94
  allow(consumer_instance).to receive(:process)
92
95
  expect(broker).to receive(:ack).with(delivery_info.delivery_tag)
93
96
  expect(consumer_instance).to receive(:message_rejected?).and_return(false)
94
- worker.handle_message(consumer, delivery_info, properties, payload)
97
+ subject
95
98
  end
96
99
 
97
100
  context 'when the consumer fails and a requeue is configured' do
@@ -108,40 +111,79 @@ describe Hutch::Worker do
108
111
  expect(broker).to_not receive(:nack)
109
112
  expect(broker).to receive(:requeue)
110
113
 
111
- worker.handle_message(consumer, delivery_info, properties, payload)
114
+ subject
112
115
  end
113
116
  end
114
117
 
115
118
 
116
119
  context 'when the consumer raises an exception' do
120
+ let(:expected_log) { /ERROR .+ error in consumer .+ RuntimeError .+ backtrace:/m }
117
121
  before { allow(consumer_instance).to receive(:process).and_raise('a consumer error') }
118
122
 
119
123
  it 'logs the error' do
120
- Hutch::Config[:error_handlers].each do |backend|
121
- expect(backend).to receive(:handle)
122
- end
123
- worker.handle_message(consumer, delivery_info, properties, payload)
124
+ expect { subject }.to change { log.tap(&:rewind).read }.from("").to(expected_log)
124
125
  end
125
126
 
126
127
  it 'rejects the message' do
127
128
  expect(broker).to receive(:nack).with(delivery_info.delivery_tag)
128
- 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
129
173
  end
130
174
  end
131
175
 
132
176
  context "when the payload is not valid json" do
133
177
  let(:payload) { "Not Valid JSON" }
178
+ let(:expected_log) { /ERROR .+ error in consumer .+ MultiJson::ParseError .+ backtrace:/m }
134
179
 
135
180
  it 'logs the error' do
136
- Hutch::Config[:error_handlers].each do |backend|
137
- expect(backend).to receive(:handle)
138
- end
139
- worker.handle_message(consumer, delivery_info, properties, payload)
181
+ expect { subject }.to change { log.tap(&:rewind).read }.from("").to(expected_log)
140
182
  end
141
183
 
142
184
  it 'rejects the message' do
143
185
  expect(broker).to receive(:nack).with(delivery_info.delivery_tag)
144
- worker.handle_message(consumer, delivery_info, properties, payload)
186
+ subject
145
187
  end
146
188
  end
147
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.2.0
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-12-20 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.3.7
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