Skip to content

Define named priority methods as real methods#115

Open
frewsxcv wants to merge 3 commits into
mainfrom
priority-real-methods
Open

Define named priority methods as real methods#115
frewsxcv wants to merge 3 commits into
mainfrom
priority-real-methods

Conversation

@frewsxcv

@frewsxcv frewsxcv commented Jul 8, 2026

Copy link
Copy Markdown

What

Replaces method_missing dispatch for named priority lookups with real method definitions:

  • Class methodsDelayed::Priority.eventual, .interactive, etc.
  • Instance predicatespriority.eventual?, priority.interactive?, etc.

Methods are defined when the class loads (for the default names) and are removed/redefined whenever Delayed::Priority.names= is assigned. Bodies still resolve through names_to_priority at call time, so assign_at_midpoint behavior is unchanged.

Why

method_missing methods are invisible to runtime reflection, so RDoc and Sorbet's tapioca can't see them. In practice this means every Sorbet-adopting app hits errors like:

Method eventual does not exist on T.class_of(Delayed::Priority) https://srb.help/7003

on every queue_with_priority Delayed::Priority.eventual call site, and has to hand-maintain a shim RBI. (In Betterment's retail monorepo this single pattern is currently the only thing blocking ~95 files from typed: true.) With real definitions, tapioca gems picks the methods up automatically:

Delayed::Priority.methods(false)
# => [..., :eventual, :interactive, :reporting, :user_visible, ...]

Behavior notes

  • Pre-existing methods are never overridden. Under method_missing, a real method always won (e.g. a hypothetical name of superclass would never shadow Class#superclass); the new definitions skip any name/predicate that already resolves, preserving that exactly. Covered by a new spec.
  • respond_to? semantics are unchanged, including respond_to?(:interactive) == false after names are customized (existing specs cover this).
  • Apps that customize names= in an initializer still won't see their custom names in a tapioca-generated gem RBI (tapioca doesn't boot the app) — same as today; they keep their shims.

Also

Fixes a pre-existing test-order leak in priority_spec.rb: the around hook reset names and alerts but not assign_at_midpoint, so any spec running after a midpoint example observed midpoint-shifted values. Reproducible on main with:

bundle exec rspec --order defined spec/delayed/priority_spec.rb:158 spec/delayed/active_job_adapter_spec.rb:149

Testing

  • spec/delayed/priority_spec.rb extended: real-method introspection, method cleanup on names= reassignment, and collision safety.
  • Full suite run locally on sqlite across multiple seeds; only failures are pre-existing on main in my local environment (Rails 8.0.2 snapshot/error-class drift, unrelated to this change).

Replaces method_missing dispatch for named priority class methods
(Delayed::Priority.eventual) and instance predicates (priority.eventual?)
with real method definitions, (re)defined whenever names are assigned.

Real methods are visible to runtime reflection, so documentation tools
and Sorbet's tapioca can see them; method_missing methods are not, which
forced Sorbet-adopting apps to shim every named priority by hand.

Pre-existing methods are never overridden, matching the old
method_missing behavior where a real method always won.

Also fixes a test-order leak in priority_spec.rb: assign_at_midpoint
was never reset by the around hook, so any example that ran after a
midpoint example saw midpoint-shifted priority values.
@frewsxcv frewsxcv marked this pull request as ready for review July 8, 2026 22:03
Comment thread lib/delayed/priority.rb Outdated
Comment on lines +135 to +136
@named_priority_class_methods = names.keys.filter_map { |name| define_priority_class_method(name) }
@named_priority_predicates = names.keys.filter_map { |name| define_priority_predicate(name) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be done without introducing additional pieces of state?

@smudge smudge Jul 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do still want to call remove_method for any previously-specified names, but I agree I think this logic could be implemented with @names as the only retained state.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rzane @smudge how does this look? b6e303e

Address review feedback: remove the @named_priority_class_methods and
@named_priority_predicates tracking ivars. On reassignment, remove the
methods for the previously-specified names (read from @NAMEs before it is
overwritten) using method_defined?(name, false) so only methods we defined
ourselves are removed, leaving any pre-existing method a colliding name
never overrode (e.g. Class#superclass, Numeric#zero?) in place.
@smudge smudge requested a review from rzane July 9, 2026 15:49
rzane
rzane previously approved these changes Jul 9, 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.

3 participants