graphql 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9997a74f471a1d4abbca1fe24f276db80bbad6f87b278a8083491ced64354906
4
- data.tar.gz: 4265a7d1e76f784ad3df4739328fedfae1698e26dc4a1fee982971650e553c47
3
+ metadata.gz: c2b3d848591e918d34df3851a973b6510912d6472f5847f493b5b0bfed4df997
4
+ data.tar.gz: 3fcf17d2bc7259ef01cbe6d85aa23b31ccbed2321700c0474124e5eb6f0792d2
5
5
  SHA512:
6
- metadata.gz: 2b1c5bed0660b9c194814e889384b3a37a27967c1f808aff55e693431056729434a2556cf85e9dd4c184262f8ad1344fe86316c3ffa12afc2c87315d69773f04
7
- data.tar.gz: 0b2c961d00651a479ce183e0c77d23d2090df85f46490aac53d724d0f657f9cfe1c49bda191feec4b3a90f1cf671cd55f0a6a97d0c79c20d567fc47e214f921d
6
+ metadata.gz: 8b2b258c108210008131e610ff0e486817ceebea1782d4f329f1f776ac8c70bdc03177f22a746c2f918d9a1c15e3b6e3971f2569cd3d2c15c6dd714e5a5d38d7
7
+ data.tar.gz: 28e7f3c992987507e74e42320133b710aa6ca6af593dd2b6300359e30728ad415ff27e7671b5e8cfedb4b1a44324a06bc9fdd4ab95535c1f99e142aafebc3ea5
@@ -28,17 +28,22 @@ module GraphQL
28
28
  def initialize(query)
29
29
  @max_depth = 0
30
30
  @current_depth = 0
31
+ @count_introspection_fields = query.schema.count_introspection_fields
31
32
  super
32
33
  end
33
34
 
34
35
  def on_enter_field(node, parent, visitor)
35
- return if visitor.skipping? || visitor.visiting_fragment_definition?
36
+ return if visitor.skipping? ||
37
+ visitor.visiting_fragment_definition? ||
38
+ (@count_introspection_fields == false && visitor.field_definition.introspection?)
36
39
 
37
40
  @current_depth += 1
38
41
  end
39
42
 
40
43
  def on_leave_field(node, parent, visitor)
41
- return if visitor.skipping? || visitor.visiting_fragment_definition?
44
+ return if visitor.skipping? ||
45
+ visitor.visiting_fragment_definition? ||
46
+ (@count_introspection_fields == false && visitor.field_definition.introspection?)
42
47
 
43
48
  if @max_depth < @current_depth
44
49
  @max_depth = @current_depth
@@ -61,6 +61,32 @@ module GraphQL
61
61
  @nonblocking
62
62
  end
63
63
 
64
+ # This is called before the fiber is spawned, from the parent context (i.e. from
65
+ # the thread or fiber that it is scheduled from).
66
+ #
67
+ # @return [Hash<Symbol, Object>] Current fiber-local variables
68
+ def get_fiber_variables
69
+ fiber_vars = {}
70
+ Thread.current.keys.each do |fiber_var_key|
71
+ # This variable should be fresh in each new fiber
72
+ if fiber_var_key != :__graphql_runtime_info
73
+ fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
74
+ end
75
+ end
76
+ fiber_vars
77
+ end
78
+
79
+ # Set up the fiber variables in a new fiber.
80
+ #
81
+ # This is called within the fiber, right after it is spawned.
82
+ #
83
+ # @param vars [Hash<Symbol, Object>] Fiber-local variables from {get_fiber_variables}
84
+ # @return [void]
85
+ def set_fiber_variables(vars)
86
+ vars.each { |k, v| Thread.current[k] = v }
87
+ nil
88
+ end
89
+
64
90
  # Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
65
91
  #
66
92
  # @param source_class [Class<GraphQL::Dataloader::Source]
@@ -295,23 +321,16 @@ module GraphQL
295
321
  #
296
322
  # @see https://github.com/rmosolgo/graphql-ruby/issues/3449
297
323
  def spawn_fiber
298
- fiber_locals = {}
299
-
300
- Thread.current.keys.each do |fiber_var_key|
301
- # This variable should be fresh in each new fiber
302
- if fiber_var_key != :__graphql_runtime_info
303
- fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
304
- end
305
- end
324
+ fiber_vars = get_fiber_variables
306
325
 
307
326
  if @nonblocking
308
327
  Fiber.new(blocking: false) do
309
- fiber_locals.each { |k, v| Thread.current[k] = v }
328
+ set_fiber_variables(fiber_vars)
310
329
  yield
311
330
  end
312
331
  else
313
332
  Fiber.new do
314
- fiber_locals.each { |k, v| Thread.current[k] = v }
333
+ set_fiber_variables(fiber_vars)
315
334
  yield
316
335
  end
317
336
  end
@@ -107,7 +107,7 @@ module GraphQL
107
107
  when GraphQLResultArray
108
108
  # There's no special handling of arrays because currently, there's no way to split the execution
109
109
  # of a list over several concurrent flows.
110
- next_result.set_child_result(key, value)
110
+ into_result.set_child_result(key, value)
111
111
  else
112
112
  # We have to assume that, since this passed the `fields_will_merge` selection,
113
113
  # that the old and new values are the same.
@@ -704,7 +704,7 @@ module GraphQL
704
704
  inner_object.dig(*@dig_keys)
705
705
  elsif inner_object.key?(@method_sym)
706
706
  inner_object[@method_sym]
707
- elsif inner_object.key?(@method_str)
707
+ elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
708
708
  inner_object[@method_str]
709
709
  elsif @fallback_value != NOT_CONFIGURED
710
710
  @fallback_value
@@ -392,8 +392,11 @@ module GraphQL
392
392
  application_object_type = resolve_type_result
393
393
  # application_object is already assigned
394
394
  end
395
- possible_object_types = context.warden.possible_types(argument.loads)
396
- if !possible_object_types.include?(application_object_type)
395
+
396
+ if !(
397
+ context.warden.possible_types(argument.loads).include?(application_object_type) ||
398
+ context.warden.loadable?(argument.loads, context)
399
+ )
397
400
  err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
398
401
  load_application_object_failed(err)
399
402
  else
@@ -60,6 +60,7 @@ module GraphQL
60
60
  def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
61
61
  def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
62
62
  def arguments(owner, ctx); owner.arguments(ctx); end
63
+ def loadable?(type, ctx); type.visible?(ctx); end
63
64
  end
64
65
  end
65
66
 
@@ -84,6 +85,7 @@ module GraphQL
84
85
  def fields(type_defn); type_defn.all_field_definitions; end # rubocop:disable Development/ContextIsPassedCop
85
86
  def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
86
87
  def reachable_type?(type_name); true; end
88
+ def loadable?(type, _ctx); true; end
87
89
  def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
88
90
  def possible_types(type_defn); @schema.possible_types(type_defn); end
89
91
  def interfaces(obj_type); obj_type.interfaces; end
@@ -122,6 +124,11 @@ module GraphQL
122
124
  end
123
125
  end
124
126
 
127
+ # @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden.
128
+ def loadable?(type, _ctx)
129
+ !reachable_type_set.include?(type) && visible_type?(type)
130
+ end
131
+
125
132
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
126
133
  def get_type(type_name)
127
134
  @visible_types ||= read_through do |name|
@@ -282,11 +289,19 @@ module GraphQL
282
289
  next true if root_type?(type_defn) || type_defn.introspection?
283
290
 
284
291
  if type_defn.kind.union?
285
- visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
292
+ possible_types(type_defn).any? && (referenced?(type_defn) || orphan_type?(type_defn))
286
293
  elsif type_defn.kind.interface?
287
- visible_possible_types?(type_defn)
294
+ possible_types(type_defn).any?
288
295
  else
289
- referenced?(type_defn) || visible_abstract_type?(type_defn)
296
+ if referenced?(type_defn)
297
+ true
298
+ elsif type_defn.kind.object?
299
+ # Show this object if it belongs to ...
300
+ interfaces(type_defn).any? { |t| referenced?(t) } || # an interface which is referenced in the schema
301
+ union_memberships(type_defn).any? { |t| referenced?(t) || orphan_type?(t) } # or a union which is referenced or added via orphan_types
302
+ else
303
+ false
304
+ end
290
305
  end
291
306
  end
292
307
 
@@ -353,17 +368,6 @@ module GraphQL
353
368
  @schema.orphan_types.include?(type_defn)
354
369
  end
355
370
 
356
- def visible_abstract_type?(type_defn)
357
- type_defn.kind.object? && (
358
- interfaces(type_defn).any? ||
359
- union_memberships(type_defn).any?
360
- )
361
- end
362
-
363
- def visible_possible_types?(type_defn)
364
- possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
365
- end
366
-
367
371
  def visible?(member)
368
372
  @visibility_cache[member]
369
373
  end
@@ -402,54 +406,62 @@ module GraphQL
402
406
  end
403
407
  end
404
408
 
409
+ included_interface_possible_types_set = Set.new
410
+
405
411
  until unvisited_types.empty?
406
412
  type = unvisited_types.pop
407
- if @reachable_type_set.add?(type)
408
- type_by_name = rt_hash[type.graphql_name] ||= type
409
- if type_by_name != type
410
- raise DuplicateNamesError.new(
411
- duplicated_name: type.graphql_name, duplicated_definition_1: type.inspect, duplicated_definition_2: type_by_name.inspect
412
- )
413
+ visit_type(type, unvisited_types, @reachable_type_set, rt_hash, included_interface_possible_types_set, include_interface_possible_types: false)
414
+ end
415
+
416
+ @reachable_type_set
417
+ end
418
+
419
+ def visit_type(type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types:)
420
+ if visited_type_set.add?(type) || (include_interface_possible_types && type.kind.interface? && included_interface_possible_types_set.add?(type))
421
+ type_by_name = type_by_name_hash[type.graphql_name] ||= type
422
+ if type_by_name != type
423
+ name_1, name_2 = [type.inspect, type_by_name.inspect].sort
424
+ raise DuplicateNamesError.new(
425
+ duplicated_name: type.graphql_name, duplicated_definition_1: name_1, duplicated_definition_2: name_2
426
+ )
427
+ end
428
+ if type.kind.input_object?
429
+ # recurse into visible arguments
430
+ arguments(type).each do |argument|
431
+ argument_type = argument.type.unwrap
432
+ unvisited_types << argument_type
413
433
  end
414
- if type.kind.input_object?
415
- # recurse into visible arguments
416
- arguments(type).each do |argument|
417
- argument_type = argument.type.unwrap
418
- unvisited_types << argument_type
419
- end
420
- elsif type.kind.union?
421
- # recurse into visible possible types
422
- possible_types(type).each do |possible_type|
423
- unvisited_types << possible_type
434
+ elsif type.kind.union?
435
+ # recurse into visible possible types
436
+ possible_types(type).each do |possible_type|
437
+ unvisited_types << possible_type
438
+ end
439
+ elsif type.kind.fields?
440
+ if type.kind.object?
441
+ # recurse into visible implemented interfaces
442
+ interfaces(type).each do |interface|
443
+ unvisited_types << interface
424
444
  end
425
- elsif type.kind.fields?
426
- if type.kind.interface?
427
- # recurse into visible possible types
428
- possible_types(type).each do |possible_type|
429
- unvisited_types << possible_type
430
- end
431
- elsif type.kind.object?
432
- # recurse into visible implemented interfaces
433
- interfaces(type).each do |interface|
434
- unvisited_types << interface
435
- end
445
+ elsif include_interface_possible_types
446
+ possible_types(type).each do |pt|
447
+ unvisited_types << pt
436
448
  end
449
+ end
450
+ # Don't visit interface possible types -- it's not enough to justify visibility
437
451
 
438
- # recurse into visible fields
439
- fields(type).each do |field|
440
- field_type = field.type.unwrap
441
- unvisited_types << field_type
442
- # recurse into visible arguments
443
- arguments(field).each do |argument|
444
- argument_type = argument.type.unwrap
445
- unvisited_types << argument_type
446
- end
452
+ # recurse into visible fields
453
+ fields(type).each do |field|
454
+ field_type = field.type.unwrap
455
+ # In this case, if it's an interface, we want to include
456
+ visit_type(field_type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types: true)
457
+ # recurse into visible arguments
458
+ arguments(field).each do |argument|
459
+ argument_type = argument.type.unwrap
460
+ unvisited_types << argument_type
447
461
  end
448
462
  end
449
463
  end
450
464
  end
451
-
452
- @reachable_type_set
453
465
  end
454
466
  end
455
467
  end
@@ -146,6 +146,19 @@ module GraphQL
146
146
  @subscriptions = new_implementation
147
147
  end
148
148
 
149
+ # @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
150
+ def default_trace_mode(new_mode = nil)
151
+ if new_mode
152
+ @default_trace_mode = new_mode
153
+ elsif defined?(@default_trace_mode)
154
+ @default_trace_mode
155
+ elsif superclass.respond_to?(:default_trace_mode)
156
+ superclass.default_trace_mode
157
+ else
158
+ :default
159
+ end
160
+ end
161
+
149
162
  def trace_class(new_class = nil)
150
163
  if new_class
151
164
  trace_mode(:default, new_class)
@@ -157,42 +170,66 @@ module GraphQL
157
170
  end
158
171
 
159
172
  # @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined.
160
- def trace_class_for(mode)
161
- @trace_modes ||= {}
162
- @trace_modes[mode] ||= begin
163
- case mode
164
- when :default
165
- superclass_base_class = if superclass.respond_to?(:trace_class_for)
166
- superclass.trace_class_for(mode)
167
- else
168
- GraphQL::Tracing::Trace
169
- end
170
- Class.new(superclass_base_class)
171
- when :default_backtrace
172
- schema_base_class = trace_class_for(:default)
173
- Class.new(schema_base_class) do
174
- include(GraphQL::Backtrace::Trace)
175
- end
176
- else
177
- mods = trace_modules_for(mode)
178
- Class.new(trace_class_for(:default)) do
179
- mods.any? && include(*mods)
180
- end
181
- end
182
- end
173
+ def trace_class_for(mode, build: false)
174
+ own_trace_modes[mode] ||
175
+ (superclass.respond_to?(:trace_class_for) ? superclass.trace_class_for(mode) : nil)
183
176
  end
184
177
 
185
178
  # Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
186
- # `:default` is used when no `trace_mode: ...` is requested.
179
+ # {default_trace_mode} is used when no `trace_mode: ...` is requested.
180
+ #
181
+ # When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
182
+ # unless `trace_mode` is explicitly given. (This class will not recieve any default trace modules.)
183
+ #
184
+ # Subclasses of the schema will use `trace_class` as a base class for this mode and those
185
+ # subclass also will _not_ receive default tracing modules.
186
+ #
187
187
  # @param mode_name [Symbol]
188
188
  # @param trace_class [Class] subclass of GraphQL::Tracing::Trace
189
189
  # @return void
190
190
  def trace_mode(mode_name, trace_class)
191
- @trace_modes ||= {}
192
- @trace_modes[mode_name] = trace_class
191
+ own_trace_modes[mode_name] = trace_class
193
192
  nil
194
193
  end
195
194
 
195
+ def own_trace_modes
196
+ @own_trace_modes ||= {}
197
+ end
198
+
199
+ module DefaultTraceClass
200
+ end
201
+
202
+ private_constant :DefaultTraceClass
203
+
204
+ def build_trace_mode(mode)
205
+ case mode
206
+ when :default
207
+ # Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
208
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
209
+ Class.new(base_class) do
210
+ include DefaultTraceClass
211
+ end
212
+ when :default_backtrace
213
+ schema_base_class = trace_class_for(:default)
214
+ Class.new(schema_base_class) do
215
+ include(GraphQL::Backtrace::Trace)
216
+ end
217
+ else
218
+ # First, see if the superclass has a custom-defined class for this.
219
+ # Then, if it doesn't, use this class's default trace
220
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default)
221
+ # Prepare the default trace class if it hasn't been initialized yet
222
+ base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
223
+ mods = trace_modules_for(mode)
224
+ if base_class < DefaultTraceClass
225
+ mods = trace_modules_for(:default) + mods
226
+ end
227
+ Class.new(base_class) do
228
+ mods.any? && include(*mods)
229
+ end
230
+ end
231
+ end
232
+
196
233
  def own_trace_modules
197
234
  @own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
198
235
  end
@@ -703,9 +740,10 @@ module GraphQL
703
740
 
704
741
  attr_writer :max_depth
705
742
 
706
- def max_depth(new_max_depth = nil)
743
+ def max_depth(new_max_depth = nil, count_introspection_fields: true)
707
744
  if new_max_depth
708
745
  @max_depth = new_max_depth
746
+ @count_introspection_fields = count_introspection_fields
709
747
  elsif defined?(@max_depth)
710
748
  @max_depth
711
749
  else
@@ -713,6 +751,14 @@ module GraphQL
713
751
  end
714
752
  end
715
753
 
754
+ def count_introspection_fields
755
+ if defined?(@count_introspection_fields)
756
+ @count_introspection_fields
757
+ else
758
+ find_inherited_value(:count_introspection_fields, true)
759
+ end
760
+ end
761
+
716
762
  def disable_introspection_entry_points
717
763
  @disable_introspection_entry_points = true
718
764
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
@@ -875,8 +921,14 @@ module GraphQL
875
921
 
876
922
  def inherited(child_class)
877
923
  if self == GraphQL::Schema
924
+ child_class.own_trace_modes[:default] = child_class.build_trace_mode(:default)
878
925
  child_class.directives(default_directives.values)
879
926
  end
927
+ # Make sure the child class has these built out, so that
928
+ # subclasses can be modified by later calls to `trace_with`
929
+ own_trace_modes.each do |name, _class|
930
+ child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
931
+ end
880
932
  child_class.singleton_class.prepend(ResolveTypeWithType)
881
933
  super
882
934
  end
@@ -999,7 +1051,8 @@ module GraphQL
999
1051
  end
1000
1052
 
1001
1053
  def tracer(new_tracer)
1002
- if !(trace_class_for(:default) < GraphQL::Tracing::CallLegacyTracers)
1054
+ default_trace = trace_class_for(:default)
1055
+ if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
1003
1056
  trace_with(GraphQL::Tracing::CallLegacyTracers)
1004
1057
  end
1005
1058
 
@@ -1021,10 +1074,20 @@ module GraphQL
1021
1074
  if mode.is_a?(Array)
1022
1075
  mode.each { |m| trace_with(trace_mod, mode: m, **options) }
1023
1076
  else
1024
- tc = trace_class_for(mode)
1077
+ tc = own_trace_modes[mode] ||= build_trace_mode(mode)
1025
1078
  tc.include(trace_mod)
1026
- if mode != :default
1027
- own_trace_modules[mode] << trace_mod
1079
+ own_trace_modules[mode] << trace_mod
1080
+
1081
+ if mode == :default
1082
+ # This module is being added as a default tracer. If any other mode classes
1083
+ # have already been created, but get their default behavior from a superclass,
1084
+ # Then mix this into this schema's subclass.
1085
+ # (But don't mix it into mode classes that aren't default-based.)
1086
+ own_trace_modes.each do |other_mode_name, other_mode_class|
1087
+ if other_mode_class < DefaultTraceClass && !(other_mode_class < trace_mod)
1088
+ other_mode_class.include(trace_mod)
1089
+ end
1090
+ end
1028
1091
  end
1029
1092
  t_opts = trace_options_for(mode)
1030
1093
  t_opts.merge!(options)
@@ -1047,6 +1110,8 @@ module GraphQL
1047
1110
 
1048
1111
  # Create a trace instance which will include the trace modules specified for the optional mode.
1049
1112
  #
1113
+ # If no `mode:` is given, then {default_trace_mode} will be used.
1114
+ #
1050
1115
  # @param mode [Symbol] Trace modules for this trade mode will be included
1051
1116
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1052
1117
  # @return [Tracing::Trace]
@@ -1057,14 +1122,19 @@ module GraphQL
1057
1122
  trace_mode = if mode
1058
1123
  mode
1059
1124
  elsif target && target.context[:backtrace]
1060
- :default_backtrace
1125
+ if default_trace_mode != :default
1126
+ raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1127
+ else
1128
+ own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1129
+ :default_backtrace
1130
+ end
1061
1131
  else
1062
- :default
1132
+ default_trace_mode
1063
1133
  end
1064
1134
 
1065
1135
  base_trace_options = trace_options_for(trace_mode)
1066
1136
  trace_options = base_trace_options.merge(options)
1067
- trace_class_for_mode = trace_class_for(trace_mode)
1137
+ trace_class_for_mode = trace_class_for(trace_mode) || raise(ArgumentError, "#{self} has no trace class for mode: #{trace_mode.inspect}")
1068
1138
  trace_class_for_mode.new(**trace_options)
1069
1139
  end
1070
1140
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.1.1"
3
+ VERSION = "2.1.2"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-02 00:00:00.000000000 Z
11
+ date: 2023-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -619,14 +619,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
619
619
  requirements:
620
620
  - - ">="
621
621
  - !ruby/object:Gem::Version
622
- version: 2.4.0
622
+ version: 2.7.0
623
623
  required_rubygems_version: !ruby/object:Gem::Requirement
624
624
  requirements:
625
625
  - - ">="
626
626
  - !ruby/object:Gem::Version
627
627
  version: '0'
628
628
  requirements: []
629
- rubygems_version: 3.4.10
629
+ rubygems_version: 3.5.0.dev
630
630
  signing_key:
631
631
  specification_version: 4
632
632
  summary: A GraphQL language and runtime for Ruby