Skip to content

Encapsulate namespace_reverse_stackable behind rescue-handler methods#2795

Merged
dblock merged 1 commit into
fix/base-only-rescue-handlersfrom
refactor/encapsulate-reverse-stackable
Jul 12, 2026
Merged

Encapsulate namespace_reverse_stackable behind rescue-handler methods#2795
dblock merged 1 commit into
fix/base-only-rescue-handlersfrom
refactor/encapsulate-reverse-stackable

Conversation

@ericproulx

Copy link
Copy Markdown
Contributor

Stacked on #2794 (targets fix/base-only-rescue-handlers). Rebase onto master once #2794 lands.

Follow-up to #2794. That bug happened because namespace_reverse_stackable — a store whose only purpose is rescue-handler maps — was a public attr_reader that two different files reached into with raw keys, so the base-only write and read could drift onto different stores.

Change

Make namespace_reverse_stackable protected (still used internally by inherit_from / copy_state_from / to_hash), and expose intent-revealing methods on InheritableSetting:

def rescue_handlers            # merged, child-scope handlers win
def base_only_rescue_handlers  # merged, child-scope handlers win
def add_rescue_handlers(mapping, subclasses:)
  • Write (dsl/request_response.rb): the handler_type branch collapses into add_rescue_handlers(mapping, subclasses: rescue_subclasses).
  • Read (endpoint.rb): error_middleware_options calls ns_stack.rescue_handlers / .base_only_rescue_handlers; the child-precedence merge moves out of Endpoint onto InheritableSetting, and the three Endpoint privates from Fix base-only rescue handlers never firing #2794 go away.

Now one place knows the key, the store, and the merge — the write/read can't drift apart again.

Tests

The reverse-ordering behaviour is already covered by ReverseStackableValues' own spec (14 examples), so the InheritableSetting specs that poked subject.namespace_reverse_stackable[...] are replaced with tests for rescue_handlers / base_only_rescue_handlers / add_rescue_handlers (including nested-scope override). request_response_spec assertions switch from the raw store to the named readers.

Full suite: 2353 examples, 0 failures. RuboCop clean.

🤖 Generated with Claude Code

namespace_reverse_stackable existed only to store rescue-handler maps, yet it
was a public attr_reader that callers reached into with raw keys from two
different files (the rescue_from write and the endpoint read). That's how the
base-only handlers write/read drifted onto mismatched stores.

Make the store internal (protected, still used by inherit_from/copy_state_from/
to_hash) and expose intent-revealing methods on InheritableSetting instead:
rescue_handlers / base_only_rescue_handlers (merged, child-scope-first) and
add_rescue_handlers(mapping, subclasses:). The DSL write and endpoint reads go
through those, so there's one place that knows the key, store and merge.

The reverse-ordering behaviour stays covered by ReverseStackableValues' own
spec; the InheritableSetting specs that poked the raw store are replaced with
tests for the new methods.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ericproulx
ericproulx force-pushed the refactor/encapsulate-reverse-stackable branch from dfdb251 to 7b4e99a Compare July 12, 2026 13:34
@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown

Danger Report

No issues found.

View run

@dblock
dblock merged commit 7235244 into fix/base-only-rescue-handlers Jul 12, 2026
69 checks passed
ericproulx added a commit that referenced this pull request Jul 13, 2026
Since #2795 internalized namespace_reverse_stackable into
InheritableSetting, ReverseStackableValues had a single consumer holding
exactly two keys (rescue_handlers / base_only_rescue_handlers). Its only
job was reverse-order chain traversal, but InheritableSetting already
tracks the same chain through #parent.

Replace the store with a plain per-scope Hash of rescue_from
registrations and merge it recursively over #parent. Hash#merge natively
yields the ordering Middleware::Error's find-based scan depends on: the
nearest scope's keys first (registration order preserved within a
scope), inherited keys appended, nearer handler winning on collision.

InheritableSetting#to_hash now exposes the merged rescue_handlers /
base_only_rescue_handlers instead of a namespace_reverse_stackable key.

Also drops the per-layer ReverseStackableValues allocation and the
intermediate arrays StackableValues#[] built on every read.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

2 participants