Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
# Specify a different separator for branch names

version: 2
updates:
- package-ecosystem: 'github-actions'
directory: '/'

multi-ecosystem-groups:
security:
schedule:
interval: 'weekly'
interval: 'daily'
open-pull-requests-limit: 0 # disables version-update PRs; security PRs unaffected
pull-request-branch-name:
separator: '-'

updates:
- package-ecosystem: 'bundler'
directory: '/'
patterns: ['*']
multi-ecosystem-group: 'security'

- package-ecosystem: 'github-actions'
directory: '/'
patterns: ['*']
multi-ecosystem-group: 'security'
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: ['3.2', '3.3', '3.4', '4.0']
ruby-version: ['3.3', '3.4', '4.0']

steps:
- uses: actions/checkout@v6
Expand Down
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ AllCops:
- Guardfile
- bin/*
- tmp/**/*
TargetRubyVersion: 3.2
TargetRubyVersion: 3.3
NewCops: enable

Style:
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ruby 4.0.2
nodejs 24.14.1
ruby 4.0.5
nodejs 24.16.0
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 0.26.0 (2026-06-01)

- Upgrade `connection_pool` dependency to `~> 3.0`
- Drop support for Ruby 3.2 (end-of-life); require Ruby `>= 3.3.0`

## 0.25.0 (2026-04-08)

- Drop support for bunny versions below 3.0.0
Expand Down
80 changes: 80 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# ears

Ruby gem for building RabbitMQ consumers using Bunny.

## Stack

- **Ruby** 4.0.x (see `.tool-versions`)
- **Bunny** >= 3.0.0 — AMQP client
- **connection_pool** ~> 3.0 — thread-safe channel pools for publishers
- **json** >= 2.9.0 — JSON serialization in middleware
- Dev tools: RSpec, RuboCop (rubocop-rspec, rubocop-rake), SimpleCov, YARD, Prettier (via Node)

## Common Commands

```bash
# Install deps
bundle install
npm install

# Tests
bundle exec rspec

# Lint / format
bundle exec rubocop
npm run lint # Prettier on Ruby files

# Autofix rubocop
bundle exec rubocop -A

# Docs
bundle exec yard doc
```

## Directory Layout

```
lib/
ears.rb # Top-level module: configure, connection, channel, setup, run!
ears/
configuration.rb # Ears::Configuration — all tunable constants
errors.rb # Custom error classes (required by configuration.rb)
consumer.rb # Abstract base class; subclass and override #work
consumer_wrapper.rb # Wraps a Consumer for Bunny delivery callbacks
setup.rb # Ears::Setup — DSL for exchange/queue/consumer wiring
publisher.rb # Ears::Publisher — publish and publish_with_confirmation
publisher_channel_pool.rb # Thread-safe ConnectionPool for publisher channels
publisher_confirmation_handler.rb
publisher_retry_handler.rb
middleware.rb # Middleware base
middlewares/
appsignal.rb
json.rb # Deserializes JSON payload before #work
max_retries.rb
testing.rb # Ears::Testing module entry-point
testing/
matchers.rb
message_capture.rb
publisher_mock.rb
test_helper.rb
spec/ # RSpec specs (mirrors lib/ structure)
```

## Architecture

**Consumer pattern:** Subclass `Ears::Consumer`, call `.configure(queue:, exchange:, routing_keys:, ...)` in the class body, override `#work(delivery_info, metadata, payload)` returning `:ack`, `:reject`, or `:requeue`. Middleware chain is applied in reverse order around `#work`.

**Publisher pattern:** Instantiate `Ears::Publisher.new(exchange_name)`, call `#publish(data, routing_key:)` or `#publish_with_confirmation(data, routing_key:)`. Internally uses `PublisherChannelPool` (two `ConnectionPool` instances — one standard, one with confirms). Pool is lazy-initialised with a mutex for thread safety and is fork-safe (resets on PID change).

**Setup DSL:** `Ears.setup { exchange(...); queue(...); consumer(...) }` or `Ears.setup_consumers(*classes)` which auto-wires from each class's `.configure` metadata.

**Configuration knobs** (set via `Ears.configure { |c| c.foo = ... }`): `rabbitmq_url`, `connection_name` (required), `publisher_pool_size` (32), `publisher_pool_timeout` (2s), `publisher_confirms_pool_size` (32), `publisher_confirms_timeout` (5s), plus retry/backoff params.

## Testing Support

`require 'ears/testing'` — provides `Ears::Testing::TestHelper` (RSpec include), `PublisherMock`, `MessageCapture`, and custom matchers (`have_been_published`).

## Known Tech Debt

- `Metrics/MethodLength` is suppressed for `Configuration#initialize` (initialises ~15 instance variables from constants; an options-hash refactor would be the real fix).
- `.rubocop_todo.yml` may have lingering entries — check before adding new inline disables.
39 changes: 20 additions & 19 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
PATH
remote: .
specs:
ears (0.25.0)
ears (0.26.0)
bunny (>= 3.0.0)
connection_pool (~> 2.4)
connection_pool (~> 3.0)
json (>= 2.9.0)

GEM
remote: https://rubygems.org/
specs:
amq-protocol (2.7.0)
amq-protocol (2.8.0)
ast (2.4.3)
bunny (3.1.0)
amq-protocol (~> 2.7)
logger (~> 1, >= 1.7)
sorted_set (~> 1, >= 1.0.2)
connection_pool (2.5.5)
connection_pool (3.0.2)
diff-lcs (1.6.2)
docile (1.4.1)
json (2.19.3)
json (2.19.7)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
logger (1.7.0)
parallel (1.28.0)
parallel (2.1.0)
parser (3.3.11.1)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
prism (1.9.0)
racc (1.8.1)
rainbow (3.1.1)
rake (13.3.1)
rake (13.4.2)
rbtree (0.4.6)
regexp_parser (2.12.0)
rspec (3.13.2)
Expand All @@ -46,11 +46,11 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.7)
rubocop (1.86.0)
rubocop (1.87.0)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parallel (>= 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
Expand Down Expand Up @@ -80,7 +80,7 @@ GEM
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
yard (0.9.38)
yard (0.9.44)

PLATFORMS
arm64-darwin
Expand All @@ -100,32 +100,33 @@ DEPENDENCIES
yard

CHECKSUMS
amq-protocol (2.7.0) sha256=a798da91f076cf8225193739d09785201f72acbc5293282a3592ad3eda5b3afc
amq-protocol (2.8.0) sha256=d06bbb341fa683459954f5a854388dac729171500116d81311cfbeaad83392ab
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
bundler (4.0.12) sha256=7f8b757d28dfb636e7b24fba2344ac6dd13b5b24f4b46d62573d483f211825ac
bunny (3.1.0) sha256=fd09ea8be3fbb7fe4b1063f874094b444f762f2da1692b3341751af7c6d62f3c
connection_pool (2.5.5) sha256=e54ff92855753df1fd7c59fa04a398833355f27dd14c074f8c83a05f72a716ad
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
ears (0.25.0)
json (2.19.3) sha256=289b0bb53052a1fa8c34ab33cc750b659ba14a5c45f3fcf4b18762dc67c78646
ears (0.26.0)
json (2.19.7) sha256=fe432c8639f6efff69f9d73b518a3705d9581ab93156f981ea72806e1e5bcc3e
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
parallel (1.28.0) sha256=33e6de1484baf2524792d178b0913fc8eb94c628d6cfe45599ad4458c638c970
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
prettier_print (1.2.1) sha256=a72838b5f23facff21f90a5423cdcdda19e4271092b41f4ea7f50b83929e6ff9
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
rbtree (0.4.6) sha256=14eea4469b24fd2472542e5f3eb105d6344c8ccf36f0b56d55fdcfeb4e0f10fc
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
rubocop (1.86.0) sha256=4ff1186fe16ebe9baff5e7aad66bb0ad4cabf5cdcd419f773146dbba2565d186
rubocop (1.87.0) sha256=b9d9ddf55116a513f8ef2c7ae660662d8b49301f118d3f0df61865b33a5c188d
rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d
rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2
Expand All @@ -137,7 +138,7 @@ CHECKSUMS
syntax_tree (6.3.0) sha256=56e25a9692c798ec94c5442fe94c5e94af76bef91edc8bb02052cbdecf35f13d
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
yard (0.9.38) sha256=721fb82afb10532aa49860655f6cc2eaa7130889df291b052e1e6b268283010f
yard (0.9.44) sha256=eb087e9b631ccd887b049f303d489963945452d5e2a7eb49a5a74a7cf6887f28

BUNDLED WITH
4.0.10
4.0.12
4 changes: 2 additions & 2 deletions ears.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
spec.description = 'A gem for building RabbitMQ consumers.'
spec.homepage = 'https://github.com/ivx/ears'
spec.license = 'MIT'
spec.required_ruby_version = Gem::Requirement.new('>= 3.2.9')
spec.required_ruby_version = Gem::Requirement.new('>= 3.3.0')

spec.metadata['allowed_push_host'] = 'https://rubygems.org'

Expand All @@ -33,6 +33,6 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']

spec.add_dependency 'bunny', '>= 3.0.0'
spec.add_dependency 'connection_pool', '~> 2.4'
spec.add_dependency 'connection_pool', '~> 3.0'
spec.add_dependency 'json', '>= 2.9.0'
end
2 changes: 1 addition & 1 deletion lib/ears/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Ears
VERSION = '0.25.0'
VERSION = '0.26.0'
end
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"devDependencies": {
"@invisionag/prettier-config": "^2.1.3",
"@prettier/plugin-ruby": "^4.0.4",
"prettier": "^3.8.1"
"prettier": "^3.8.3"
},
"prettier": "@invisionag/prettier-config/ruby"
}
Loading