Skip to content

Remove Grape::Util::ReverseStackableValues#2796

Merged
dblock merged 1 commit into
masterfrom
remove-reverse-stackable-values
Jul 14, 2026
Merged

Remove Grape::Util::ReverseStackableValues#2796
dblock merged 1 commit into
masterfrom
remove-reverse-stackable-values

Conversation

@ericproulx

Copy link
Copy Markdown
Contributor

Since #2795 internalized namespace_reverse_stackable into InheritableSetting, Grape::Util::ReverseStackableValues had a single consumer holding exactly two keys (rescue_handlers / base_only_rescue_handlers). Its only remaining job was reverse-order chain traversal — but InheritableSetting already tracks the same chain through #parent, so the class was a second way of walking a chain the setting has a pointer to.

This removes the class (and its spec) and replaces the store with a plain per-scope Hash of rescue_from registrations, merged recursively over #parent:

def merged_rescue_handlers(key)
  inherited = parent&.merged_rescue_handlers(key)
  own = @rescue_handler_maps&.[](key)
  return inherited unless own

  own.merge(inherited || {}) { |_klass, nearer, _inherited| nearer }
end

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. Behavior is unchanged, including the mounted-app specificity cases (prefers rescues defined by mounted even if outer is more specific) and same-scope ordering (rescue_from ArgumentError before rescue_from StandardError).

Side effects:

  • InheritableSetting#to_hash now exposes rescue_handlers / base_only_rescue_handlers (the merged view callers actually consume) instead of a namespace_reverse_stackable key.
  • Settings layers with no rescue_from no longer carry a ReverseStackableValues instance, and reads no longer build the intermediate arrays StackableValues#[] allocated per chain level.

🤖 Generated with Claude Code

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>
@ericproulx ericproulx force-pushed the remove-reverse-stackable-values branch from d69e812 to b03ffa2 Compare July 13, 2026 14:16
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

Danger Report

No issues found.

View run

@ericproulx ericproulx requested a review from dblock July 13, 2026 17:28
@dblock dblock merged commit 4eb2c59 into master Jul 14, 2026
73 of 137 checks passed
@dblock dblock deleted the remove-reverse-stackable-values branch July 14, 2026 01:24
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