Skip to content

Commit f97d3fb

Browse files
committed
Support super on object fields
1 parent ec03ea5 commit f97d3fb

File tree

4 files changed

+52
-17
lines changed

4 files changed

+52
-17
lines changed

lib/graphql/schema/field.rb

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -232,39 +232,49 @@ def resolve_field(obj, args, ctx)
232232
inner_obj = obj && obj.object
233233
prev_resolve.call(inner_obj, args, ctx)
234234
else
235-
resolve_field_dynamic(obj, args, ctx)
235+
public_send_field(obj, @method_sym, args, ctx)
236236
end
237237
end
238238

239-
private
240-
241-
# Try a few ways to resolve the field
242-
# @api private
243-
def resolve_field_dynamic(obj, args, ctx)
244-
if obj.respond_to?(@method_sym)
245-
public_send_field(obj, @method_sym, args, ctx)
246-
elsif obj.object.is_a?(Hash)
239+
# Find a way to resolve this field, checking:
240+
#
241+
# - Hash keys, if the wrapped object is a hash;
242+
# - A method on the wrapped object;
243+
# - Or, raise not implemented.
244+
#
245+
# This can be overridden by defining a method on the object type.
246+
# @param obj [GraphQL::Schema::Object]
247+
# @param ruby_kwargs [Hash<Symbol => Object>]
248+
# @param ctx [GraphQL::Query::Context]
249+
def resolve_field_method(obj, ruby_kwargs, ctx)
250+
if obj.object.is_a?(Hash)
247251
inner_object = obj.object
248252
if inner_object.key?(@method_sym)
249253
inner_object[@method_sym]
250254
else
251255
inner_object[@method_str]
252256
end
253257
elsif obj.object.respond_to?(@method_sym)
254-
public_send_field(obj.object, @method_sym, args, ctx)
258+
if ruby_kwargs.any?
259+
obj.object.public_send(@method_sym, **ruby_kwargs)
260+
else
261+
obj.object.public_send(@method_sym)
262+
end
255263
else
256264
raise <<-ERR
257-
Failed to implement #{ctx.irep_node.owner_type.name}.#{ctx.field.name}, tried:
265+
Failed to implement #{ctx.irep_node.owner_type.name}.#{ctx.field.name}, tried:
258266
259-
- `#{obj.class}##{@method_sym}`, which did not exist
260-
- `#{obj.object.class}##{@method_sym}`, which did not exist
261-
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
267+
- `#{obj.class}##{@method_sym}`, which did not exist
268+
- `#{obj.object.class}##{@method_sym}`, which did not exist
269+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
262270
263-
To implement this field, define one of the methods above (and check for typos)
264-
ERR
271+
To implement this field, define one of the methods above (and check for typos)
272+
ERR
265273
end
266274
end
267275

276+
private
277+
268278
NO_ARGS = {}.freeze
269279

270280
def public_send_field(obj, method_name, graphql_args, field_ctx)

lib/graphql/schema/member/has_fields.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,21 @@ def fields
2525
all_fields
2626
end
2727

28-
# Register this field with the class, overriding a previous one if needed
28+
# Register this field with the class, overriding a previous one if needed.
29+
# Also, add a parent method for resolving this field.
2930
# @param field_defn [GraphQL::Schema::Field]
3031
# @return [void]
3132
def add_field(field_defn)
3233
own_fields[field_defn.name] = field_defn
34+
if defined?(self::DefaultResolve)
35+
field_key = field_defn.name.inspect
36+
self::DefaultResolve.module_eval <<-RUBY, __FILE__, __LINE__ + 1
37+
def #{field_defn.method_sym}(**args)
38+
field_inst = self.class.fields[#{field_key}] || raise(%|Failed to find field #{field_key} for \#{self.class} among \#{self.class.fields.keys}|)
39+
field_inst.resolve_field_method(self, args, context)
40+
end
41+
RUBY
42+
end
3343
nil
3444
end
3545

lib/graphql/schema/object.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ def to_graphql
7171

7272
obj_type
7373
end
74+
75+
# When a subclass is created, give it a DefaultResolve module.
76+
# That module has the base methods for executing fields.
77+
# That way, later method definitions can call `super` to get the default behavior.
78+
def inherited(child_class)
79+
default_resolve_module = Module.new
80+
child_class.const_set(:DefaultResolve, default_resolve_module)
81+
child_class.include(default_resolve_module)
82+
super
83+
end
7484
end
7585
end
7686
end

spec/support/jazz.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ def self.resolve_type(object, context)
302302

303303
class HashKeyTest < BaseObject
304304
field :falsey, Boolean, null: false
305+
306+
def falsey
307+
# test that super can be used in objects
308+
super
309+
end
305310
end
306311

307312
# Another new-style definition, with method overrides

0 commit comments

Comments
 (0)