diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e923fab..a824797 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -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' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8d59262..7dd4991 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 diff --git a/.rubocop.yml b/.rubocop.yml index b7229b2..8959191 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -13,7 +13,7 @@ AllCops: - Guardfile - bin/* - tmp/**/* - TargetRubyVersion: 3.2 + TargetRubyVersion: 3.3 NewCops: enable Style: diff --git a/.tool-versions b/.tool-versions index 5809813..0a42b4a 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -ruby 4.0.2 -nodejs 24.14.1 +ruby 4.0.5 +nodejs 24.16.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f79a2..98154a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a4c6485 --- /dev/null +++ b/CLAUDE.md @@ -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. diff --git a/Gemfile.lock b/Gemfile.lock index cdb3be4..87ad056 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,28 +1,28 @@ 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 @@ -30,7 +30,7 @@ GEM 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) @@ -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) @@ -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 @@ -100,24 +100,25 @@ 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 @@ -125,7 +126,7 @@ CHECKSUMS 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 @@ -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 diff --git a/ears.gemspec b/ears.gemspec index 3fcba07..4f7a000 100644 --- a/ears.gemspec +++ b/ears.gemspec @@ -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' @@ -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 diff --git a/lib/ears/version.rb b/lib/ears/version.rb index 9aef6d2..9aba081 100644 --- a/lib/ears/version.rb +++ b/lib/ears/version.rb @@ -1,3 +1,3 @@ module Ears - VERSION = '0.25.0' + VERSION = '0.26.0' end diff --git a/package-lock.json b/package-lock.json index de9249e..02a1670 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "devDependencies": { "@invisionag/prettier-config": "^2.1.3", "@prettier/plugin-ruby": "^4.0.4", - "prettier": "^3.8.1" + "prettier": "^3.8.3" } }, "node_modules/@invisionag/prettier-config": { @@ -30,9 +30,9 @@ } }, "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", "bin": { @@ -62,9 +62,9 @@ "requires": {} }, "prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true } } diff --git a/package.json b/package.json index 1a14b71..1c559fd 100644 --- a/package.json +++ b/package.json @@ -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" }