Repository navigation
Copy only containers between oneof variants instead of deep_dup - #3046
Merged
Merged
Conversation
OneofValidator gave each variant a deep_dup of the value, so that a variant that fails after coercing or defaulting something doesn't leak that into the next one. deep_dup also copies the leaves, which breaks uploads: Tempfile is a delegator and not an Object, so Object#deep_dup is forwarded to the File it wraps and returns a bare File. Types::File.parsed? then fails its is_a?(Tempfile) check, and every valid upload inside a oneof variant has been rejected with "does not match any of the allowed schemas" since oneof was added. Validators write by assigning into Hashes and Arrays, so copying those alone keeps the variants isolated. This was the last call to deep_dup in Grape, so its require goes too. No spec pinned the isolation: the suite passed with no copy at all. Add specs for a nested Hash coercion, an Array element coercion, a nested default and an upload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ericproulx
force-pushed
the
fix/oneof-copy-containers
branch
from
October 31, 2026 17:13
64b6a94 to
6833503
Compare
Danger ReportNo issues found. |
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
OneofValidatorgives each variant its own copy of the value, so a variant that fails after coercing or defaulting something doesn't leak that into the next one. It made the copy withdeep_dup, which also copies the leaves, and that breaks uploads:Every valid upload gets a 400,
value does not match any of the allowed schemas, and has sinceoneofwas added in 3.3.0. The same param in a plainHashscope works.Tempfileis a delegator and doesn't inherit fromObject, soObject#deep_dupis forwarded to theFileit wraps and returns a bareFile.Types::File.parsed?then fails itsis_a?(Tempfile)check.Validators write by assigning into Hashes and Arrays, so this PR copies only those and shares the leaves.
Isolation between variants
No spec pinned this: the full suite passed with no copy at all. The new specs show what each kind of copy does when the first variant fails and the second wins:
deep_dup(before)dupcode: "007", coerced to Integer by the failed variant"007""7""7""007""007""7""7""007"nil"red""red"nilOne limit: a custom coercer that mutates a String in place, rather than returning a new value, would now be seen by later variants, because leaves are shared.
Dropped require
This was Grape's last call to
deep_dup, soactive_support/core_ext/object/deep_dupis no longer required bylib/grape.rb. An app outside Rails that callsdeep_dupwhile relying on Grape to have loaded it now needs its own require. That was never documented, so there is no UPGRADING entry.grape-swaggerandgrape-entitydon't use it.Test plan
bundle exec rakepasses: RuboCop clean, 3162 examples, 0 failures.oneof_validator_spec.rbpasses ongemfiles/rack_2_2.gemfileandgemfiles/rack_3_2.gemfile.deep_dup, a shallowdup, no copy, not copying Arrays, and copying Hashes one level only each fail at least one new spec.🤖 Generated with Claude Code