Fix the test suite against unreleased Grape 4.0 - #985
Merged
Conversation
Two Grape 4.0 changes break the suite. Both are intentional upstream and already documented there, so the fixes belong here. 1. ruby-grape/grape#2817 builds an Array/Set element coercer eagerly, in the coercer's constructor, so that coercers -- shared across requests -- create no state at request time. A collection whose element type Grape cannot coerce is therefore rejected while the params block is evaluated, rather than on the first request that supplies the parameter. Entities::ApiError is an OpenStruct/Representable::Decorator with no `parse`, so `type: Array[Entities::ApiError]` now raises as the API class loads. The suite only ever GETs /swagger_doc, never posting to the route that declares it, which is why the misdeclaration was invisible before: the element coercer was never built. Give the fixtures a one-argument `parse`, as the bare `type: X` fixtures already have. The existing fixture comments credited this to "Grape 3.2+", which is right for bare `type: X` but not for the collection form: 3.2.0 and 3.3.4 both accept `type: Array[X]`. Corrected, and UPGRADING's custom-type bullet now covers the collection form too. 2. ruby-grape/grape#2823 turned Grape::Util::StackableValues into a read-only view: `.new` takes (new_values, inherited_values) and `[]=` is gone. swagger_routing_spec built instances directly, so it fails on both counts. combine_namespaces only ever reads keys off the object, so stub that contract instead of constructing one -- version-agnostic, and it cannot break again on either the constructor or the writer. Grape's read-only compatibility surface is unaffected: grape-swagger's lib never constructs a StackableValues, only reads through it. Verified green against grape=HEAD (4.0.0) and grape 3.3.4; example count is unchanged from master (530). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Danger ReportNo issues found. |
The route only exists so the API compiles and yields a real namespace_stackable to harvest; its body was never called, but an empty block trips Lint/EmptyBlock. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Author
|
@numbata you need to merge this PR first to fix grape=HEAD tests. |
Contributor
|
@dblock I will merge this and do release. |
The harvested StackableValues double stubbed new_values/inherited_values, but StackableValues#[] reads Grape's own ivars directly, so it silently returned the harvest probe's real (empty) namespace instead of the fixture for any caller reading through []. It only worked because Route#parse happens to read the namespace via new_values and never hits [] for it - a coincidence that breaks the moment that read path changes. Stub #[] too so all read paths agree. Also tightens the UPGRADING.md guidance for collection custom types: parse(val) = val performs no validation, so it's only safe as a documented pattern when the type is documentation-only and never actually coerces client input.
numbata
approved these changes
Aug 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two Grape 4.0 changes break the suite against
grape=HEAD. Both are intentional upstream and documented there, so the fixes belong here.Before this PR,
GRAPE_VERSION=HEAD bundle exec rspecfails 23 examples; after, the suite is green on bothgrape=HEAD(4.0.0) and grape 3.3.4.1. Collection custom types need
parse(12 failures)ruby-grape/grape#2817 builds an
Array/Setelement coercer eagerly, in the coercer's constructor, so that coercers — shared across requests — create no state at request time. A collection whose element type Grape cannot coerce is therefore rejected while theparamsblock is evaluated, instead of on the first request that supplies the parameter:Entities::ApiErroris anOpenStruct/Representable::Decoratorwith noparse, sotype: Array[Entities::ApiError]now raises as the API class loads. Fixed by giving the fixtures a one-argumentparse, which is what the baretype: Xfixtures already carry.Why this was invisible until now.
params_array_speconly everGETs/swagger_doc/...— it never POSTs to the route declaring that type. Grape used to build the element coercer lazily, so nothing ever built it. Had a request supplied the parameter, grape 3.3.4 answered400 {"error":"array_of_entities is invalid"}— theArgumentErrorraised inside the coercer and swallowed by the coercion validator. So this was a latent misdeclaration in the fixtures, not new breakage.Comment correction. The existing fixture comments credited this to "Grape 3.2+". That's right for bare
type: Xbut wrong for the collection form:type: XArray[X]ArgumentErrorArgumentErrorArgumentErrorArgumentErrorComments now point at Grape 4.0 / #2817, and UPGRADING's custom-type bullet — which currently only covers the bare form — gains the collection case.
2.
StackableValuesis a read-only view (11 failures)ruby-grape/grape#2823 made
Grape::Util::StackableValuesa read-only view:.newnow takes(new_values, inherited_values)and[]=is gone.swagger_routing_specbuilt instances directly and then wrote to them, so it fails on both counts.combine_namespacesonly ever reads keys off the object ([](key)returning an Array, for.lastand.join('/')), so these specs now stub that narrow contract rather than constructing a real instance. That's version-agnostic and can't break again on either the constructor or the writer — the same reasoning behind thestackable_values_doublehelper already inrequest_param_parsers/route_spec.rb, which covers the other 9.Note the fixture values changed shape slightly: the old
[]=pushed, so[:mount_path] = ['//foo/', '/bar']actually produced[['//foo/', '/bar']]. The doubles use the flat form, which is what real Grape produces (add_mount_pathpushes one string per mount level). Assertions are unchanged —Array#joinrecurses, so both shapes yield the same joined path.Grape's compatibility surface is unaffected: grape-swagger's
libnever constructs aStackableValues, it only reads through one (is_a?,#inherited_values,#new_values,#[]), all of which #2823 preserved deliberately.Verification
grape=HEAD(4.0.0) and grape 3.3.4.master(530, verified with--dry-runagainst both trees) — no coverage dropped.rubocopcould not run:.rubocop.ymlreferencesStyle/OneClassPerFile, which the installed RuboCop rejects. Pre-existing — it fails the same way on untouched files likelib/grape-swagger.rb.🤖 Generated with Claude Code