Ruby 3: Don't pass empty hash as kwargs #3610
Merged
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.
Problem description
When dynamically generating a schema via
GraphQL::Schema.definewhile running Ruby 3, we encountered an error like thiisCause
This issue apparently occurs when dynamically defining a field without any arguments in Ruby 3 (this worked without issue for Ruby 2.7 and earlier).
In this case, an empty hash is being passed to the
method_missingofDefinedObjectProxywhen attempting to define theargumentson aGraphQL::Fieldas being an empty hash{}.:graphql-ruby/lib/graphql/define/defined_object_proxy.rb
Lines 36 to 46 in bace1e4
This then calls
graphql-ruby/lib/graphql/define/instance_definable.rb
Lines 223 to 238 in bace1e4
which then just calls the accessor for
argumentsonGraphQL::Field. Note, also, the comment onAssignAttribute#callThis is a clue to the solution, to be described below.
Explanation
In Ruby 2.7 (using ruby2_keywords), this is not a problem because the empty hash is retained as just an empty hash when the splatted hash is further passed in a method as
*args. Here's a minimal Ruby example:In Ruby 3, the behaviour of splatting a hash argument into keyword arguments changed. In this particular case, after splatting with
**the hash disappears and then interpreted as just[]when passed through*args. Here's a minimal example in Ruby:This empty array is used in
AssignAttribute#callgraphql-ruby/lib/graphql/define/instance_definable.rb
Line 234 in bace1e4
Which is why
argumentsis assigned asnilrather than{}, despite being called asarguments = {}at the very top.Solution
The approach I'm suggesting in this PR is to avoid splatting an emptyHash into keyword arguments. This will be compatible with all versions of Ruby.