Skip to content

Resolve a scope's request params in ParamsScope instead of the params DSL - #3028

Merged
ericproulx merged 1 commit into
masterfrom
refactor/params-scope-resolution
Oct 11, 2026
Merged

ericproulx merged 1 commit into
masterfrom
refactor/params-scope-resolution

Conversation

@ericproulx

Copy link
Copy Markdown
Contributor

Grape::DSL::Parameters holds the methods a params block calls (requires, optional, given, use, …). It also held three things that run on every request to resolve a scope's params:

  • #params, called by every validator, by AttributesIterator and by ParamsScope#meets_dependency?;
  • the private #map_params, which ParamsScope#validated_params called across the module boundary;
  • EmptyOptionalValue, the placeholder for an Array element that left an optional scope out. The validators and AttributesIterator named it as Grape::DSL::Parameters::EmptyOptionalValue.

All three read ParamsScope's state (@parent, @element, @optional, @independent), and ParamsScope is the only class that includes the module.

Change

  • #params, #map_params and the placeholder move to ParamsScope, unchanged. The placeholder is now Grape::Validations::ParamsScope::EmptyOptionalValue, documented where it is defined.
  • AttributesIterator#skip? tests for it with equal?, as the validators do. It used to ask the element's ==, which for a placeholder only identity can match.
  • DSL::Parameters is left with the params DSL alone.

Grape::DSL::Parameters::EmptyOptionalValue was a public constant (since 1.5.1) but never documented. GitHub code search finds it only in Grape and its forks, so it is moved rather than aliased, as #3009 did with the attribute iterator subclasses. No UPGRADING entry.

Tests

  • The #params examples in dsl/parameters_spec.rb ran against a stub of the module with element and parent assigned by hand. They now run against real scopes in params_scope_spec.rb, each nested scope captured from inside its own block. The stub loses its now-unused element/parent accessors.
  • Same example count as master. The ported examples pass on master's lib too, as a move should.
  • Mutation-checked map_params: each of the Hash, Array and fallback branches, and the root case, fails at least one ported example when broken.

Performance

benchmark-ips on API.call(env) for a POST with a 10-element Array block holding an optional nested Hash scope, Ruby 4.0.7, master and this branch run alternately, mean of two rounds:

master branch
interpreter 12.92k 12.79k (−1.0%)
YJIT 37.08k 37.20k (+0.3%)

Each run's error was ±2.3–4.2%, and the interpreter result flipped between rounds, so this is noise.

🤖 Generated with Claude Code

@ericproulx
ericproulx force-pushed the refactor/params-scope-resolution branch from 9355afa to 832ddd0 Compare October 10, 2026 22:58
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Danger Report

No issues found.

View run

@ericproulx
ericproulx requested a review from dblock October 10, 2026 23:01
… DSL

DSL::Parameters holds the methods a params block calls, but it also held
what resolves a scope's params on every request: #params, #map_params and
the EmptyOptionalValue placeholder. They read ParamsScope's state, the
only class that includes the module, and ParamsScope's own
#validated_params called the private #map_params across it; the
validators and AttributesIterator named the placeholder as
Grape::DSL::Parameters::EmptyOptionalValue.

All three now live in ParamsScope, and the placeholder is
ParamsScope::EmptyOptionalValue. AttributesIterator tests for it with
equal?, as the validators do, rather than asking the element's ==. The
#params examples that ran against a stub of the module now run against
real scopes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ericproulx
ericproulx force-pushed the refactor/params-scope-resolution branch from 832ddd0 to aed52c6 Compare October 11, 2026 08:26
@ericproulx
ericproulx merged commit a974d36 into master Oct 11, 2026
69 checks passed
@ericproulx
ericproulx deleted the refactor/params-scope-resolution branch October 11, 2026 08:28
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.

1 participant