Skip to content

Add Micro::Case::ActiveJob: async runner with rename-safe registry and DSL#151

Open
serradura wants to merge 1 commit into
mainfrom
feature/active-job-runner
Open

Add Micro::Case::ActiveJob: async runner with rename-safe registry and DSL#151
serradura wants to merge 1 commit into
mainfrom
feature/active-job-runner

Conversation

@serradura

Copy link
Copy Markdown
Member

Closes #2.

Adds Micro::Case::ActiveJob — a built-in, opt-in async runner. Purely additive; the public API of u-case stays frozen and the gemspec is unchanged (activejob remains an optional, host-provided dependency).

Summary

  • MyCase.async.call(input) / MyCase.later.call(input) — enqueue any Micro::Case subclass via ActiveJob. Per-use-case job subclass is built lazily and named <UseCase>::Job so it serializes across enqueue/dequeue.
  • active_job do … end class macro — eager DSL evaluated at class-body load time. Narrow surface: key, retry_on, discard_on, after_discard, default_options, around_perform, after_transaction_commit. Unknown methods raise NoMethodError with the valid-method list for typo protection.
  • Micro::Case::ActiveJob::Registry — thread-safe Mutex-backed mapping of stable string keys to use-case classes. Auto-registers klass.name => klass on first call so day-one adopters don't need a DSL declaration. Explicit key '…' makes renames safe: the payload carries the key, not the constant name.
  • Micro::Case::ActiveJob.batch(pairs) — uses ActiveJob.perform_all_later on Rails 7.1+, falls back to per-pair enqueue on older Rails.
  • Micro::Case::ActiveJob::Error (raised on the worker when raise_on_failure: true and the result is a failure; exposes .result) and Micro::Case::ActiveJob::UnknownKey (raised when a payload's key doesn't resolve).
  • Micro::Case.config.strict_registry (default false) — disables the safe_constantize fallback for registry misses.

ActiveJob features exposed declaratively: retry_on / discard_on (Rails 5.1+), after_discard (Rails 7.1+; logs once and no-ops on older Rails), enqueue_after_transaction_commit via after_transaction_commit :always|:never|:default (Rails 7.2+; logs + no-ops on older Rails), around_perform, default_options. GlobalID-coded inputs work transparently — the runner does not pre-coerce input on the worker side.

All 10 new internal argument validations route through Micro::Case::Check::Enabled / Disabled (matching the convention introduced in 5.4.0), so they participate in config.disable_runtime_checks = true.

Open-question resolutions

  • Bulk enqueue lives on Micro::Case::ActiveJob.batch (not Micro::Case.async_batch) — keeps Micro::Case's class-method namespace tight.
  • Inheritance: subclasses inherit job subclass reference, default_options, and registered key. A subclass that redeclares active_job do … end must use a fresh key — enforced by the registry's duplicate-key check.
  • Auto-registration on first async call: enabled.
  • Per-enqueue job_options: wins over DSL default_options (matches Rails' set overriding queue_as).
  • strict_registry default: false (friendlier for early adoption).
  • DSL evaluator named Micro::Case::ActiveJob::DSL.
  • Implementation lives in a single file (lib/micro/case/active_job.rb).

Test plan

  • bundle exec rake test — 832 runs, 8246 assertions, 0 failures.
  • bundle exec appraisal rails-8-1 rake test — 1079 runs, 0 failures.
  • ENABLE_TRANSITIONS=false bundle exec appraisal rails-8-1 rake test — 745 runs, 0 failures.
  • Verify the gem still loads without activejob (core lib/micro/case.rb does not require any active_job code; check.rb only adds methods, no AJ constant references).
  • 37 new tests cover: basic enqueue, job_options threading, raise_on_failure, rename-via-registry, auto-registration, duplicate/unknown keys, DSL methods (retry_on, default_options, around_perform, typo protection), inheritance, batch, to_proc composition, strict mode, and the check toggle for each new method.
  • Full matrix run (bundle exec rake matrix) against the CI grid — recommended before merge.

Out of scope (not bumped here)

  • Version bump. Per CLAUDE.md, this would be a minor bump (purely additive, no dependency-floor change), but the bump is intentionally not done in this PR — release tagging is a human step. The CHANGELOG entry sits under [Unreleased] until then.
  • README Documentation / Compatibility table updates — those bump only on release.

🤖 Generated with Claude Code

…d DSL

Closes #2. Purely additive — no breaking changes to the public API.

The new file is loaded via `require 'micro/case/active_job'`; the gemspec
is unchanged, so `activejob` remains an optional, host-provided dependency.

Public surface:
- `Micro::Case.async` / `Micro::Case.later` — return a `Caller` that
  enqueues via a per-use-case `<UseCase>::Job` subclass.
- `Micro::Case.active_job(&block)` — eager class macro evaluating the
  narrow DSL (`key`, `retry_on`, `discard_on`, `after_discard`,
  `default_options`, `around_perform`, `after_transaction_commit`).
  Unknown DSL methods raise `NoMethodError` for typo protection.
- `Micro::Case::ActiveJob::Registry` — thread-safe key-to-class mapping
  so renaming a use case between enqueue and pickup no longer raises
  `NameError`. Auto-registers `klass.name` for day-one adoption.
- `Micro::Case::ActiveJob::Caller` — merges DSL `default_options` with
  per-call `job_options` (explicit wins); `to_proc` for composition.
- `Micro::Case::ActiveJob.batch(pairs)` — uses `perform_all_later` on
  Rails 7.1+; falls back to per-pair enqueue on older Rails.
- `Micro::Case::ActiveJob::Error` / `UnknownKey` — documented rescue
  contracts.
- `Micro::Case.config.strict_registry` (default false) — disables the
  `safe_constantize` fallback for registry misses.

All argument checks introduced by the runner route through
`Micro::Case::Check::Enabled` / `Disabled` (10 new methods, identical
signatures on both modules) per the convention from 5.4.0.

Test plan: 37 new tests under `test/micro/case/active_job*` cover basic
enqueue, `job_options` threading, `raise_on_failure`, rename-via-registry,
auto-registration, duplicate/unknown keys, DSL methods, typo protection,
inheritance, batch, `to_proc` composition, strict mode, and the check
toggle. `bundle exec rake test` and the Rails 8.1 appraisal pass green
under both `ENABLE_TRANSITIONS=true|false`.

`activejob` added to the default `Gemfile`'s test group and to every
appraisal in `Appraisals` so the new suite runs under `rake test` and
the full matrix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@serradura serradura self-assigned this May 27, 2026
@serradura serradura added this to the 5.x milestone May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Raise/Rescue blocks

1 participant