From 6909931d461f934a23f6d2fd52e8a14215e3dc63 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Tue, 17 Oct 2023 15:38:18 -0400 Subject: [PATCH] Migrate NullContext to use Singleton --- lib/graphql/query/null_context.rb | 12 ++---------- lib/graphql/schema.rb | 10 +++++----- lib/graphql/schema/directive.rb | 2 +- lib/graphql/schema/enum.rb | 4 ++-- lib/graphql/schema/has_single_input_argument.rb | 4 ++-- lib/graphql/schema/member/has_arguments.rb | 10 +++++----- lib/graphql/schema/member/has_fields.rb | 8 ++++---- lib/graphql/schema/member/has_interfaces.rb | 4 ++-- lib/graphql/schema/member/validates_input.rb | 6 +++--- lib/graphql/schema/resolver.rb | 6 +++--- lib/graphql/schema/union.rb | 2 +- lib/graphql/subscriptions.rb | 2 +- lib/graphql/subscriptions/event.rb | 2 +- spec/graphql/query/null_context_spec.rb | 13 +++++++------ spec/graphql/schema/enum_spec.rb | 2 +- spec/graphql/schema/input_object_spec.rb | 2 +- spec/graphql/schema/scalar_spec.rb | 6 +++--- spec/integration/rails/graphql/input_object_spec.rb | 2 +- spec/integration/rails/graphql/schema_spec.rb | 4 ++-- 19 files changed, 47 insertions(+), 54 deletions(-) diff --git a/lib/graphql/query/null_context.rb b/lib/graphql/query/null_context.rb index bd43b5febb..099d9af488 100644 --- a/lib/graphql/query/null_context.rb +++ b/lib/graphql/query/null_context.rb @@ -3,6 +3,8 @@ module GraphQL class Query # This object can be `ctx` in places where there is no query class NullContext + include Singleton + class NullQuery def after_lazy(value) yield(value) @@ -27,16 +29,6 @@ def initialize def interpreter? true end - - class << self - extend Forwardable - - def instance - @instance ||= self.new - end - - def_delegators :instance, :query, :warden, :schema, :interpreter?, :dataloader, :[], :fetch, :dig, :key? - end end end end diff --git a/lib/graphql/schema.rb b/lib/graphql/schema.rb index 564d7c43aa..2d54108f95 100644 --- a/lib/graphql/schema.rb +++ b/lib/graphql/schema.rb @@ -325,7 +325,7 @@ def plugins # Build a map of `{ name => type }` and return it # @return [Hash Class>] A dictionary of type classes by their GraphQL name # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes. - def types(context = GraphQL::Query::NullContext) + def types(context = GraphQL::Query::NullContext.instance) all_types = non_introspection_types.merge(introspection_system.types) visible_types = {} all_types.each do |k, v| @@ -352,7 +352,7 @@ def types(context = GraphQL::Query::NullContext) # @param type_name [String] # @return [Module, nil] A type, or nil if there's no type called `type_name` - def get_type(type_name, context = GraphQL::Query::NullContext) + def get_type(type_name, context = GraphQL::Query::NullContext.instance) local_entry = own_types[type_name] type_defn = case local_entry when nil @@ -483,7 +483,7 @@ def warden_class # @param type [Module] The type definition whose possible types you want to see # @return [Hash] All possible types, if no `type` is given. # @return [Array] Possible types for `type`, if it's given. - def possible_types(type = nil, context = GraphQL::Query::NullContext) + def possible_types(type = nil, context = GraphQL::Query::NullContext.instance) if type # TODO duck-typing `.possible_types` would probably be nicer here if type.kind.union? @@ -566,7 +566,7 @@ def type_from_ast(ast_node, context: nil) GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node) end - def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext) + def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance) parent_type = case type_or_name when LateBoundType get_type(type_or_name.name, context) @@ -589,7 +589,7 @@ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext) end end - def get_fields(type, context = GraphQL::Query::NullContext) + def get_fields(type, context = GraphQL::Query::NullContext.instance) type.fields(context) end diff --git a/lib/graphql/schema/directive.rb b/lib/graphql/schema/directive.rb index 4fd33db57f..4916c2d924 100644 --- a/lib/graphql/schema/directive.rb +++ b/lib/graphql/schema/directive.rb @@ -119,7 +119,7 @@ def initialize(owner, **arguments) # - lazy resolution # Probably, those won't be needed here, since these are configuration arguments, # not runtime arguments. - @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext) + @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance) end def graphql_name diff --git a/lib/graphql/schema/enum.rb b/lib/graphql/schema/enum.rb index d63ecf6e72..02ecc946b3 100644 --- a/lib/graphql/schema/enum.rb +++ b/lib/graphql/schema/enum.rb @@ -68,7 +68,7 @@ def value(*args, **kwargs, &block) end # @return [Array] Possible values of this enum - def enum_values(context = GraphQL::Query::NullContext) + def enum_values(context = GraphQL::Query::NullContext.instance) inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil visible_values = [] warden = Warden.from_context(context) @@ -110,7 +110,7 @@ def all_enum_value_definitions end # @return [Hash GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name. - def values(context = GraphQL::Query::NullContext) + def values(context = GraphQL::Query::NullContext.instance) enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val } end diff --git a/lib/graphql/schema/has_single_input_argument.rb b/lib/graphql/schema/has_single_input_argument.rb index 2021e92baa..1c1dc37fac 100644 --- a/lib/graphql/schema/has_single_input_argument.rb +++ b/lib/graphql/schema/has_single_input_argument.rb @@ -54,11 +54,11 @@ def dummy end end - def field_arguments(context = GraphQL::Query::NullContext) + def field_arguments(context = GraphQL::Query::NullContext.instance) dummy.arguments(context) end - def get_field_argument(name, context = GraphQL::Query::NullContext) + def get_field_argument(name, context = GraphQL::Query::NullContext.instance) dummy.get_argument(name, context) end diff --git a/lib/graphql/schema/member/has_arguments.rb b/lib/graphql/schema/member/has_arguments.rb index 55a1d63a3a..14d2aaf06c 100644 --- a/lib/graphql/schema/member/has_arguments.rb +++ b/lib/graphql/schema/member/has_arguments.rb @@ -109,7 +109,7 @@ def remove_argument(arg_defn) end # @return [Hash GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions - def arguments(context = GraphQL::Query::NullContext) + def arguments(context = GraphQL::Query::NullContext.instance) if own_arguments.any? own_arguments_that_apply = {} own_arguments.each do |name, args_entry| @@ -133,7 +133,7 @@ def inherited(child_class) end module InheritedArguments - def arguments(context = GraphQL::Query::NullContext) + def arguments(context = GraphQL::Query::NullContext.instance) own_arguments = super inherited_arguments = superclass.arguments(context) @@ -166,7 +166,7 @@ def all_argument_definitions end - def get_argument(argument_name, context = GraphQL::Query::NullContext) + def get_argument(argument_name, context = GraphQL::Query::NullContext.instance) warden = Warden.from_context(context) for ancestor in ancestors if ancestor.respond_to?(:own_arguments) && @@ -181,7 +181,7 @@ def get_argument(argument_name, context = GraphQL::Query::NullContext) end module FieldConfigured - def arguments(context = GraphQL::Query::NullContext) + def arguments(context = GraphQL::Query::NullContext.instance) own_arguments = super if @resolver_class inherited_arguments = @resolver_class.field_arguments(context) @@ -236,7 +236,7 @@ def all_argument_definitions end # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name. - def get_argument(argument_name, context = GraphQL::Query::NullContext) + def get_argument(argument_name, context = GraphQL::Query::NullContext.instance) warden = Warden.from_context(context) if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden)) visible_arg diff --git a/lib/graphql/schema/member/has_fields.rb b/lib/graphql/schema/member/has_fields.rb index f998616bad..0b99af19af 100644 --- a/lib/graphql/schema/member/has_fields.rb +++ b/lib/graphql/schema/member/has_fields.rb @@ -97,7 +97,7 @@ def all_field_definitions end module InterfaceMethods - def get_field(field_name, context = GraphQL::Query::NullContext) + def get_field(field_name, context = GraphQL::Query::NullContext.instance) warden = Warden.from_context(context) for ancestor in ancestors if ancestor.respond_to?(:own_fields) && @@ -110,7 +110,7 @@ def get_field(field_name, context = GraphQL::Query::NullContext) end # @return [Hash GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields - def fields(context = GraphQL::Query::NullContext) + def fields(context = GraphQL::Query::NullContext.instance) warden = Warden.from_context(context) # Local overrides take precedence over inherited fields visible_fields = {} @@ -130,7 +130,7 @@ def fields(context = GraphQL::Query::NullContext) end module ObjectMethods - def get_field(field_name, context = GraphQL::Query::NullContext) + def get_field(field_name, context = GraphQL::Query::NullContext.instance) # Objects need to check that the interface implementation is visible, too warden = Warden.from_context(context) ancs = ancestors @@ -148,7 +148,7 @@ def get_field(field_name, context = GraphQL::Query::NullContext) end # @return [Hash GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields - def fields(context = GraphQL::Query::NullContext) + def fields(context = GraphQL::Query::NullContext.instance) # Objects need to check that the interface implementation is visible, too warden = Warden.from_context(context) # Local overrides take precedence over inherited fields diff --git a/lib/graphql/schema/member/has_interfaces.rb b/lib/graphql/schema/member/has_interfaces.rb index 2d6de05237..1b8c9aa76a 100644 --- a/lib/graphql/schema/member/has_interfaces.rb +++ b/lib/graphql/schema/member/has_interfaces.rb @@ -70,7 +70,7 @@ def inherited(child_class) end module InheritedInterfaces - def interfaces(context = GraphQL::Query::NullContext) + def interfaces(context = GraphQL::Query::NullContext.instance) visible_interfaces = super inherited_interfaces = superclass.interfaces(context) if visible_interfaces.any? @@ -99,7 +99,7 @@ def interface_type_memberships end # param context [Query::Context] If omitted, skip filtering. - def interfaces(context = GraphQL::Query::NullContext) + def interfaces(context = GraphQL::Query::NullContext.instance) warden = Warden.from_context(context) visible_interfaces = nil own_interface_type_memberships.each do |type_membership| diff --git a/lib/graphql/schema/member/validates_input.rb b/lib/graphql/schema/member/validates_input.rb index 7d3b1e6906..80113863fe 100644 --- a/lib/graphql/schema/member/validates_input.rb +++ b/lib/graphql/schema/member/validates_input.rb @@ -17,15 +17,15 @@ def validate_input(val, ctx, max_errors: nil) end def valid_isolated_input?(v) - valid_input?(v, GraphQL::Query::NullContext) + valid_input?(v, GraphQL::Query::NullContext.instance) end def coerce_isolated_input(v) - coerce_input(v, GraphQL::Query::NullContext) + coerce_input(v, GraphQL::Query::NullContext.instance) end def coerce_isolated_result(v) - coerce_result(v, GraphQL::Query::NullContext) + coerce_result(v, GraphQL::Query::NullContext.instance) end end end diff --git a/lib/graphql/schema/resolver.rb b/lib/graphql/schema/resolver.rb index 0ff8e79846..bdf0ad965b 100644 --- a/lib/graphql/schema/resolver.rb +++ b/lib/graphql/schema/resolver.rb @@ -205,12 +205,12 @@ def load_arguments(args) end end - def get_argument(name, context = GraphQL::Query::NullContext) + def get_argument(name, context = GraphQL::Query::NullContext.instance) self.class.get_argument(name, context) end class << self - def field_arguments(context = GraphQL::Query::NullContext) + def field_arguments(context = GraphQL::Query::NullContext.instance) arguments(context) end @@ -218,7 +218,7 @@ def any_field_arguments? any_arguments? end - def get_field_argument(name, context = GraphQL::Query::NullContext) + def get_field_argument(name, context = GraphQL::Query::NullContext.instance) get_argument(name, context) end diff --git a/lib/graphql/schema/union.rb b/lib/graphql/schema/union.rb index d62b710c0d..aaef250038 100644 --- a/lib/graphql/schema/union.rb +++ b/lib/graphql/schema/union.rb @@ -10,7 +10,7 @@ def inherited(child_class) super end - def possible_types(*types, context: GraphQL::Query::NullContext, **options) + def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options) if types.any? types.each do |t| assert_valid_union_member(t) diff --git a/lib/graphql/subscriptions.rb b/lib/graphql/subscriptions.rb index 78f3e2e9ab..fbbfb646b2 100644 --- a/lib/graphql/subscriptions.rb +++ b/lib/graphql/subscriptions.rb @@ -83,7 +83,7 @@ def trigger(event_name, args, object, scope: nil, context: {}) # Normalize symbol-keyed args to strings, try camelizing them # Should this accept a real context somehow? - normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext) + normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext.instance) event = Subscriptions::Event.new( name: normalized_event_name, diff --git a/lib/graphql/subscriptions/event.rb b/lib/graphql/subscriptions/event.rb index 991513a47b..0e01bafa25 100644 --- a/lib/graphql/subscriptions/event.rb +++ b/lib/graphql/subscriptions/event.rb @@ -37,7 +37,7 @@ def initialize(name:, arguments:, field: nil, context: nil, scope: nil) end # @return [String] an identifier for this unit of subscription - def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext) + def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance) subscription = field.resolver || GraphQL::Schema::Subscription normalized_args = stringify_args(field, arguments.to_h, context) subscription.topic_for(arguments: normalized_args, field: field, scope: scope) diff --git a/spec/graphql/query/null_context_spec.rb b/spec/graphql/query/null_context_spec.rb index a8a631c9ab..87d78b6b93 100644 --- a/spec/graphql/query/null_context_spec.rb +++ b/spec/graphql/query/null_context_spec.rb @@ -2,35 +2,36 @@ require "spec_helper" describe GraphQL::Query::NullContext do + let(:null_context) { GraphQL::Query::NullContext.instance } describe "#[]" do it "returns nil" do - assert_nil(GraphQL::Query::NullContext[:foo]) + assert_nil(null_context[:foo]) end end describe "#fetch" do it "returns the default value argument" do - assert_equal(:default, GraphQL::Query::NullContext.fetch(:foo, :default)) + assert_equal(:default, null_context.fetch(:foo, :default)) end it "returns the block result" do - assert_equal(:default, GraphQL::Query::NullContext.fetch(:foo) { :default }) + assert_equal(:default, null_context.fetch(:foo) { :default }) end it "raises a KeyError when not passed a default value or a block" do - assert_raises(KeyError) { GraphQL::Query::NullContext.fetch(:foo) } + assert_raises(KeyError) { null_context.fetch(:foo) } end end describe "#key?" do it "returns false" do - assert(!GraphQL::Query::NullContext.key?(:foo)) + assert(!null_context.key?(:foo)) end end describe "#dig?" do it "returns nil" do - assert_nil(GraphQL::Query::NullContext.dig(:foo, :bar, :baz)) + assert_nil(null_context.dig(:foo, :bar, :baz)) end end end diff --git a/spec/graphql/schema/enum_spec.rb b/spec/graphql/schema/enum_spec.rb index 77aa78f0f8..772828e1ae 100644 --- a/spec/graphql/schema/enum_spec.rb +++ b/spec/graphql/schema/enum_spec.rb @@ -280,7 +280,7 @@ def names(things:) describe "validate_input with bad input" do it "returns an invalid result" do - result = enum.validate_input("bad enum", GraphQL::Query::NullContext) + result = enum.validate_input("bad enum", GraphQL::Query::NullContext.instance) assert(!result.valid?) assert_equal( result.problems.first['explanation'], diff --git a/spec/graphql/schema/input_object_spec.rb b/spec/graphql/schema/input_object_spec.rb index ad4f63f048..d0f2cbd326 100644 --- a/spec/graphql/schema/input_object_spec.rb +++ b/spec/graphql/schema/input_object_spec.rb @@ -807,7 +807,7 @@ def f(arg:) let(:input_object) { Dummy::DairyProductInput } def validate_isolated_input(t, input) - t.validate_input(input, GraphQL::Query::NullContext) + t.validate_input(input, GraphQL::Query::NullContext.instance) end describe "input validation" do diff --git a/spec/graphql/schema/scalar_spec.rb b/spec/graphql/schema/scalar_spec.rb index 403b58e3ae..bdbfdf8fe0 100644 --- a/spec/graphql/schema/scalar_spec.rb +++ b/spec/graphql/schema/scalar_spec.rb @@ -147,7 +147,7 @@ class Query < GraphQL::Schema::Object describe "validate_input with good input" do - let(:result) { GraphQL::Types::Int.validate_input(150, GraphQL::Query::NullContext) } + let(:result) { GraphQL::Types::Int.validate_input(150, GraphQL::Query::NullContext.instance) } it "returns a valid result" do assert(result.valid?) @@ -155,7 +155,7 @@ class Query < GraphQL::Schema::Object end describe "validate_input with bad input" do - let(:result) { GraphQL::Types::Int.validate_input("bad num", GraphQL::Query::NullContext) } + let(:result) { GraphQL::Types::Int.validate_input("bad num", GraphQL::Query::NullContext.instance) } it "returns an invalid result for bad input" do assert(!result.valid?) @@ -206,7 +206,7 @@ def self.coerce_result(value, _ctx) end describe "custom scalar errors" do - let(:result) { custom_scalar.validate_input("xyz", GraphQL::Query::NullContext) } + let(:result) { custom_scalar.validate_input("xyz", GraphQL::Query::NullContext.instance) } it "returns an invalid result" do assert !result.valid? diff --git a/spec/integration/rails/graphql/input_object_spec.rb b/spec/integration/rails/graphql/input_object_spec.rb index 2da67c0ff6..b224490e45 100644 --- a/spec/integration/rails/graphql/input_object_spec.rb +++ b/spec/integration/rails/graphql/input_object_spec.rb @@ -7,7 +7,7 @@ nil, ruby_kwargs: { source: 'COW', fatContent: 0.8 }, defaults_used: Set.new, - context: GraphQL::Query::NullContext) + context: GraphQL::Query::NullContext.instance) end describe '#to_json' do diff --git a/spec/integration/rails/graphql/schema_spec.rb b/spec/integration/rails/graphql/schema_spec.rb index a8f96589c2..cff1de4ca3 100644 --- a/spec/integration/rails/graphql/schema_spec.rb +++ b/spec/integration/rails/graphql/schema_spec.rb @@ -60,7 +60,7 @@ describe "#resolve_type" do describe "when the return value is nil" do it "returns nil" do - result = relay_schema.resolve_type(123, nil, GraphQL::Query::NullContext) + result = relay_schema.resolve_type(123, nil, GraphQL::Query::NullContext.instance) assert_equal([nil, nil], result) end end @@ -68,7 +68,7 @@ describe "when the return value is not a BaseType" do it "raises an error " do err = assert_raises(RuntimeError) { - relay_schema.resolve_type(nil, :test_error, GraphQL::Query::NullContext) + relay_schema.resolve_type(nil, :test_error, GraphQL::Query::NullContext.instance) } assert_includes err.message, "not_a_type (Symbol)" end