graphql 0.16.1 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/query_complexity.rb +20 -13
- data/lib/graphql/analysis/query_depth.rb +2 -2
- data/lib/graphql/argument.rb +7 -2
- data/lib/graphql/base_type.rb +2 -1
- data/lib/graphql/boolean_type.rb +1 -0
- data/lib/graphql/define/assign_object_field.rb +19 -6
- data/lib/graphql/define/instance_definable.rb +44 -3
- data/lib/graphql/directive.rb +2 -6
- data/lib/graphql/enum_type.rb +11 -14
- data/lib/graphql/field.rb +47 -12
- data/lib/graphql/field/resolve.rb +57 -0
- data/lib/graphql/float_type.rb +2 -0
- data/lib/graphql/id_type.rb +2 -0
- data/lib/graphql/input_object_type.rb +5 -1
- data/lib/graphql/int_type.rb +2 -0
- data/lib/graphql/interface_type.rb +1 -1
- data/lib/graphql/internal_representation/node.rb +18 -29
- data/lib/graphql/internal_representation/rewrite.rb +7 -4
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/input_value_type.rb +1 -1
- data/lib/graphql/language/parser.rb +161 -136
- data/lib/graphql/language/parser.y +9 -1
- data/lib/graphql/object_type.rb +2 -2
- data/lib/graphql/query.rb +4 -4
- data/lib/graphql/query/directive_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/execution_context.rb +3 -2
- data/lib/graphql/query/serial_execution/field_resolution.rb +2 -5
- data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
- data/lib/graphql/query/serial_execution/value_resolution.rb +2 -2
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema/timeout_middleware.rb +1 -0
- data/lib/graphql/string_type.rb +2 -0
- data/lib/graphql/union_type.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -3
- data/spec/graphql/analysis/query_complexity_spec.rb +41 -5
- data/spec/graphql/define/instance_definable_spec.rb +2 -2
- data/spec/graphql/field_spec.rb +18 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +7 -6
- data/spec/graphql/language/parser_spec.rb +5 -0
- data/spec/graphql/query/serial_execution/execution_context_spec.rb +2 -1
- data/spec/graphql/schema/timeout_middleware_spec.rb +29 -28
- data/spec/support/dairy_app.rb +12 -10
- metadata +3 -3
- data/lib/graphql/internal_representation/definition.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24203b4225051e3273a336d37502cdddd29c3c21
|
4
|
+
data.tar.gz: d3c4de442aa838dfde6db055cd4da9e397a89578
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d134105d845e9b416078a504048f9ba263943fba5741a4de5d9f3cf229a193614858794b9d6ef5b857b84cb14ea3f1bb14d1b9ffc6beb4ee9e6fff2d9c9fac1c
|
7
|
+
data.tar.gz: e7107d02800a97cf95ed00dc54463056ba7dd0bc38623f5750deb4cc6a0ea736074a84ee77da81859c7c548ffe75b117a9c130e7a98fc13a6339dfdc8a8ba1ea
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
38
38
|
else
|
39
39
|
0
|
40
40
|
end
|
41
|
-
memo[:complexities_on_type].last.merge(irep_node.
|
41
|
+
memo[:complexities_on_type].last.merge(irep_node.definitions, own_complexity)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
memo
|
@@ -56,17 +56,24 @@ module GraphQL
|
|
56
56
|
# Get a complexity value for a field,
|
57
57
|
# by getting the number or calling its proc
|
58
58
|
def get_complexity(irep_node, query, child_complexity)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
59
|
+
max_possible_complexity = 0
|
60
|
+
irep_node.definitions.each do |type_defn, field_defn|
|
61
|
+
defined_complexity = field_defn.complexity
|
62
|
+
type_cpx = case defined_complexity
|
63
|
+
when Proc
|
64
|
+
args = query.arguments_for(irep_node, field_defn)
|
65
|
+
defined_complexity.call(query.context, args, child_complexity)
|
66
|
+
when Numeric
|
67
|
+
defined_complexity + (child_complexity || 0)
|
68
|
+
else
|
69
|
+
raise("Invalid complexity: #{defined_complexity.inspect} on #{field_defn.name}")
|
70
|
+
end
|
71
|
+
|
72
|
+
if type_cpx > max_possible_complexity
|
73
|
+
max_possible_complexity = type_cpx
|
74
|
+
end
|
69
75
|
end
|
76
|
+
max_possible_complexity
|
70
77
|
end
|
71
78
|
|
72
79
|
# Selections on an object may apply differently depending on what is _actually_ returned by the resolve function.
|
@@ -97,8 +104,8 @@ module GraphQL
|
|
97
104
|
end
|
98
105
|
|
99
106
|
# Store the complexity score for each of `types`
|
100
|
-
def merge(
|
101
|
-
|
107
|
+
def merge(definitions, complexity)
|
108
|
+
definitions.each { |type_defn, field_defn| @types[type_defn] += complexity }
|
102
109
|
end
|
103
110
|
|
104
111
|
private
|
@@ -24,7 +24,7 @@ module GraphQL
|
|
24
24
|
def call(memo, visit_type, irep_node)
|
25
25
|
if irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
|
26
26
|
if visit_type == :enter
|
27
|
-
if GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.
|
27
|
+
if GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.definition_name)
|
28
28
|
# Don't validate introspection fields
|
29
29
|
memo[:skip_current_scope] = true
|
30
30
|
elsif memo[:skip_current_scope]
|
@@ -33,7 +33,7 @@ module GraphQL
|
|
33
33
|
memo[:current_depth] += 1
|
34
34
|
end
|
35
35
|
else
|
36
|
-
if GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.
|
36
|
+
if GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.definition_name)
|
37
37
|
memo[:skip_current_scope] = false
|
38
38
|
elsif GraphQL::Query::DirectiveResolution.include_node?(irep_node, memo[:query])
|
39
39
|
if memo[:max_depth] < memo[:current_depth]
|
data/lib/graphql/argument.rb
CHANGED
@@ -17,9 +17,14 @@ module GraphQL
|
|
17
17
|
class Argument
|
18
18
|
include GraphQL::Define::InstanceDefinable
|
19
19
|
accepts_definitions :name, :type, :description, :default_value
|
20
|
-
|
20
|
+
lazy_defined_attr_accessor :type, :description, :default_value
|
21
21
|
|
22
22
|
# @return [String] The name of this argument on its {GraphQL::Field} or {GraphQL::InputObjectType}
|
23
|
-
|
23
|
+
def name
|
24
|
+
ensure_defined
|
25
|
+
@name
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_writer :name
|
24
29
|
end
|
25
30
|
end
|
data/lib/graphql/base_type.rb
CHANGED
@@ -3,8 +3,8 @@ module GraphQL
|
|
3
3
|
class BaseType
|
4
4
|
include GraphQL::Define::NonNullWithBang
|
5
5
|
include GraphQL::Define::InstanceDefinable
|
6
|
-
attr_accessor :name, :description
|
7
6
|
accepts_definitions :name, :description
|
7
|
+
lazy_defined_attr_accessor :name, :description
|
8
8
|
|
9
9
|
# @param other [GraphQL::BaseType] compare to this object
|
10
10
|
# @return [Boolean] are these types equivalent? (incl. non-null, list)
|
@@ -53,6 +53,7 @@ module GraphQL
|
|
53
53
|
# @param ctx [GraphQL::Query::Context]
|
54
54
|
# @return [GraphQL::ObjectType] the type which should expose `object`
|
55
55
|
def resolve_type(object, ctx)
|
56
|
+
ensure_defined
|
56
57
|
instance_exec(object, ctx, &(@resolve_type_proc || DEFAULT_RESOLVE_TYPE))
|
57
58
|
end
|
58
59
|
|
data/lib/graphql/boolean_type.rb
CHANGED
@@ -3,6 +3,7 @@ GraphQL::BOOLEAN_TYPE = GraphQL::ScalarType.define do
|
|
3
3
|
ALLOWED_INPUTS = [true, false]
|
4
4
|
|
5
5
|
name "Boolean"
|
6
|
+
description "Represents `true` or `false` values."
|
6
7
|
|
7
8
|
coerce_input -> (value) { ALLOWED_INPUTS.include?(value) ? value : nil }
|
8
9
|
coerce_result -> (value) { !!value }
|
@@ -2,15 +2,28 @@ module GraphQL
|
|
2
2
|
module Define
|
3
3
|
# Turn field configs into a {GraphQL::Field} and attach it to a {GraphQL::ObjectType} or {GraphQL::InterfaceType}
|
4
4
|
module AssignObjectField
|
5
|
-
def self.call(fields_type, name,
|
6
|
-
if
|
5
|
+
def self.call(fields_type, name, type_or_field = nil, desc = nil, field: nil, deprecation_reason: nil, property: nil, complexity: nil, hash_key: nil, &block)
|
6
|
+
if type_or_field.is_a?(GraphQL::Field)
|
7
|
+
field = type_or_field
|
8
|
+
elsif block_given?
|
7
9
|
field = GraphQL::Field.define(&block)
|
8
|
-
|
9
|
-
field
|
10
|
+
elsif field.nil?
|
11
|
+
field = GraphQL::Field.new
|
10
12
|
end
|
11
|
-
|
13
|
+
|
14
|
+
if !type_or_field.nil? && !type_or_field.is_a?(GraphQL::Field)
|
15
|
+
field.type = type_or_field
|
16
|
+
end
|
17
|
+
|
12
18
|
desc && field.description = desc
|
13
|
-
|
19
|
+
|
20
|
+
# If the field's resolve proc was defined in the config block,
|
21
|
+
# don't override it with `property` or `hash_key`
|
22
|
+
if field.resolve_proc.is_a?(GraphQL::Field::Resolve::BuiltInResolve)
|
23
|
+
property && field.property = property
|
24
|
+
hash_key && field.hash_key = hash_key
|
25
|
+
end
|
26
|
+
|
14
27
|
complexity && field.complexity = complexity
|
15
28
|
deprecation_reason && field.deprecation_reason = deprecation_reason
|
16
29
|
field.name ||= name.to_s
|
@@ -44,12 +44,36 @@ module GraphQL
|
|
44
44
|
base.extend(ClassMethods)
|
45
45
|
end
|
46
46
|
|
47
|
+
# Set the definition block for this instance.
|
48
|
+
# It can be run later with {#ensure_defined}
|
49
|
+
def definition_proc=(defn_block)
|
50
|
+
@definition_proc = defn_block
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Run the definition block if it hasn't been run yet.
|
56
|
+
# This can only be run once: the block is deleted after it's used.
|
57
|
+
# You have to call this before using any value which could
|
58
|
+
# come from the definition block.
|
59
|
+
# @return [void]
|
60
|
+
def ensure_defined
|
61
|
+
if @definition_proc
|
62
|
+
defn_proc = @definition_proc
|
63
|
+
@definition_proc = nil
|
64
|
+
proxy = DefinedObjectProxy.new(self, self.class.dictionary)
|
65
|
+
proxy.instance_eval(&defn_proc)
|
66
|
+
end
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
47
70
|
module ClassMethods
|
48
|
-
#
|
71
|
+
# Prepare the defintion for an instance of this class using its {.definitions}.
|
72
|
+
# Note that the block is not called right away -- instead, it's deferred until
|
73
|
+
# one of the defined fields is needed.
|
49
74
|
def define(&block)
|
50
75
|
instance = self.new
|
51
|
-
|
52
|
-
block && proxy.instance_eval(&block)
|
76
|
+
instance.definition_proc = block
|
53
77
|
instance
|
54
78
|
end
|
55
79
|
|
@@ -60,6 +84,23 @@ module GraphQL
|
|
60
84
|
@own_dictionary = own_dictionary.merge(AssignmentDictionary.create(*accepts))
|
61
85
|
end
|
62
86
|
|
87
|
+
# Define a reader and writer for each of `attr_names` which
|
88
|
+
# ensures that the definition block was called before accessing it.
|
89
|
+
def lazy_defined_attr_accessor(*attr_names)
|
90
|
+
attr_names.each do |attr_name|
|
91
|
+
ivar_name = :"@#{attr_name}"
|
92
|
+
define_method(attr_name) do
|
93
|
+
ensure_defined
|
94
|
+
instance_variable_get(ivar_name)
|
95
|
+
end
|
96
|
+
|
97
|
+
define_method("#{attr_name}=") do |new_value|
|
98
|
+
ensure_defined
|
99
|
+
instance_variable_set(ivar_name, new_value)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
63
104
|
# @return [Hash] combined definitions for self and ancestors
|
64
105
|
def dictionary
|
65
106
|
if superclass.respond_to?(:dictionary)
|
data/lib/graphql/directive.rb
CHANGED
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
include GraphQL::Define::InstanceDefinable
|
4
4
|
accepts_definitions :locations, :name, :description, :include_proc, argument: GraphQL::Define::AssignArgument
|
5
5
|
|
6
|
-
|
6
|
+
lazy_defined_attr_accessor :locations, :arguments, :name, :description, :include_proc
|
7
7
|
|
8
8
|
LOCATIONS = [
|
9
9
|
QUERY = :QUERY,
|
@@ -20,11 +20,7 @@ module GraphQL
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def include?(arguments)
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def include_proc=(include_proc)
|
27
|
-
@include_proc = include_proc
|
23
|
+
include_proc.call(arguments)
|
28
24
|
end
|
29
25
|
|
30
26
|
def to_s
|
data/lib/graphql/enum_type.rb
CHANGED
@@ -19,10 +19,10 @@ module GraphQL
|
|
19
19
|
@values_by_value = {}
|
20
20
|
end
|
21
21
|
|
22
|
-
def values=(
|
22
|
+
def values=(new_values)
|
23
23
|
@values_by_name = {}
|
24
24
|
@values_by_value = {}
|
25
|
-
|
25
|
+
new_values.each { |enum_value| add_value(enum_value) }
|
26
26
|
end
|
27
27
|
|
28
28
|
def add_value(enum_value)
|
@@ -31,24 +31,16 @@ module GraphQL
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def values
|
34
|
+
ensure_defined
|
34
35
|
@values_by_name
|
35
36
|
end
|
36
37
|
|
37
|
-
# Define a value within this enum
|
38
|
-
# @deprecated use {.define} API instead
|
39
|
-
# @param name [String] the string representation of this value
|
40
|
-
# @param description [String]
|
41
|
-
# @param deprecation_reason [String] if provided, `deprecated?` will be true
|
42
|
-
# @param value [Object] the underlying value for this enum value
|
43
|
-
def value(name, description=nil, deprecation_reason: nil, value: name)
|
44
|
-
values[name] = EnumValue.new(name: name, description: description, deprecation_reason: deprecation_reason, value: value)
|
45
|
-
end
|
46
|
-
|
47
38
|
def kind
|
48
39
|
GraphQL::TypeKinds::ENUM
|
49
40
|
end
|
50
41
|
|
51
42
|
def validate_non_null_input(value_name)
|
43
|
+
ensure_defined
|
52
44
|
result = GraphQL::Query::InputValidationResult.new
|
53
45
|
|
54
46
|
if !@values_by_name.key?(value_name)
|
@@ -67,11 +59,16 @@ module GraphQL
|
|
67
59
|
# @param value_name [String] the string representation of this enum value
|
68
60
|
# @return [Object] the underlying value for this enum value
|
69
61
|
def coerce_non_null_input(value_name)
|
70
|
-
|
71
|
-
@values_by_name.
|
62
|
+
ensure_defined
|
63
|
+
if @values_by_name.key?(value_name)
|
64
|
+
@values_by_name.fetch(value_name).value
|
65
|
+
else
|
66
|
+
nil
|
67
|
+
end
|
72
68
|
end
|
73
69
|
|
74
70
|
def coerce_result(value)
|
71
|
+
ensure_defined
|
75
72
|
@values_by_value.fetch(value).name
|
76
73
|
end
|
77
74
|
|
data/lib/graphql/field.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "graphql/field/resolve"
|
2
|
+
|
1
3
|
module GraphQL
|
2
4
|
# {Field}s belong to {ObjectType}s and {InterfaceType}s.
|
3
5
|
#
|
@@ -57,20 +59,35 @@ module GraphQL
|
|
57
59
|
#
|
58
60
|
class Field
|
59
61
|
include GraphQL::Define::InstanceDefinable
|
60
|
-
accepts_definitions :name, :description, :resolve, :type, :property, :deprecation_reason, :complexity, argument: GraphQL::Define::AssignArgument
|
62
|
+
accepts_definitions :name, :description, :resolve, :type, :property, :deprecation_reason, :complexity, :hash_key, argument: GraphQL::Define::AssignArgument
|
61
63
|
|
62
|
-
|
64
|
+
lazy_defined_attr_accessor :deprecation_reason, :description, :property, :hash_key
|
63
65
|
|
64
66
|
attr_reader :resolve_proc
|
65
67
|
|
66
68
|
# @return [String] The name of this field on its {GraphQL::ObjectType} (or {GraphQL::InterfaceType})
|
67
|
-
|
69
|
+
def name
|
70
|
+
ensure_defined
|
71
|
+
@name
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_writer :name
|
68
75
|
|
69
76
|
# @return [Hash<String => GraphQL::Argument>] Map String argument names to their {GraphQL::Argument} implementations
|
70
|
-
|
77
|
+
def arguments
|
78
|
+
ensure_defined
|
79
|
+
@arguments
|
80
|
+
end
|
81
|
+
|
82
|
+
attr_writer :arguments
|
71
83
|
|
72
84
|
# @return [Numeric, Proc] The complexity for this field (default: 1), as a constant or a proc like `-> (query_ctx, args, child_complexity) { } # Numeric`
|
73
|
-
|
85
|
+
def complexity
|
86
|
+
ensure_defined
|
87
|
+
@complexity
|
88
|
+
end
|
89
|
+
|
90
|
+
attr_writer :complexity
|
74
91
|
|
75
92
|
def initialize
|
76
93
|
@complexity = 1
|
@@ -86,21 +103,27 @@ module GraphQL
|
|
86
103
|
# @param arguments [Hash] Arguments declared in the query
|
87
104
|
# @param context [GraphQL::Query::Context]
|
88
105
|
def resolve(object, arguments, context)
|
89
|
-
|
106
|
+
ensure_defined
|
107
|
+
resolve_proc.call(object, arguments, context)
|
90
108
|
end
|
91
109
|
|
92
110
|
def resolve=(resolve_proc)
|
111
|
+
ensure_defined
|
93
112
|
@resolve_proc = resolve_proc || build_default_resolver
|
94
113
|
end
|
95
114
|
|
96
115
|
def type=(new_return_type)
|
116
|
+
ensure_defined
|
97
117
|
@clean_type = nil
|
98
118
|
@dirty_type = new_return_type
|
99
119
|
end
|
100
120
|
|
101
121
|
# Get the return type for this field.
|
102
122
|
def type
|
103
|
-
@clean_type ||=
|
123
|
+
@clean_type ||= begin
|
124
|
+
ensure_defined
|
125
|
+
GraphQL::BaseType.resolve_related_type(@dirty_type)
|
126
|
+
end
|
104
127
|
end
|
105
128
|
|
106
129
|
# You can only set a field's name _once_ -- this to prevent
|
@@ -108,13 +131,28 @@ module GraphQL
|
|
108
131
|
#
|
109
132
|
# This is important because {#name} may be used by {#resolve}.
|
110
133
|
def name=(new_name)
|
134
|
+
ensure_defined
|
111
135
|
if @name.nil?
|
112
136
|
@name = new_name
|
113
|
-
|
137
|
+
elsif @name != new_name
|
114
138
|
raise("Can't rename an already-named field. (Tried to rename \"#{@name}\" to \"#{new_name}\".) If you're passing a field with the `field:` argument, make sure it's an unused instance of GraphQL::Field.")
|
115
139
|
end
|
116
140
|
end
|
117
141
|
|
142
|
+
# @param new_property [Symbol] A method to call to resolve this field. Overrides the existing resolve proc.
|
143
|
+
def property=(new_property)
|
144
|
+
ensure_defined
|
145
|
+
@property = new_property
|
146
|
+
self.resolve = nil # reset resolve proc
|
147
|
+
end
|
148
|
+
|
149
|
+
# @param new_hash_key [Symbol] A key to access with `#[key]` to resolve this field. Overrides the existing resolve proc.
|
150
|
+
def hash_key=(new_hash_key)
|
151
|
+
ensure_defined
|
152
|
+
@hash_key = new_hash_key
|
153
|
+
self.resolve = nil # reset resolve proc
|
154
|
+
end
|
155
|
+
|
118
156
|
def to_s
|
119
157
|
"<Field: #{name || "not-named"}>"
|
120
158
|
end
|
@@ -122,10 +160,7 @@ module GraphQL
|
|
122
160
|
private
|
123
161
|
|
124
162
|
def build_default_resolver
|
125
|
-
|
126
|
-
resolve_method = self.property || self.name
|
127
|
-
obj.public_send(resolve_method)
|
128
|
-
end
|
163
|
+
GraphQL::Field::Resolve.create_proc(self)
|
129
164
|
end
|
130
165
|
end
|
131
166
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module GraphQL
|
2
|
+
class Field
|
3
|
+
# Create resolve procs ahead of time based on a {GraphQL::Field}'s `name`, `property`, and `hash_key` configuration.
|
4
|
+
module Resolve
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# @param [GraphQL::Field] A field that needs a resolve proc
|
8
|
+
# @return [Proc] A resolver for this field, based on its config
|
9
|
+
def create_proc(field)
|
10
|
+
if field.property
|
11
|
+
MethodResolve.new(field.property.to_sym)
|
12
|
+
elsif !field.hash_key.nil?
|
13
|
+
HashKeyResolve.new(field.hash_key)
|
14
|
+
else
|
15
|
+
NameResolve.new(field)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class BuiltInResolve
|
20
|
+
end
|
21
|
+
|
22
|
+
# Resolve the field by `public_send`ing `@method_name`
|
23
|
+
class MethodResolve < BuiltInResolve
|
24
|
+
def initialize(method_name)
|
25
|
+
@method_name = method_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(obj, args, ctx)
|
29
|
+
obj.public_send(@method_name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Resolve the field by looking up `@hash_key` with `#[]`
|
34
|
+
class HashKeyResolve < BuiltInResolve
|
35
|
+
def initialize(hash_key)
|
36
|
+
@hash_key = hash_key
|
37
|
+
end
|
38
|
+
|
39
|
+
def call(obj, args, ctx)
|
40
|
+
obj[@hash_key]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Call the field's name at query-time since
|
45
|
+
# it might have changed
|
46
|
+
class NameResolve < BuiltInResolve
|
47
|
+
def initialize(field)
|
48
|
+
@field = field
|
49
|
+
end
|
50
|
+
|
51
|
+
def call(obj, args, ctx)
|
52
|
+
obj.public_send(@field.name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|