ruby-grape / ruby-grape/grape-swagger
Four route reads have no reader on Grape's side: success, failure, default_response, desc
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 1.1k
- Forks
- 479
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 6
Description
Four of the route reads in lib/ resolve to no method Grape defines. They work only because Grape::Router::BaseRoute forwards unknown names to @options with delegate_missing_to :@options:
| read | site | option key | reader on BaseRoute? |
|---|---|---|---|
route.success |
endpoint.rb:103, endpoint.rb:159 |
:success |
no |
route.failure |
endpoint.rb:238 |
:failure |
no |
route.default_response |
endpoint.rb:266 |
:default_response |
no |
route.desc |
endpoint.rb:143 |
:desc |
no |
Audited by checking every route.<name> read in lib/ against Grape::Router::BaseRoute.method_defined?. The other eighteen — tags, hidden, entity, http_codes, summary, detail, settings, security, deprecated, produces, … — are genuine attr_readers, @pattern delegates, or def_delegators over Grape::Util::ApiDescription::DSL_METHODS. Only these four are method_missing.
Verified on grape 3.3.5:
Grape::Router::BaseRoute.method_defined?(:success) # => false
Grape::Router::BaseRoute.method_defined?(:failure) # => false
Grape::Router::BaseRoute.method_defined?(:default_response) # => false
Grape::Router::BaseRoute.method_defined?(:desc) # => false
Grape::Util::ApiDescription::DSL_METHODS.include?(:success) # => false
Nothing is on fire: delegate_missing_to is present in every released Grape, so all four read correctly today. But they are reads of an option bag dressed as reads of an API, and they are the reason grape-swagger cannot be made independent of that forwarding.
success and failure are the sharp ones
These two are not merely undefined — they mean different things depending on which desc form the caller used, and grape-swagger reads only one of the two spellings.
Grape::Util::ApiDescription aliases them in the block form:
alias success entity
alias failure http_codes
A Hash of options never reaches ApiDescription, so the literal keys survive unaliased. Both forms, same API, grape 3.3.5:
| declaration | stored keys | route.entity |
route.success |
route.http_codes |
route.failure |
|---|---|---|---|---|---|
desc 'a', success: X, failure: Y |
:success, :failure |
nil |
X |
nil |
Y |
desc 'b' do success X; failure Y end |
:entity, :http_codes |
X |
nil |
Y |
nil |
So there is no single reader that answers "what did the user declare as the success entity" — route.entity is blind to the Hash form and route.success is blind to the block form. grape-swagger papers over this by reading both and taking whichever is non-nil (route.entity || route.success at endpoint.rb:103, route.http_codes || route.failure at endpoint.rb:238), which is correct but only because method_missing is there to answer the second half.
This also means the obvious cleanup — "just use route.entity" — is wrong today. It would silently drop success: for every user of the Hash form, which is the form the README teaches throughout.
default_response has no spelling that works anywhere
Unlike success/failure, default_response has no alias, no DSL_METHODS entry, and no block-form equivalent. It exists only as a Hash key that Grape passes through untouched, and method_missing is the only thing that has ever read it back.
It also arrived at the reader form recently and by accident: #868 added it as route.options[:default_response], and #984 converted that line to route.default_response while sweeping 19 route.options reads down to 1. Every other line that sweep touched had a real delegator behind it.
And the README documents a different option
README.md:1124 ("Default response", linked from the TOC) documents the option as default::
desc 'thing', default: { message: 'the default response' }
but the code has only ever read :default_response. #868 introduced both spellings in the same commit — route.options[:default_response] in lib, default: in the README — and nothing has reconciled them since. End to end through add_swagger_documentation:
desc 'readme form', default: { message: 'the default response' } => responses: ["200"]
desc 'code form', default_response: { message: 'the default response' } => responses: ["200", "default"]
The documented spelling silently produces no default response. The spelling that works appears nowhere in the docs — only in spec/swagger_v2/api_swagger_v2_response_with_models_spec.rb:19 and api_swagger_v2_response_with_models_and_primitive_types_spec.rb.
default is in DSL_METHODS, so route.default would be a real reader: the README happens to describe the version of this feature that needs no forwarding, and the code implements the one that does.
Prior art
ruby-grape/grape#2857 proposed dropping delegate_missing_to :@options from BaseRoute — it identified these same four names as grape-swagger's entire exposure, and proposed normalizing the Hash form inside desc so success:/failure: would store :entity/:http_codes like the block form. It was closed on 2026-08-22 without being merged, so none of that normalization exists and there is no deadline attached to this issue. It is recorded here because the normalization half is the piece worth reviving on its own, independently of whether the forwarding is ever removed.
Constraint on the fix
Falling back to route.options[:success] and friends is not the answer here. #986 removed the last route.options read from lib/, and reaching back into the raw bag would undo that on purpose rather than by oversight: it keeps grape-swagger coupled to Grape's internal storage instead of its API, and it preserves the dual-spelling ambiguity rather than resolving it. Whatever the fix turns out to be, it has to land each of the four reads on a reader — one that already exists, or one Grape would define.
Environment
- grape-swagger
master(2.3.0 development) - grape 3.3.5
- Ruby 4.0.5
🤖 Generated with Claude Code
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the four reads listed in lib/grape-swagger/endpoint.rb and inspect Grape::Router::BaseRoute and Grape::Util::ApiDescription for existing readers and aliases. Run the response specs named in the issue, then verify both Hash and block desc forms retain their success/failure behavior and that the documented default spelling matches the implemented behavior. Done means the four reads no longer depend on accidental method_missing forwarding.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ruby
- Domain
- api, documentation
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100