graphql 2.0.16 → 2.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

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