Fix double wrap on json errors#2771
Conversation
Danger ReportNo issues found. |
|
The fix is correct and the regression is real — happy to see this restored. One correction on the explanation, though, since the new code comment is meant to prevent a future refactor and currently documents the wrong cause.
A plain SimpleDelegator.new({}).is_a?(Hash) # => false (not forwarded)If forwarding were the mechanism, this would be def kind_of?(klass)
klass == output.class || super # Hash == @output_hash.class => Hash == Hash => true
end
alias is_a? kind_of?So it matches because of the class's own The Suggested tweak to the comment so it doesn't get "corrected" later by someone who tests a bare # Use +is_a?+ rather than +case/when+ here. +case/when Hash+ matches via
# +Module#===+, a C-level real-class check that ignores any Ruby-level override.
# +Grape::Entity#serializable_hash+ returns an +OutputBuilder+ (a +SimpleDelegator+)
# that defines its own +kind_of?+/+is_a?+ returning true for the wrapped Hash's class,
# so +is_a?+ matches while +case/when Hash+ would fall through and wrap the payload in
# a spurious +{ error: ... }+ envelope.Mechanism aside, the patch itself is good to merge. |
|
@MattHall thank you, TIL :) |
|
@ericproulx would it be possible to get this fix released as 3.3.1? |
|
@schinery of course. If you don't mind I'm gonna wait a little more in case other issues are created. Let's say by the end of the week, I'm gonna release 3.3.1 with that fix. |
Magic, thanks @ericproulx 👍🏻 |
When an error is presented via an entity, ErrorFormatter::Base#present returns:
Grape::Entity#serializable_hashdoes not return a plainHash- it returns aGrape::Entity::Exposure::NestingExposure::OutputBuilder, which is aSimpleDelegatorwrapping aHash(not a Hash subclass).3.3.0 refactored
ErrorFormatter::Json#wrap_messagefrom anis_a?check tocase/when:3.2.1
3.3.0
obj.is_a?(Hash)is an ordinary method call, soSimpleDelegatorforwards it to the wrappedHash(true). But case/when Hash matches via Module#===, a C-level ancestry check that ignores delegation (false) — so the delegator falls through to the else branch and gets wrapped.Fix
Restore
is_a?based matching inErrorFormatter::Json#wrap_message, bringing it back in line with the base class and the pre-3.3.0 behaviour. A comment documents theModule#===vsis_a?delegation pitfall so it isn't refactored back.