Skip to content

Commit 2e26791

Browse files
authored
Merge pull request #4670 from rmosolgo/inmplements-definition-methods
Fix definition_methods with implements
2 parents be52b28 + c029b47 commit 2e26791

File tree

3 files changed

+69
-10
lines changed

3 files changed

+69
-10
lines changed

guides/type_definitions/interfaces.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ end
177177

178178
The type definition DSL uses this mechanism, too, so you can override those methods here also.
179179

180+
Note: Under the hood, `definition_methods` causes a module to be `extend`ed by the Inteface. Any calls to `extend` or `implement` may override methods from `definition_methods`.
181+
180182
### Resolve Type
181183

182184
When a field's return type is an interface, GraphQL has to figure out what _specific_ object type to use for the return value. In the example above, each `customer` must be categorized as an `Individual` or `Company`. You can do this by:

lib/graphql/schema/interface.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ module DefinitionMethods
2020
# - Added as class methods to this interface
2121
# - Added as class methods to all child interfaces
2222
def definition_methods(&block)
23+
# Use an instance variable to tell whether it's been included previously or not;
24+
# You can't use constant detection because constants are brought into scope
25+
# by `include`, which has already happened at this point.
26+
if !defined?(@_definition_methods)
27+
defn_methods_module = Module.new
28+
@_definition_methods = defn_methods_module
29+
const_set(:DefinitionMethods, defn_methods_module)
30+
extend(self::DefinitionMethods)
31+
end
2332
self::DefinitionMethods.module_eval(&block)
2433
end
2534

@@ -47,20 +56,11 @@ def included(child_class)
4756

4857
child_class.type_membership_class(self.type_membership_class)
4958
child_class.ancestors.reverse_each do |ancestor|
50-
if ancestor.const_defined?(:DefinitionMethods)
59+
if ancestor.const_defined?(:DefinitionMethods) && ancestor != child_class
5160
child_class.extend(ancestor::DefinitionMethods)
5261
end
5362
end
5463

55-
# Use an instance variable to tell whether it's been included previously or not;
56-
# You can't use constant detection because constants are brought into scope
57-
# by `include`, which has already happened at this point.
58-
if !child_class.instance_variable_defined?(:@_definition_methods)
59-
defn_methods_module = Module.new
60-
child_class.instance_variable_set(:@_definition_methods, defn_methods_module)
61-
child_class.const_set(:DefinitionMethods, defn_methods_module)
62-
child_class.extend(child_class::DefinitionMethods)
63-
end
6464
child_class.introspection(introspection)
6565
child_class.description(description)
6666
# If interfaces are mixed into each other, only define this class once

spec/graphql/schema/interface_spec.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ module InterfaceB
124124

125125
module InterfaceC
126126
include GraphQL::Schema::Interface
127+
definition_methods do
128+
end
127129
end
128130

129131
module InterfaceD
@@ -603,6 +605,61 @@ def nil_fallback
603605
}
604606
assert_equal expected_result, result["data"]
605607
end
608+
609+
describe "in definition_methods when implementing another interface" do
610+
class InterfaceInheritanceSchema < GraphQL::Schema
611+
module Node
612+
include GraphQL::Schema::Interface
613+
definition_methods do
614+
def resolve_type(obj, ctx)
615+
raise "This should never be called -- it's overriden"
616+
end
617+
end
618+
end
619+
module Pet
620+
include GraphQL::Schema::Interface
621+
implements Node
622+
623+
definition_methods do
624+
def resolve_type(obj, ctx)
625+
if obj[:name] == "Fifi"
626+
Dog
627+
else
628+
Cat
629+
end
630+
end
631+
end
632+
end
633+
class Cat < GraphQL::Schema::Object
634+
implements Pet
635+
end
636+
637+
class Dog < GraphQL::Schema::Object
638+
implements Pet
639+
end
640+
641+
class Query < GraphQL::Schema::Object
642+
field :pet, Pet do
643+
argument :name, String
644+
end
645+
646+
def pet(name:)
647+
{ name: name }
648+
end
649+
end
650+
651+
query(Query)
652+
orphan_types(Cat, Dog)
653+
end
654+
655+
it "calls the local definition, not the inherited one" do
656+
res = InterfaceInheritanceSchema.execute("{ pet(name: \"Fifi\") { __typename } }")
657+
assert_equal "Dog", res["data"]["pet"]["__typename"]
658+
659+
res = InterfaceInheritanceSchema.execute("{ pet(name: \"Pepper\") { __typename } }")
660+
assert_equal "Cat", res["data"]["pet"]["__typename"]
661+
end
662+
end
606663
end
607664
end
608665
end

0 commit comments

Comments
 (0)