graphql 1.9.6 → 1.9.7

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.
Files changed (102) hide show
  1. checksums.yaml +5 -5
  2. data/lib/generators/graphql/install_generator.rb +2 -1
  3. data/lib/generators/graphql/templates/base_field.erb +7 -0
  4. data/lib/graphql/analysis/ast/visitor.rb +5 -2
  5. data/lib/graphql/execution/multiplex.rb +5 -1
  6. data/lib/graphql/query.rb +6 -1
  7. data/lib/graphql/relay/array_connection.rb +1 -1
  8. data/lib/graphql/schema.rb +15 -1
  9. data/lib/graphql/schema/argument.rb +5 -1
  10. data/lib/graphql/schema/input_object.rb +21 -12
  11. data/lib/graphql/schema/introspection_system.rb +6 -1
  12. data/lib/graphql/schema/member/has_arguments.rb +4 -2
  13. data/lib/graphql/schema/resolver.rb +8 -3
  14. data/lib/graphql/schema/subscription.rb +22 -0
  15. data/lib/graphql/schema/timeout.rb +109 -0
  16. data/lib/graphql/types/relay/base_edge.rb +0 -3
  17. data/lib/graphql/upgrader/member.rb +148 -111
  18. data/lib/graphql/version.rb +1 -1
  19. data/readme.md +1 -1
  20. data/spec/fixtures/upgrader/mutation.original.rb +28 -0
  21. data/spec/fixtures/upgrader/mutation.transformed.rb +28 -0
  22. data/spec/graphql/analysis/ast_spec.rb +27 -0
  23. data/spec/graphql/execution/instrumentation_spec.rb +34 -6
  24. data/spec/graphql/execution/multiplex_spec.rb +11 -0
  25. data/spec/graphql/internal_representation/rewrite_spec.rb +6 -1
  26. data/spec/graphql/schema/input_object_spec.rb +56 -7
  27. data/spec/graphql/schema/introspection_system_spec.rb +24 -0
  28. data/spec/graphql/schema/subscription_spec.rb +65 -0
  29. data/spec/graphql/schema/timeout_spec.rb +206 -0
  30. data/spec/integration/mongoid/star_trek/schema.rb +1 -2
  31. data/spec/integration/rails/graphql/input_object_spec.rb +19 -0
  32. data/spec/integration/rails/graphql/relay/array_connection_spec.rb +47 -28
  33. data/spec/integration/rails/graphql/schema_spec.rb +18 -0
  34. data/spec/integration/tmp/app/graphql/types/date_type.rb +14 -0
  35. data/spec/integration/tmp/dummy/Gemfile +50 -0
  36. data/spec/integration/tmp/dummy/README.md +24 -0
  37. data/spec/integration/tmp/dummy/Rakefile +6 -0
  38. data/spec/integration/tmp/dummy/app/assets/config/manifest.js +3 -0
  39. data/spec/integration/tmp/dummy/app/assets/javascripts/application.js +16 -0
  40. data/spec/integration/tmp/dummy/app/assets/javascripts/cable.js +13 -0
  41. data/spec/integration/tmp/dummy/app/assets/stylesheets/application.css +15 -0
  42. data/spec/integration/tmp/dummy/app/channels/application_cable/channel.rb +5 -0
  43. data/spec/integration/tmp/dummy/app/channels/application_cable/connection.rb +5 -0
  44. data/spec/integration/tmp/dummy/app/controllers/application_controller.rb +4 -0
  45. data/spec/integration/tmp/dummy/app/controllers/graphql_controller.rb +44 -0
  46. data/spec/integration/tmp/dummy/app/helpers/application_helper.rb +3 -0
  47. data/spec/integration/tmp/dummy/app/jobs/application_job.rb +3 -0
  48. data/spec/integration/tmp/dummy/app/mailers/application_mailer.rb +5 -0
  49. data/spec/integration/tmp/dummy/app/mydirectory/dummy_schema.rb +5 -0
  50. data/spec/integration/tmp/dummy/app/mydirectory/mutations/update_name.rb +15 -0
  51. data/spec/integration/tmp/dummy/app/mydirectory/types/base_enum.rb +5 -0
  52. data/spec/integration/tmp/dummy/app/mydirectory/types/base_input_object.rb +5 -0
  53. data/spec/integration/tmp/dummy/app/mydirectory/types/base_interface.rb +6 -0
  54. data/spec/integration/tmp/dummy/app/mydirectory/types/base_object.rb +5 -0
  55. data/spec/integration/tmp/dummy/app/mydirectory/types/base_scalar.rb +5 -0
  56. data/spec/integration/tmp/dummy/app/mydirectory/types/base_union.rb +5 -0
  57. data/spec/integration/tmp/dummy/app/mydirectory/types/mutation_type.rb +12 -0
  58. data/spec/integration/tmp/dummy/app/mydirectory/types/query_type.rb +14 -0
  59. data/spec/integration/tmp/dummy/app/views/layouts/application.html.erb +14 -0
  60. data/spec/integration/tmp/dummy/app/views/layouts/mailer.html.erb +13 -0
  61. data/spec/integration/tmp/dummy/app/views/layouts/mailer.text.erb +1 -0
  62. data/spec/integration/tmp/dummy/bin/bundle +3 -0
  63. data/spec/integration/tmp/dummy/bin/rails +4 -0
  64. data/spec/integration/tmp/dummy/bin/rake +4 -0
  65. data/spec/integration/tmp/dummy/bin/setup +34 -0
  66. data/spec/integration/tmp/dummy/bin/update +29 -0
  67. data/spec/integration/tmp/dummy/config.ru +5 -0
  68. data/spec/integration/tmp/dummy/config/application.rb +26 -0
  69. data/spec/integration/tmp/dummy/config/boot.rb +4 -0
  70. data/spec/integration/tmp/dummy/config/cable.yml +9 -0
  71. data/spec/integration/tmp/dummy/config/environment.rb +6 -0
  72. data/spec/integration/tmp/dummy/config/environments/development.rb +52 -0
  73. data/spec/integration/tmp/dummy/config/environments/production.rb +84 -0
  74. data/spec/integration/tmp/dummy/config/environments/test.rb +43 -0
  75. data/spec/integration/tmp/dummy/config/initializers/application_controller_renderer.rb +9 -0
  76. data/spec/integration/tmp/dummy/config/initializers/assets.rb +12 -0
  77. data/spec/integration/tmp/dummy/config/initializers/backtrace_silencers.rb +8 -0
  78. data/spec/integration/tmp/dummy/config/initializers/cookies_serializer.rb +6 -0
  79. data/spec/integration/tmp/dummy/config/initializers/filter_parameter_logging.rb +5 -0
  80. data/spec/integration/tmp/dummy/config/initializers/inflections.rb +17 -0
  81. data/spec/integration/tmp/dummy/config/initializers/mime_types.rb +5 -0
  82. data/spec/integration/tmp/dummy/config/initializers/new_framework_defaults.rb +24 -0
  83. data/spec/integration/tmp/dummy/config/initializers/session_store.rb +4 -0
  84. data/spec/integration/tmp/dummy/config/initializers/wrap_parameters.rb +10 -0
  85. data/spec/integration/tmp/dummy/config/locales/en.yml +23 -0
  86. data/spec/integration/tmp/dummy/config/puma.rb +48 -0
  87. data/spec/integration/tmp/dummy/config/routes.rb +9 -0
  88. data/spec/integration/tmp/dummy/config/secrets.yml +22 -0
  89. data/spec/integration/tmp/dummy/db/seeds.rb +8 -0
  90. data/spec/integration/tmp/dummy/log/test.log +0 -0
  91. data/spec/integration/tmp/dummy/public/404.html +67 -0
  92. data/spec/integration/tmp/dummy/public/422.html +67 -0
  93. data/spec/integration/tmp/dummy/public/500.html +66 -0
  94. data/spec/integration/tmp/dummy/public/apple-touch-icon-precomposed.png +0 -0
  95. data/spec/integration/tmp/dummy/public/apple-touch-icon.png +0 -0
  96. data/spec/integration/tmp/dummy/public/favicon.ico +0 -0
  97. data/spec/integration/tmp/dummy/public/robots.txt +5 -0
  98. data/spec/integration/tmp/dummy/test/test_helper.rb +8 -0
  99. data/spec/support/jazz.rb +6 -0
  100. data/spec/support/star_wars/schema.rb +1 -2
  101. metadata +171 -6
  102. data/spec/integration/tmp/app/graphql/types/bird_type.rb +0 -7
@@ -32,9 +32,6 @@ module GraphQL
32
32
  def node_type(node_type = nil, null: true)
33
33
  if node_type
34
34
  @node_type = node_type
35
- wrapped_type_name = node_type.graphql_name
36
- # Set this to be named like the node type, but suffixed with `Edge`
37
- graphql_name("#{wrapped_type_name}Edge")
38
35
  # Add a default `node` field
39
36
  field :node, node_type, null: null, description: "The item at the end of the edge."
40
37
  end
@@ -125,7 +125,7 @@ module GraphQL
125
125
  end
126
126
 
127
127
  def apply(input_text)
128
- input_text.sub(@find_pattern, @replace_pattern)
128
+ input_text.gsub(@find_pattern, @replace_pattern)
129
129
  end
130
130
  end
131
131
 
@@ -147,10 +147,10 @@ module GraphQL
147
147
  name = matches[:overridden_name]
148
148
  if type_name_without_the_type_part != name
149
149
  # If the overridden name is still required, use `graphql_name` for it
150
- transformable = transformable.sub(/ name (.*)/, ' graphql_name \1')
150
+ transformable = transformable.gsub(/ name (.*)/, ' graphql_name \1')
151
151
  else
152
152
  # Otherwise, remove it altogether
153
- transformable = transformable.sub(/\s+name ('|").*('|")/, '')
153
+ transformable = transformable.gsub(/\s+name ('|").*('|")/, '')
154
154
  end
155
155
  end
156
156
  end
@@ -285,43 +285,57 @@ module GraphQL
285
285
  def apply(input_text)
286
286
  if input_text =~ @proc_check_pattern
287
287
  processor = apply_processor(input_text, NamedProcProcessor.new(@proc_name))
288
- proc_body = input_text[processor.proc_body_start..processor.proc_body_end]
289
- method_defn_indent = " " * processor.proc_defn_indent
290
- method_defn = "def self.#{@proc_name}(#{processor.proc_arg_names.join(", ")})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
291
- method_defn = trim_lines(method_defn)
292
- # replace the proc with the new method
293
- input_text[processor.proc_defn_start..processor.proc_defn_end] = method_defn
288
+ processor.proc_to_method_sections.reverse.each do |proc_to_method_section|
289
+ proc_body = input_text[proc_to_method_section.proc_body_start..proc_to_method_section.proc_body_end]
290
+ method_defn_indent = " " * proc_to_method_section.proc_defn_indent
291
+ method_defn = "def self.#{@proc_name}(#{proc_to_method_section.proc_arg_names.join(", ")})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
292
+ method_defn = trim_lines(method_defn)
293
+ # replace the proc with the new method
294
+ input_text[proc_to_method_section.proc_defn_start..proc_to_method_section.proc_defn_end] = method_defn
295
+ end
294
296
  end
295
297
  input_text
296
298
  end
297
299
 
298
300
  class NamedProcProcessor < Parser::AST::Processor
299
- attr_reader :proc_arg_names, :proc_defn_start, :proc_defn_end, :proc_defn_indent, :proc_body_start, :proc_body_end
301
+ attr_reader :proc_to_method_sections
300
302
  def initialize(proc_name)
301
303
  @proc_name_sym = proc_name.to_sym
302
- @proc_arg_names = nil
303
- # Beginning of the `#{proc_name} -> {...}` call
304
- @proc_defn_start = nil
305
- # End of the last `end/}`
306
- @proc_defn_end = nil
307
- # Amount of whitespace to insert to the rewritten body
308
- @proc_defn_indent = nil
309
- # First statement of the proc
310
- @proc_body_start = nil
311
- # End of last statement in the proc
312
- @proc_body_end = nil
313
- # Used for identifying the proper block
314
- @inside_proc = false
304
+ @proc_to_method_sections = []
305
+ end
306
+
307
+ class ProcToMethodSection
308
+ attr_accessor :proc_arg_names, :proc_defn_start, :proc_defn_end, :proc_defn_indent, :proc_body_start, :proc_body_end, :inside_proc
309
+
310
+ def initialize
311
+ # @proc_name_sym = proc_name.to_sym
312
+ @proc_arg_names = nil
313
+ # Beginning of the `#{proc_name} -> {...}` call
314
+ @proc_defn_start = nil
315
+ # End of the last `end/}`
316
+ @proc_defn_end = nil
317
+ # Amount of whitespace to insert to the rewritten body
318
+ @proc_defn_indent = nil
319
+ # First statement of the proc
320
+ @proc_body_start = nil
321
+ # End of last statement in the proc
322
+ @proc_body_end = nil
323
+ # Used for identifying the proper block
324
+ @inside_proc = false
325
+ end
315
326
  end
316
327
 
317
328
  def on_send(node)
318
329
  receiver, method_name, _args = *node
319
330
  if method_name == @proc_name_sym && receiver.nil?
331
+ proc_section = ProcToMethodSection.new
320
332
  source_exp = node.loc.expression
321
- @proc_defn_start = source_exp.begin.begin_pos
322
- @proc_defn_end = source_exp.end.end_pos
323
- @proc_defn_indent = source_exp.column
324
- @inside_proc = true
333
+ proc_section.proc_defn_start = source_exp.begin.begin_pos
334
+ proc_section.proc_defn_end = source_exp.end.end_pos
335
+ proc_section.proc_defn_indent = source_exp.column
336
+ proc_section.inside_proc = true
337
+
338
+ @proc_to_method_sections << proc_section
325
339
  end
326
340
  res = super(node)
327
341
  @inside_proc = false
@@ -331,11 +345,14 @@ module GraphQL
331
345
  def on_block(node)
332
346
  send_node, args_node, body_node = node.children
333
347
  _receiver, method_name, _send_args_node = *send_node
334
- if method_name == :lambda && @inside_proc
348
+ if method_name == :lambda && !@proc_to_method_sections.empty? && @proc_to_method_sections[-1].inside_proc
349
+ proc_to_method_section = @proc_to_method_sections[-1]
350
+
335
351
  source_exp = body_node.loc.expression
336
- @proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
337
- @proc_body_start = source_exp.begin.begin_pos
338
- @proc_body_end = source_exp.end.end_pos
352
+ proc_to_method_section.proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
353
+ proc_to_method_section.proc_body_start = source_exp.begin.begin_pos
354
+ proc_to_method_section.proc_body_end = source_exp.end.end_pos
355
+ proc_to_method_section.inside_proc = false
339
356
  end
340
357
  super(node)
341
358
  end
@@ -353,30 +370,34 @@ module GraphQL
353
370
  if input_text =~ /GraphQL::Relay::Mutation\.define/
354
371
  named_proc_processor = apply_processor(input_text, ProcToClassMethodTransform::NamedProcProcessor.new(@proc_name))
355
372
  resolve_proc_processor = apply_processor(input_text, ResolveProcToMethodTransform::ResolveProcProcessor.new)
356
- proc_body = input_text[named_proc_processor.proc_body_start..named_proc_processor.proc_body_end]
357
- method_defn_indent = " " * named_proc_processor.proc_defn_indent
358
-
359
- obj_arg_name, args_arg_name, ctx_arg_name = resolve_proc_processor.proc_arg_names
360
- # This is not good, it will hit false positives
361
- # Should use AST to make this substitution
362
- if obj_arg_name != "_"
363
- proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
364
- end
365
- if ctx_arg_name != "_"
366
- proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
367
- end
368
373
 
369
- method_defn = "def #{@proc_name}(**#{args_arg_name})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
370
- method_defn = trim_lines(method_defn)
371
- # Update usage of args keys
372
- method_defn = method_defn.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
373
- method_begin = $~[:method_begin]
374
- arg_name = underscorize($~[:arg_name])
375
- method_end = $~[:method_end]
376
- "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
374
+ named_proc_processor.proc_to_method_sections.zip(resolve_proc_processor.resolve_proc_sections).reverse.each do |pair|
375
+ proc_to_method_section, resolve_proc_section = *pair
376
+ proc_body = input_text[proc_to_method_section.proc_body_start..proc_to_method_section.proc_body_end]
377
+ method_defn_indent = " " * proc_to_method_section.proc_defn_indent
378
+
379
+ obj_arg_name, args_arg_name, ctx_arg_name = resolve_proc_section.proc_arg_names
380
+ # This is not good, it will hit false positives
381
+ # Should use AST to make this substitution
382
+ if obj_arg_name != "_"
383
+ proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
384
+ end
385
+ if ctx_arg_name != "_"
386
+ proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
387
+ end
388
+
389
+ method_defn = "def #{@proc_name}(**#{args_arg_name})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
390
+ method_defn = trim_lines(method_defn)
391
+ # Update usage of args keys
392
+ method_defn = method_defn.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
393
+ method_begin = $~[:method_begin]
394
+ arg_name = underscorize($~[:arg_name])
395
+ method_end = $~[:method_end]
396
+ "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
397
+ end
398
+ # replace the proc with the new method
399
+ input_text[proc_to_method_section.proc_defn_start..proc_to_method_section.proc_defn_end] = method_defn
377
400
  end
378
- # replace the proc with the new method
379
- input_text[named_proc_processor.proc_defn_start..named_proc_processor.proc_defn_end] = method_defn
380
401
  end
381
402
  input_text
382
403
  end
@@ -512,74 +533,86 @@ module GraphQL
512
533
  input_text.match(/(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)/)
513
534
  field_name = $~[:name]
514
535
  processor = apply_processor(input_text, ResolveProcProcessor.new)
515
- proc_body = input_text[processor.proc_start..processor.proc_end]
516
- obj_arg_name, args_arg_name, ctx_arg_name = processor.proc_arg_names
517
- # This is not good, it will hit false positives
518
- # Should use AST to make this substitution
519
- if obj_arg_name != "_"
520
- proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
521
- end
522
- if ctx_arg_name != "_"
523
- proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
524
- end
525
536
 
526
- method_def_indent = " " * (processor.resolve_indent - 2)
527
- # Turn the proc body into a method body
528
- method_body = reindent_lines(proc_body, from_indent: processor.resolve_indent + 2, to_indent: processor.resolve_indent)
529
- # Add `def... end`
530
- method_def = if input_text.include?("argument ")
531
- # This field has arguments
532
- "def #{field_name}(**#{args_arg_name})"
533
- else
534
- # No field arguments, so, no method arguments
535
- "def #{field_name}"
536
- end
537
- # Wrap the body in def ... end
538
- method_body = "\n#{method_def_indent}#{method_def}\n#{method_body}\n#{method_def_indent}end\n"
539
- # Update Argument access to be underscore and symbols
540
- # Update `args[...]` and `args.key?`
541
- method_body = method_body.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
542
- method_begin = $~[:method_begin]
543
- arg_name = underscorize($~[:arg_name])
544
- method_end = $~[:method_end]
545
- "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
546
- end
537
+ processor.resolve_proc_sections.reverse.each do |resolve_proc_section|
538
+ proc_body = input_text[resolve_proc_section.proc_start..resolve_proc_section.proc_end]
539
+ obj_arg_name, args_arg_name, ctx_arg_name = resolve_proc_section.proc_arg_names
540
+ # This is not good, it will hit false positives
541
+ # Should use AST to make this substitution
542
+ if obj_arg_name != "_"
543
+ proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
544
+ end
545
+ if ctx_arg_name != "_"
546
+ proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
547
+ end
548
+
549
+ method_def_indent = " " * (resolve_proc_section.resolve_indent - 2)
550
+ # Turn the proc body into a method body
551
+ method_body = reindent_lines(proc_body, from_indent: resolve_proc_section.resolve_indent + 2, to_indent: resolve_proc_section.resolve_indent)
552
+ # Add `def... end`
553
+ method_def = if input_text.include?("argument ")
554
+ # This field has arguments
555
+ "def #{field_name}(**#{args_arg_name})"
556
+ else
557
+ # No field arguments, so, no method arguments
558
+ "def #{field_name}"
559
+ end
560
+ # Wrap the body in def ... end
561
+ method_body = "\n#{method_def_indent}#{method_def}\n#{method_body}\n#{method_def_indent}end\n"
562
+ # Update Argument access to be underscore and symbols
563
+ # Update `args[...]` and `args.key?`
564
+ method_body = method_body.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
565
+ method_begin = $~[:method_begin]
566
+ arg_name = underscorize($~[:arg_name])
567
+ method_end = $~[:method_end]
568
+ "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
569
+ end
547
570
 
548
- # Replace the resolve proc with the method
549
- input_text[processor.resolve_start..processor.resolve_end] = ""
550
- # The replacement above might have left some preceeding whitespace,
551
- # so remove it by deleting all whitespace chars before `resolve`:
552
- preceeding_whitespace = processor.resolve_start - 1
553
- while input_text[preceeding_whitespace] == " " && preceeding_whitespace > 0
554
- input_text[preceeding_whitespace] = ""
555
- preceeding_whitespace -= 1
571
+ # Replace the resolve proc with the method
572
+ input_text[resolve_proc_section.resolve_start..resolve_proc_section.resolve_end] = ""
573
+ # The replacement above might have left some preceeding whitespace,
574
+ # so remove it by deleting all whitespace chars before `resolve`:
575
+ preceeding_whitespace = resolve_proc_section.resolve_start - 1
576
+ while input_text[preceeding_whitespace] == " " && preceeding_whitespace > 0
577
+ input_text[preceeding_whitespace] = ""
578
+ preceeding_whitespace -= 1
579
+ end
580
+ input_text += method_body
581
+ input_text
556
582
  end
557
- input_text += method_body
558
- input_text
559
- else
560
- # No resolve proc
561
- input_text
562
583
  end
584
+
585
+ input_text
563
586
  end
564
587
 
565
588
  class ResolveProcProcessor < Parser::AST::Processor
566
- attr_reader :proc_start, :proc_end, :proc_arg_names, :resolve_start, :resolve_end, :resolve_indent
589
+ attr_reader :resolve_proc_sections
567
590
  def initialize
568
- @proc_arg_names = nil
569
- @resolve_start = nil
570
- @resolve_end = nil
571
- @resolve_indent = nil
572
- @proc_start = nil
573
- @proc_end = nil
591
+ @resolve_proc_sections = []
592
+ end
593
+
594
+ class ResolveProcSection
595
+ attr_accessor :proc_start, :proc_end, :proc_arg_names, :resolve_start, :resolve_end, :resolve_indent
596
+ def initialize
597
+ @proc_arg_names = nil
598
+ @resolve_start = nil
599
+ @resolve_end = nil
600
+ @resolve_indent = nil
601
+ @proc_start = nil
602
+ @proc_end = nil
603
+ end
574
604
  end
575
605
 
576
606
  def on_send(node)
577
607
  receiver, method_name, _args = *node
578
608
  if method_name == :resolve && receiver.nil?
609
+ resolve_proc_section = ResolveProcSection.new
579
610
  source_exp = node.loc.expression
580
- @resolve_start = source_exp.begin.begin_pos
581
- @resolve_end = source_exp.end.end_pos
582
- @resolve_indent = source_exp.column
611
+ resolve_proc_section.resolve_start = source_exp.begin.begin_pos
612
+ resolve_proc_section.resolve_end = source_exp.end.end_pos
613
+ resolve_proc_section.resolve_indent = source_exp.column
614
+
615
+ @resolve_proc_sections << resolve_proc_section
583
616
  end
584
617
  super(node)
585
618
  end
@@ -588,11 +621,15 @@ module GraphQL
588
621
  send_node, args_node, body_node = node.children
589
622
  _receiver, method_name, _send_args_node = *send_node
590
623
  # Assume that the first three-argument proc we enter is the resolve
591
- if method_name == :lambda && args_node.children.size == 3 && @proc_arg_names.nil?
624
+ if (
625
+ method_name == :lambda && args_node.children.size == 3 &&
626
+ !@resolve_proc_sections.empty? && @resolve_proc_sections[-1].proc_arg_names.nil?
627
+ )
628
+ resolve_proc_section = @resolve_proc_sections[-1]
592
629
  source_exp = body_node.loc.expression
593
- @proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
594
- @proc_start = source_exp.begin.begin_pos
595
- @proc_end = source_exp.end.end_pos
630
+ resolve_proc_section.proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
631
+ resolve_proc_section.proc_start = source_exp.begin.begin_pos
632
+ resolve_proc_section.proc_end = source_exp.end.end_pos
596
633
  end
597
634
  super(node)
598
635
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.9.6"
3
+ VERSION = "1.9.7"
4
4
  end
data/readme.md CHANGED
@@ -33,7 +33,7 @@ $ rails generate graphql:install
33
33
 
34
34
  After this, you may need to run `bundle install` again, as by default graphiql-rails is added on installation.
35
35
 
36
- Or, see ["Getting Started"](https://graphql-ruby.org/).
36
+ Or, see ["Getting Started"](https://graphql-ruby.org/getting_started.html).
37
37
 
38
38
  ## Upgrade
39
39
 
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module Platform
3
+ module Mutations
4
+ Echo = GraphQL::Relay::Mutation.define do
5
+ name 'EchoMutation'
6
+
7
+ input_field :message, types.String
8
+
9
+ field :data, types.String
10
+
11
+ resolve ->(_obj, inputs, _ctx) {
12
+ { data: inputs[:message] }
13
+ }
14
+ end
15
+
16
+ Repeat = GraphQL::Relay::Mutation.define do
17
+ name 'RepeatMutation'
18
+
19
+ input_field :message, types.String
20
+
21
+ field :data, types.String
22
+
23
+ resolve ->(_obj, inputs, _ctx) {
24
+ { data: inputs[:message] }
25
+ }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module Platform
3
+ module Mutations
4
+ class Echo < Mutations::BaseMutation
5
+ graphql_name 'EchoMutation'
6
+
7
+ argument :message, String, required: false
8
+
9
+ field :data, String, null: true
10
+
11
+ def resolve(**inputs)
12
+ { data: inputs[:message] }
13
+ end
14
+ end
15
+
16
+ class Repeat < Mutations::BaseMutation
17
+ graphql_name 'RepeatMutation'
18
+
19
+ argument :message, String, required: false
20
+
21
+ field :data, String, null: true
22
+
23
+ def resolve(**inputs)
24
+ { data: inputs[:message] }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -71,6 +71,17 @@ describe GraphQL::Analysis::AST do
71
71
  end
72
72
  end
73
73
 
74
+ class AstArguments < GraphQL::Analysis::AST::Analyzer
75
+ def on_enter_argument(node, parent, visitor)
76
+ @argument = visitor.argument_definition
77
+ @previous_argument = visitor.previous_argument_definition
78
+ end
79
+
80
+ def result
81
+ [@argument, @previous_argument]
82
+ end
83
+ end
84
+
74
85
  describe "using the AST analysis engine" do
75
86
  let(:schema) do
76
87
  query_type = Class.new(GraphQL::Schema::Object) do
@@ -179,6 +190,22 @@ describe GraphQL::Analysis::AST do
179
190
  assert_equal "__Schema.types", prev_field.metadata[:type_class].path
180
191
  end
181
192
  end
193
+
194
+ describe "Visitor#argument_definition" do
195
+ let(:analyzers) { [AstArguments] }
196
+ let(:query) do
197
+ GraphQL::Query.new(
198
+ Dummy::Schema,
199
+ '{ searchDairy(product: [{ source: "SHEEP" }]) { ... on Cheese { id } } }'
200
+ )
201
+ end
202
+
203
+ it "it runs the analyzer" do
204
+ argument, prev_argument = reduce_result.first
205
+ assert_equal "DairyProductInput.source", argument.metadata[:type_class].path
206
+ assert_equal "Query.searchDairy.product", prev_argument.metadata[:type_class].path
207
+ end
208
+ end
182
209
  end
183
210
 
184
211
  it "calls the defined analyzers" do