lib/graphql/execution/interpreter/arguments.rb in graphql-1.11.10 vs lib/graphql/execution/interpreter/arguments.rb in graphql-1.12.0
- old
+ new
@@ -3,39 +3,59 @@
module GraphQL
module Execution
class Interpreter
# A wrapper for argument hashes in GraphQL queries.
#
+ # This object is immutable so that the runtime code can be sure that
+ # modifications don't leak from one use to another
+ #
# @see GraphQL::Query#arguments_for to get access to these objects.
class Arguments
extend Forwardable
include GraphQL::Dig
# The Ruby-style arguments hash, ready for a resolver.
# This hash is the one used at runtime.
#
# @return [Hash<Symbol, Object>]
- def keyword_arguments
- @keyword_arguments ||= begin
- kwargs = {}
+ attr_reader :keyword_arguments
+
+ # @param argument_values [nil, Hash{Symbol => ArgumentValue}]
+ # @param keyword_arguments [nil, Hash{Symbol => Object}]
+ def initialize(keyword_arguments: nil, argument_values:)
+ @empty = argument_values.nil? || argument_values.empty?
+ # This is only present when `extras` have been merged in:
+ if keyword_arguments
+ # This is a little crazy. We expect the `:argument_details` extra to _include extras_,
+ # but the object isn't created until _after_ extras are put together.
+ # So, we have to use a special flag here to say, "at the last minute, add yourself to the keyword args."
+ #
+ # Otherwise:
+ # - We can't access the final Arguments instance _while_ we're preparing extras
+ # - After we _can_ access it, it's frozen, so we can't add anything.
+ #
+ # So, this flag gives us a chance to sneak it in before freezing, _and_ while we have access
+ # to the new Arguments instance itself.
+ if keyword_arguments[:argument_details] == :__arguments_add_self
+ keyword_arguments[:argument_details] = self
+ end
+ @keyword_arguments = keyword_arguments.freeze
+ elsif !@empty
+ @keyword_arguments = {}
argument_values.each do |name, arg_val|
- kwargs[name] = arg_val.value
+ @keyword_arguments[name] = arg_val.value
end
- kwargs
+ @keyword_arguments.freeze
+ else
+ @keyword_arguments = NO_ARGS
end
+ @argument_values = argument_values ? argument_values.freeze : NO_ARGS
+ freeze
end
- # @param argument_values [nil, Hash{Symbol => ArgumentValue}]
- def initialize(argument_values:)
- @argument_values = argument_values
- @empty = argument_values.nil? || argument_values.empty?
- end
-
# @return [Hash{Symbol => ArgumentValue}]
- def argument_values
- @argument_values ||= {}
- end
+ attr_reader :argument_values
def empty?
@empty
end
@@ -43,9 +63,26 @@
def_delegators :argument_values, :each_value
def inspect
"#<#{self.class} @keyword_arguments=#{keyword_arguments.inspect}>"
end
+
+ # Create a new arguments instance which includes these extras.
+ #
+ # This is called by the runtime to implement field `extras: [...]`
+ #
+ # @param extra_args [Hash<Symbol => Object>]
+ # @return [Interpreter::Arguments]
+ # @api private
+ def merge_extras(extra_args)
+ self.class.new(
+ argument_values: argument_values,
+ keyword_arguments: keyword_arguments.merge(extra_args)
+ )
+ end
+
+ NO_ARGS = {}.freeze
+ EMPTY = self.new(argument_values: nil, keyword_arguments: NO_ARGS).freeze
end
end
end
end