graphql 2.0.16 → 2.0.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/ast/visitor.rb +42 -35
  3. data/lib/graphql/analysis/ast.rb +2 -2
  4. data/lib/graphql/backtrace/tracer.rb +1 -1
  5. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  6. data/lib/graphql/execution/interpreter/runtime.rb +106 -88
  7. data/lib/graphql/execution/interpreter.rb +14 -9
  8. data/lib/graphql/execution/lazy.rb +6 -12
  9. data/lib/graphql/execution/multiplex.rb +2 -1
  10. data/lib/graphql/graphql_ext.bundle +0 -0
  11. data/lib/graphql/introspection/directive_type.rb +2 -2
  12. data/lib/graphql/introspection/field_type.rb +1 -1
  13. data/lib/graphql/introspection/schema_type.rb +2 -2
  14. data/lib/graphql/introspection/type_type.rb +5 -5
  15. data/lib/graphql/language/lexer.rb +216 -1505
  16. data/lib/graphql/language/lexer.ri +744 -0
  17. data/lib/graphql/language/nodes.rb +39 -31
  18. data/lib/graphql/language/parser.rb +9 -9
  19. data/lib/graphql/language/parser.y +9 -9
  20. data/lib/graphql/language/visitor.rb +191 -83
  21. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  22. data/lib/graphql/query/context.rb +45 -11
  23. data/lib/graphql/query.rb +15 -2
  24. data/lib/graphql/schema/argument.rb +0 -4
  25. data/lib/graphql/schema/directive.rb +12 -2
  26. data/lib/graphql/schema/enum.rb +24 -17
  27. data/lib/graphql/schema/enum_value.rb +5 -3
  28. data/lib/graphql/schema/field.rb +53 -45
  29. data/lib/graphql/schema/interface.rb +0 -10
  30. data/lib/graphql/schema/late_bound_type.rb +2 -0
  31. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -14
  32. data/lib/graphql/schema/member/has_arguments.rb +104 -57
  33. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  34. data/lib/graphql/schema/member/has_fields.rb +14 -2
  35. data/lib/graphql/schema/member/has_interfaces.rb +49 -8
  36. data/lib/graphql/schema/member/has_validators.rb +31 -5
  37. data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
  38. data/lib/graphql/schema/object.rb +2 -4
  39. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  40. data/lib/graphql/schema/timeout.rb +23 -27
  41. data/lib/graphql/schema/warden.rb +26 -4
  42. data/lib/graphql/schema.rb +37 -19
  43. data/lib/graphql/static_validation/literal_validator.rb +15 -1
  44. data/lib/graphql/static_validation/validator.rb +1 -1
  45. data/lib/graphql/subscriptions/event.rb +2 -7
  46. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  47. data/lib/graphql/tracing/appoptics_trace.rb +231 -0
  48. data/lib/graphql/tracing/appsignal_trace.rb +66 -0
  49. data/lib/graphql/tracing/data_dog_trace.rb +148 -0
  50. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  51. data/lib/graphql/tracing/notifications_trace.rb +41 -0
  52. data/lib/graphql/tracing/platform_trace.rb +107 -0
  53. data/lib/graphql/tracing/platform_tracing.rb +15 -3
  54. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  55. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  56. data/lib/graphql/tracing/scout_trace.rb +72 -0
  57. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  58. data/lib/graphql/tracing.rb +136 -39
  59. data/lib/graphql/type_kinds.rb +6 -3
  60. data/lib/graphql/types/relay/connection_behaviors.rb +0 -4
  61. data/lib/graphql/types/relay/edge_behaviors.rb +0 -4
  62. data/lib/graphql/types/string.rb +1 -1
  63. data/lib/graphql/version.rb +1 -1
  64. data/lib/graphql.rb +7 -8
  65. metadata +15 -4
  66. data/lib/graphql/language/lexer.rl +0 -280
@@ -141,18 +141,26 @@ module GraphQL
141
141
  .gsub(/([a-z])([A-Z])/,'\1_\2') # insert underscores
142
142
  .downcase # remove caps
143
143
 
144
- child_class.module_eval <<-RUBY
144
+ child_class.module_eval <<-RUBY, __FILE__, __LINE__
145
145
  def visit_method
146
146
  :on_#{name_underscored}
147
147
  end
148
148
 
149
149
  class << self
150
150
  attr_accessor :children_method_name
151
+
152
+ def visit_method
153
+ :on_#{name_underscored}
154
+ end
151
155
  end
152
156
  self.children_method_name = :#{name_underscored}s
153
157
  RUBY
154
158
  end
155
159
 
160
+ def children_of_type
161
+ @children_methods
162
+ end
163
+
156
164
  private
157
165
 
158
166
  # Name accessors which return lists of nodes,
@@ -300,7 +308,7 @@ module GraphQL
300
308
  # @return [String] the key for this argument
301
309
 
302
310
  # @!attribute value
303
- # @return [String, Float, Integer, Boolean, Array, InputObject] The value passed for this key
311
+ # @return [String, Float, Integer, Boolean, Array, InputObject, VariableIdentifier] The value passed for this key
304
312
 
305
313
  def children
306
314
  @children ||= Array(value).flatten.select { |v| v.is_a?(AbstractNode) }
@@ -325,35 +333,6 @@ module GraphQL
325
333
  )
326
334
  end
327
335
 
328
- # This is the AST root for normal queries
329
- #
330
- # @example Deriving a document by parsing a string
331
- # document = GraphQL.parse(query_string)
332
- #
333
- # @example Creating a string from a document
334
- # document.to_query_string
335
- # # { ... }
336
- #
337
- # @example Creating a custom string from a document
338
- # class VariableScrubber < GraphQL::Language::Printer
339
- # def print_argument(arg)
340
- # "#{arg.name}: <HIDDEN>"
341
- # end
342
- # end
343
- #
344
- # document.to_query_string(printer: VariableScrubber.new)
345
- #
346
- class Document < AbstractNode
347
- scalar_methods false
348
- children_methods(definitions: nil)
349
- # @!attribute definitions
350
- # @return [Array<OperationDefinition, FragmentDefinition>] top-level GraphQL units: operations or fragments
351
-
352
- def slice_definition(name)
353
- GraphQL::Language::DefinitionSlice.slice(self, name)
354
- end
355
- end
356
-
357
336
  # An enum value. The string is available as {#name}.
358
337
  class Enum < NameOnlyNode
359
338
  end
@@ -526,6 +505,35 @@ module GraphQL
526
505
  self.children_method_name = :definitions
527
506
  end
528
507
 
508
+ # This is the AST root for normal queries
509
+ #
510
+ # @example Deriving a document by parsing a string
511
+ # document = GraphQL.parse(query_string)
512
+ #
513
+ # @example Creating a string from a document
514
+ # document.to_query_string
515
+ # # { ... }
516
+ #
517
+ # @example Creating a custom string from a document
518
+ # class VariableScrubber < GraphQL::Language::Printer
519
+ # def print_argument(arg)
520
+ # "#{arg.name}: <HIDDEN>"
521
+ # end
522
+ # end
523
+ #
524
+ # document.to_query_string(printer: VariableScrubber.new)
525
+ #
526
+ class Document < AbstractNode
527
+ scalar_methods false
528
+ children_methods(definitions: nil)
529
+ # @!attribute definitions
530
+ # @return [Array<OperationDefinition, FragmentDefinition>] top-level GraphQL units: operations or fragments
531
+
532
+ def slice_definition(name)
533
+ GraphQL::Language::DefinitionSlice.slice(self, name)
534
+ end
535
+ end
536
+
529
537
  # A type name, used for variable definitions
530
538
  class TypeName < NameOnlyNode
531
539
  end
@@ -16,22 +16,22 @@ module_eval(<<'...end parser.y/module_eval...', 'parser.y', 448)
16
16
 
17
17
  EMPTY_ARRAY = [].freeze
18
18
 
19
- def initialize(query_string, filename:, tracer: Tracing::NullTracer)
19
+ def initialize(query_string, filename:, trace: Tracing::NullTrace)
20
20
  raise GraphQL::ParseError.new("No query string was present", nil, nil, query_string) if query_string.nil?
21
21
  @query_string = query_string
22
22
  @filename = filename
23
- @tracer = tracer
23
+ @trace = trace
24
24
  @reused_next_token = [nil, nil]
25
25
  end
26
26
 
27
27
  def parse_document
28
28
  @document ||= begin
29
29
  # Break the string into tokens
30
- @tracer.trace("lex", {query_string: @query_string}) do
30
+ @trace.lex(query_string: @query_string) do
31
31
  @tokens ||= GraphQL.scan(@query_string)
32
32
  end
33
33
  # From the tokens, build an AST
34
- @tracer.trace("parse", {query_string: @query_string}) do
34
+ @trace.parse(query_string: @query_string) do
35
35
  if @tokens.empty?
36
36
  raise GraphQL::ParseError.new("Unexpected end of document", nil, nil, @query_string)
37
37
  else
@@ -44,17 +44,17 @@ end
44
44
  class << self
45
45
  attr_accessor :cache
46
46
 
47
- def parse(query_string, filename: nil, tracer: GraphQL::Tracing::NullTracer)
48
- new(query_string, filename: filename, tracer: tracer).parse_document
47
+ def parse(query_string, filename: nil, trace: GraphQL::Tracing::NullTrace)
48
+ new(query_string, filename: filename, trace: trace).parse_document
49
49
  end
50
50
 
51
- def parse_file(filename, tracer: GraphQL::Tracing::NullTracer)
51
+ def parse_file(filename, trace: GraphQL::Tracing::NullTrace)
52
52
  if cache
53
53
  cache.fetch(filename) do
54
- parse(File.read(filename), filename: filename, tracer: tracer)
54
+ parse(File.read(filename), filename: filename, trace: trace)
55
55
  end
56
56
  else
57
- parse(File.read(filename), filename: filename, tracer: tracer)
57
+ parse(File.read(filename), filename: filename, trace: trace)
58
58
  end
59
59
  end
60
60
  end
@@ -448,22 +448,22 @@ end
448
448
 
449
449
  EMPTY_ARRAY = [].freeze
450
450
 
451
- def initialize(query_string, filename:, tracer: Tracing::NullTracer)
451
+ def initialize(query_string, filename:, trace: Tracing::NullTrace)
452
452
  raise GraphQL::ParseError.new("No query string was present", nil, nil, query_string) if query_string.nil?
453
453
  @query_string = query_string
454
454
  @filename = filename
455
- @tracer = tracer
455
+ @trace = trace
456
456
  @reused_next_token = [nil, nil]
457
457
  end
458
458
 
459
459
  def parse_document
460
460
  @document ||= begin
461
461
  # Break the string into tokens
462
- @tracer.trace("lex", {query_string: @query_string}) do
462
+ @trace.lex(query_string: @query_string) do
463
463
  @tokens ||= GraphQL.scan(@query_string)
464
464
  end
465
465
  # From the tokens, build an AST
466
- @tracer.trace("parse", {query_string: @query_string}) do
466
+ @trace.parse(query_string: @query_string) do
467
467
  if @tokens.empty?
468
468
  raise GraphQL::ParseError.new("Unexpected end of document", nil, nil, @query_string)
469
469
  else
@@ -476,17 +476,17 @@ end
476
476
  class << self
477
477
  attr_accessor :cache
478
478
 
479
- def parse(query_string, filename: nil, tracer: GraphQL::Tracing::NullTracer)
480
- new(query_string, filename: filename, tracer: tracer).parse_document
479
+ def parse(query_string, filename: nil, trace: GraphQL::Tracing::NullTrace)
480
+ new(query_string, filename: filename, trace: trace).parse_document
481
481
  end
482
482
 
483
- def parse_file(filename, tracer: GraphQL::Tracing::NullTracer)
483
+ def parse_file(filename, trace: GraphQL::Tracing::NullTrace)
484
484
  if cache
485
485
  cache.fetch(filename) do
486
- parse(File.read(filename), filename: filename, tracer: tracer)
486
+ parse(File.read(filename), filename: filename, trace: trace)
487
487
  end
488
488
  else
489
- parse(File.read(filename), filename: filename, tracer: tracer)
489
+ parse(File.read(filename), filename: filename, trace: trace)
490
490
  end
491
491
  end
492
492
  end
@@ -65,7 +65,9 @@ module GraphQL
65
65
  # Visit `document` and all children, applying hooks as you go
66
66
  # @return [void]
67
67
  def visit
68
- result = on_node_with_modifications(@document, nil)
68
+ # `@document` may be any kind of node:
69
+ visit_method = :"#{@document.visit_method}_with_modifications"
70
+ result = public_send(visit_method, @document, nil)
69
71
  @result = if result.is_a?(Array)
70
72
  result.first
71
73
  else
@@ -74,104 +76,210 @@ module GraphQL
74
76
  end
75
77
  end
76
78
 
77
- # Call the user-defined handler for `node`.
78
- def visit_node(node, parent)
79
- public_send(node.visit_method, node, parent)
80
- end
79
+ # We don't use `alias` here because it breaks `super`
80
+ def self.make_visit_methods(ast_node_class)
81
+ node_method = ast_node_class.visit_method
82
+ children_of_type = ast_node_class.children_of_type
83
+ child_visit_method = :"#{node_method}_children"
81
84
 
82
- # The default implementation for visiting an AST node.
83
- # It doesn't _do_ anything, but it continues to visiting the node's children.
84
- # To customize this hook, override one of its make_visit_methodes (or the base method?)
85
- # in your subclasses.
86
- #
87
- # For compatibility, it calls hook procs, too.
88
- # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
89
- # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
90
- # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
91
- def on_abstract_node(node, parent)
92
- if node.equal?(DELETE_NODE)
93
- # This might be passed to `super(DELETE_NODE, ...)`
94
- # by a user hook, don't want to keep visiting in that case.
95
- nil
96
- else
97
- # Run hooks if there are any
98
- new_node = node
99
- no_hooks = !@visitors.key?(node.class)
100
- if no_hooks || begin_visit(new_node, parent)
101
- node.children.each do |child_node|
102
- new_child_and_node = on_node_with_modifications(child_node, new_node)
103
- # Reassign `node` in case the child hook makes a modification
104
- if new_child_and_node.is_a?(Array)
105
- new_node = new_child_and_node[1]
85
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
86
+ # The default implementation for visiting an AST node.
87
+ # It doesn't _do_ anything, but it continues to visiting the node's children.
88
+ # To customize this hook, override one of its make_visit_methods (or the base method?)
89
+ # in your subclasses.
90
+ #
91
+ # For compatibility, it calls hook procs, too.
92
+ # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
93
+ # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
94
+ # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
95
+ def #{node_method}(node, parent)
96
+ if node.equal?(DELETE_NODE)
97
+ # This might be passed to `super(DELETE_NODE, ...)`
98
+ # by a user hook, don't want to keep visiting in that case.
99
+ [node, parent]
100
+ else
101
+ # Run hooks if there are any
102
+ new_node = node
103
+ no_hooks = !@visitors.key?(node.class)
104
+ if no_hooks || begin_visit(new_node, parent)
105
+ #{
106
+ if method_defined?(child_visit_method)
107
+ "new_node = #{child_visit_method}(new_node)"
108
+ elsif children_of_type
109
+ children_of_type.map do |child_accessor, child_class|
110
+ "node.#{child_accessor}.each do |child_node|
111
+ new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
112
+ # Reassign `node` in case the child hook makes a modification
113
+ if new_child_and_node.is_a?(Array)
114
+ new_node = new_child_and_node[1]
115
+ end
116
+ end"
117
+ end.join("\n")
118
+ else
119
+ ""
120
+ end
121
+ }
122
+ end
123
+ end_visit(new_node, parent) unless no_hooks
124
+
125
+ if new_node.equal?(node)
126
+ [node, parent]
127
+ else
128
+ [new_node, parent]
106
129
  end
107
130
  end
108
131
  end
109
- end_visit(new_node, parent) unless no_hooks
110
132
 
111
- if new_node.equal?(node)
112
- nil
133
+ def #{node_method}_with_modifications(node, parent)
134
+ new_node_and_new_parent = #{node_method}(node, parent)
135
+ apply_modifications(node, parent, new_node_and_new_parent)
136
+ end
137
+ RUBY
138
+ end
139
+
140
+ def on_document_children(document_node)
141
+ new_node = document_node
142
+ document_node.children.each do |child_node|
143
+ visit_method = :"#{child_node.visit_method}_with_modifications"
144
+ new_child_and_node = public_send(visit_method, child_node, new_node)
145
+ # Reassign `node` in case the child hook makes a modification
146
+ if new_child_and_node.is_a?(Array)
147
+ new_node = new_child_and_node[1]
148
+ end
149
+ end
150
+ new_node
151
+ end
152
+
153
+ def on_field_children(new_node)
154
+ new_node.arguments.each do |arg_node| # rubocop:disable Development/ContextIsPassedCop
155
+ new_child_and_node = on_argument_with_modifications(arg_node, new_node)
156
+ # Reassign `node` in case the child hook makes a modification
157
+ if new_child_and_node.is_a?(Array)
158
+ new_node = new_child_and_node[1]
159
+ end
160
+ end
161
+ new_node = visit_directives(new_node)
162
+ new_node = visit_selections(new_node)
163
+ new_node
164
+ end
165
+
166
+ def visit_directives(new_node)
167
+ new_node.directives.each do |dir_node|
168
+ new_child_and_node = on_directive_with_modifications(dir_node, new_node)
169
+ # Reassign `node` in case the child hook makes a modification
170
+ if new_child_and_node.is_a?(Array)
171
+ new_node = new_child_and_node[1]
172
+ end
173
+ end
174
+ new_node
175
+ end
176
+
177
+ def visit_selections(new_node)
178
+ new_node.selections.each do |selection|
179
+ new_child_and_node = case selection
180
+ when GraphQL::Language::Nodes::Field
181
+ on_field_with_modifications(selection, new_node)
182
+ when GraphQL::Language::Nodes::InlineFragment
183
+ on_inline_fragment_with_modifications(selection, new_node)
184
+ when GraphQL::Language::Nodes::FragmentSpread
185
+ on_fragment_spread_with_modifications(selection, new_node)
113
186
  else
114
- [new_node, parent]
187
+ raise ArgumentError, "Invariant: unexpected field selection #{selection.class} (#{selection.inspect})"
188
+ end
189
+ # Reassign `node` in case the child hook makes a modification
190
+ if new_child_and_node.is_a?(Array)
191
+ new_node = new_child_and_node[1]
115
192
  end
116
193
  end
194
+ new_node
117
195
  end
118
196
 
119
- # We don't use `alias` here because it breaks `super`
120
- def self.make_visit_method(node_method)
121
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
122
- def #{node_method}(node, parent)
123
- child_mod = on_abstract_node(node, parent)
124
- # If visiting the children returned changes, continue passing those.
125
- child_mod || [node, parent]
197
+ def on_fragment_definition_children(new_node)
198
+ new_node = visit_directives(new_node)
199
+ new_node = visit_selections(new_node)
200
+ new_node
201
+ end
202
+
203
+ alias :on_inline_fragment_children :on_fragment_definition_children
204
+
205
+ def on_operation_definition_children(new_node)
206
+ new_node.variables.each do |arg_node|
207
+ new_child_and_node = on_variable_definition_with_modifications(arg_node, new_node)
208
+ # Reassign `node` in case the child hook makes a modification
209
+ if new_child_and_node.is_a?(Array)
210
+ new_node = new_child_and_node[1]
126
211
  end
127
- RUBY
212
+ end
213
+ new_node = visit_directives(new_node)
214
+ new_node = visit_selections(new_node)
215
+ new_node
216
+ end
217
+
218
+ def on_argument_children(new_node)
219
+ new_node.children.each do |value_node|
220
+ new_child_and_node = case value_node
221
+ when Language::Nodes::VariableIdentifier
222
+ on_variable_identifier_with_modifications(value_node, new_node)
223
+ when Language::Nodes::InputObject
224
+ on_input_object_with_modifications(value_node, new_node)
225
+ when Language::Nodes::Enum
226
+ on_enum_with_modifications(value_node, new_node)
227
+ when Language::Nodes::NullValue
228
+ on_null_value_with_modifications(value_node, new_node)
229
+ else
230
+ raise ArgumentError, "Invariant: unexpected argument value node #{value_node.class} (#{value_node.inspect})"
231
+ end
232
+ # Reassign `node` in case the child hook makes a modification
233
+ if new_child_and_node.is_a?(Array)
234
+ new_node = new_child_and_node[1]
235
+ end
236
+ end
237
+ new_node
128
238
  end
129
239
 
130
- make_visit_method :on_argument
131
- make_visit_method :on_directive
132
- make_visit_method :on_directive_definition
133
- make_visit_method :on_directive_location
134
- make_visit_method :on_document
135
- make_visit_method :on_enum
136
- make_visit_method :on_enum_type_definition
137
- make_visit_method :on_enum_type_extension
138
- make_visit_method :on_enum_value_definition
139
- make_visit_method :on_field
140
- make_visit_method :on_field_definition
141
- make_visit_method :on_fragment_definition
142
- make_visit_method :on_fragment_spread
143
- make_visit_method :on_inline_fragment
144
- make_visit_method :on_input_object
145
- make_visit_method :on_input_object_type_definition
146
- make_visit_method :on_input_object_type_extension
147
- make_visit_method :on_input_value_definition
148
- make_visit_method :on_interface_type_definition
149
- make_visit_method :on_interface_type_extension
150
- make_visit_method :on_list_type
151
- make_visit_method :on_non_null_type
152
- make_visit_method :on_null_value
153
- make_visit_method :on_object_type_definition
154
- make_visit_method :on_object_type_extension
155
- make_visit_method :on_operation_definition
156
- make_visit_method :on_scalar_type_definition
157
- make_visit_method :on_scalar_type_extension
158
- make_visit_method :on_schema_definition
159
- make_visit_method :on_schema_extension
160
- make_visit_method :on_type_name
161
- make_visit_method :on_union_type_definition
162
- make_visit_method :on_union_type_extension
163
- make_visit_method :on_variable_definition
164
- make_visit_method :on_variable_identifier
240
+ [
241
+ Language::Nodes::Argument,
242
+ Language::Nodes::Directive,
243
+ Language::Nodes::DirectiveDefinition,
244
+ Language::Nodes::DirectiveLocation,
245
+ Language::Nodes::Document,
246
+ Language::Nodes::Enum,
247
+ Language::Nodes::EnumTypeDefinition,
248
+ Language::Nodes::EnumTypeExtension,
249
+ Language::Nodes::EnumValueDefinition,
250
+ Language::Nodes::Field,
251
+ Language::Nodes::FieldDefinition,
252
+ Language::Nodes::FragmentDefinition,
253
+ Language::Nodes::FragmentSpread,
254
+ Language::Nodes::InlineFragment,
255
+ Language::Nodes::InputObject,
256
+ Language::Nodes::InputObjectTypeDefinition,
257
+ Language::Nodes::InputObjectTypeExtension,
258
+ Language::Nodes::InputValueDefinition,
259
+ Language::Nodes::InterfaceTypeDefinition,
260
+ Language::Nodes::InterfaceTypeExtension,
261
+ Language::Nodes::ListType,
262
+ Language::Nodes::NonNullType,
263
+ Language::Nodes::NullValue,
264
+ Language::Nodes::ObjectTypeDefinition,
265
+ Language::Nodes::ObjectTypeExtension,
266
+ Language::Nodes::OperationDefinition,
267
+ Language::Nodes::ScalarTypeDefinition,
268
+ Language::Nodes::ScalarTypeExtension,
269
+ Language::Nodes::SchemaDefinition,
270
+ Language::Nodes::SchemaExtension,
271
+ Language::Nodes::TypeName,
272
+ Language::Nodes::UnionTypeDefinition,
273
+ Language::Nodes::UnionTypeExtension,
274
+ Language::Nodes::VariableDefinition,
275
+ Language::Nodes::VariableIdentifier,
276
+ ].each do |ast_node_class|
277
+ make_visit_methods(ast_node_class)
278
+ end
165
279
 
166
280
  private
167
281
 
168
- # Run the hooks for `node`, and if the hooks return a copy of `node`,
169
- # copy `parent` so that it contains the copy of that node as a child,
170
- # then return the copies
171
- # If a non-array value is returned, consuming functions should ignore
172
- # said value
173
- def on_node_with_modifications(node, parent)
174
- new_node_and_new_parent = visit_node(node, parent)
282
+ def apply_modifications(node, parent, new_node_and_new_parent)
175
283
  if new_node_and_new_parent.is_a?(Array)
176
284
  new_node = new_node_and_new_parent[0]
177
285
  new_parent = new_node_and_new_parent[1]
@@ -7,14 +7,6 @@ module GraphQL
7
7
  class ActiveRecordRelationConnection < Pagination::RelationConnection
8
8
  private
9
9
 
10
- def relation_larger_than(relation, initial_offset, size)
11
- if already_loaded?(relation)
12
- (relation.size + initial_offset) > size
13
- else
14
- set_offset(sliced_nodes, initial_offset + size).exists?
15
- end
16
- end
17
-
18
10
  def relation_count(relation)
19
11
  int_or_hash = if already_loaded?(relation)
20
12
  relation.size
@@ -72,6 +72,18 @@ module GraphQL
72
72
  # @return [Array<String, Integer>] The current position in the result
73
73
  attr_reader :path
74
74
 
75
+ module EmptyScopedContext
76
+ EMPTY_HASH = {}.freeze
77
+
78
+ def self.key?(k)
79
+ false
80
+ end
81
+
82
+ def self.merged_context
83
+ EMPTY_HASH
84
+ end
85
+ end
86
+
75
87
  # Make a new context which delegates key lookup to `values`
76
88
  # @param query [GraphQL::Query] the query who owns this context
77
89
  # @param values [Hash] A hash of arbitrary values which will be accessible at query-time
@@ -87,13 +99,14 @@ module GraphQL
87
99
  @path = []
88
100
  @value = nil
89
101
  @context = self # for SharedMethods TODO delete sharedmethods
90
- @scoped_context = ScopedContext.new(self)
102
+ @scoped_context = EmptyScopedContext
91
103
  end
92
104
 
93
105
  class ScopedContext
94
106
  def initialize(query_context)
95
107
  @query_context = query_context
96
108
  @scoped_contexts = {}
109
+ @all_keys = Set.new
97
110
  @no_path = [].freeze
98
111
  end
99
112
 
@@ -106,6 +119,7 @@ module GraphQL
106
119
  end
107
120
 
108
121
  def merge!(hash)
122
+ @all_keys.merge(hash.keys)
109
123
  ctx = @scoped_contexts
110
124
  current_path.each do |path_part|
111
125
  ctx = ctx[path_part] ||= { parent: ctx }
@@ -114,15 +128,12 @@ module GraphQL
114
128
  this_scoped_ctx.merge!(hash)
115
129
  end
116
130
 
117
- def current_path
118
- thread_info = Thread.current[:__graphql_runtime_info]
119
- (thread_info && thread_info[:current_path]) || @no_path
120
- end
121
-
122
131
  def key?(key)
123
- each_present_path_ctx do |path_ctx|
124
- if path_ctx.key?(key)
125
- return true
132
+ if @all_keys.include?(key)
133
+ each_present_path_ctx do |path_ctx|
134
+ if path_ctx.key?(key)
135
+ return true
136
+ end
126
137
  end
127
138
  end
128
139
  false
@@ -137,6 +148,10 @@ module GraphQL
137
148
  nil
138
149
  end
139
150
 
151
+ def current_path
152
+ @query_context.current_path || @no_path
153
+ end
154
+
140
155
  def dig(key, *other_keys)
141
156
  each_present_path_ctx do |path_ctx|
142
157
  if path_ctx.key?(key)
@@ -209,14 +224,30 @@ module GraphQL
209
224
  elsif @provided_values.key?(key)
210
225
  @provided_values[key]
211
226
  elsif RUNTIME_METADATA_KEYS.include?(key)
212
- thread_info = Thread.current[:__graphql_runtime_info]
213
- thread_info && thread_info[key]
227
+ if key == :current_path
228
+ current_path
229
+ else
230
+ thread_info = Thread.current[:__graphql_runtime_info]
231
+ thread_info && thread_info[key]
232
+ end
214
233
  else
215
234
  # not found
216
235
  nil
217
236
  end
218
237
  end
219
238
 
239
+ def current_path
240
+ thread_info = Thread.current[:__graphql_runtime_info]
241
+ path = thread_info &&
242
+ (result = thread_info[:current_result]) &&
243
+ (result.path)
244
+ if path && (rn = thread_info[:current_result_name])
245
+ path = path.dup
246
+ path.push(rn)
247
+ end
248
+ path
249
+ end
250
+
220
251
  def delete(key)
221
252
  if @scoped_context.key?(key)
222
253
  @scoped_context.delete(key)
@@ -298,6 +329,9 @@ module GraphQL
298
329
  end
299
330
 
300
331
  def scoped_merge!(hash)
332
+ if @scoped_context == EmptyScopedContext
333
+ @scoped_context = ScopedContext.new(self)
334
+ end
301
335
  @scoped_context.merge!(hash)
302
336
  end
303
337