graphql-client 0.16.0 → 0.18.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6810301cbdd84362dc4b8584e24deec3aec4248f4471734748cf1a33feaa5232
4
- data.tar.gz: bb68c48d70c276c0443da6620506b37dbe152e20fb931a2ecf3a0cba45050f96
3
+ metadata.gz: 82b89599e02036868b2249fbd3ac2aeb89b6043b19040bdaeaf66f31b5f1b28e
4
+ data.tar.gz: 2e168f61452bd5041aa01dce3a09c99959b629afeb2880ca629d1034f22fb0d9
5
5
  SHA512:
6
- metadata.gz: 6b7f114d4330ae5168669d7a663a0afdbd508ba056aa252a206efca9071f558c8749ecfd809ca0677ab9e3a5e95cc90eec3a6bcc2e4020875aab6621d9059dd8
7
- data.tar.gz: b1a24192dc4ee004ae6d546d4d724d0c49da4a17a4fb86a1a7e81d1aefbe16618f32ed27a63a8c2297bd4074ec8dd91794ab057d3826d08ccfefe21d689e7e76
6
+ metadata.gz: 3f14f21ff57e8dc29dbef6e5fb2ee6d2fb552075986ef57c8f89a3d7ce5b2faa8e2965b20a5b8f7735394b3ee13cdc8e55923bda0dd02fa8625f7e2b824dab7b
7
+ data.tar.gz: ee04576697c258bdc3959366d0cccef720e24eb2358ecbb28c020d912c5b241ac7ba195a8240656c6f825109c23a321ef2449892b4d893e29802c03b6580f557
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # graphql-client [![Gem Version](https://badge.fury.io/rb/graphql-client.svg)](https://badge.fury.io/rb/graphql-client) [![Build Status](https://travis-ci.org/github/graphql-client.svg?branch=master)](https://travis-ci.org/github/graphql-client)
1
+ # graphql-client [![Gem Version](https://badge.fury.io/rb/graphql-client.svg)](https://badge.fury.io/rb/graphql-client) [![CI](https://github.com/github/graphql-client/workflows/CI/badge.svg)](https://github.com/github/graphql-client/actions?query=workflow)
2
2
 
3
3
  GraphQL Client is a Ruby library for declaring, composing and executing GraphQL queries.
4
4
 
@@ -12,6 +12,8 @@ module GraphQL
12
12
 
13
13
  # Enforcements collocated object access best practices.
14
14
  module CollocatedEnforcement
15
+ extend self
16
+
15
17
  # Public: Ignore collocated caller enforcement for the scope of the block.
16
18
  def allow_noncollocated_callers
17
19
  Thread.current[:query_result_caller_location_ignore] = true
@@ -20,6 +22,23 @@ module GraphQL
20
22
  Thread.current[:query_result_caller_location_ignore] = nil
21
23
  end
22
24
 
25
+ def verify_collocated_path(location, path, method = "method")
26
+ return yield if Thread.current[:query_result_caller_location_ignore]
27
+
28
+ if (location.path != path) && !(WHITELISTED_GEM_NAMES.any? { |g| location.path.include?("gems/#{g}") })
29
+ error = NonCollocatedCallerError.new("#{method} was called outside of '#{path}' https://git.io/v1syX")
30
+ error.set_backtrace(caller(2))
31
+ raise error
32
+ end
33
+
34
+ begin
35
+ Thread.current[:query_result_caller_location_ignore] = true
36
+ yield
37
+ ensure
38
+ Thread.current[:query_result_caller_location_ignore] = nil
39
+ end
40
+ end
41
+
23
42
  # Internal: Decorate method with collocated caller enforcement.
24
43
  #
25
44
  # mod - Target Module/Class
@@ -31,21 +50,9 @@ module GraphQL
31
50
  mod.prepend(Module.new do
32
51
  methods.each do |method|
33
52
  define_method(method) do |*args, &block|
34
- return super(*args, &block) if Thread.current[:query_result_caller_location_ignore]
35
-
36
- locations = caller_locations(1, 1)
37
-
38
- if (locations.first.path != path) && !(caller_locations.any? { |cl| WHITELISTED_GEM_NAMES.any? { |g| cl.path.include?("gems/#{g}") } })
39
- error = NonCollocatedCallerError.new("#{method} was called outside of '#{path}' https://git.io/v1syX")
40
- error.set_backtrace(caller(1))
41
- raise error
42
- end
43
-
44
- begin
45
- Thread.current[:query_result_caller_location_ignore] = true
53
+ location = caller_locations(1, 1)[0]
54
+ CollocatedEnforcement.verify_collocated_path(location, path, method) do
46
55
  super(*args, &block)
47
- ensure
48
- Thread.current[:query_result_caller_location_ignore] = nil
49
56
  end
50
57
  end
51
58
  end
@@ -115,9 +115,24 @@ module GraphQL
115
115
  else
116
116
  cast_object(obj)
117
117
  end
118
+ when GraphQL::Client::Schema::ObjectType::WithDefinition
119
+ case obj
120
+ when schema_class.klass
121
+ if obj._definer == schema_class
122
+ obj
123
+ else
124
+ cast_object(obj)
125
+ end
126
+ when nil
127
+ nil
128
+ when Hash
129
+ schema_class.new(obj, errors)
130
+ else
131
+ cast_object(obj)
132
+ end
118
133
  when GraphQL::Client::Schema::ObjectType
119
134
  case obj
120
- when NilClass, schema_class
135
+ when nil, schema_class
121
136
  obj
122
137
  when Hash
123
138
  schema_class.new(obj, errors)
@@ -144,8 +159,8 @@ module GraphQL
144
159
 
145
160
  def cast_object(obj)
146
161
  if obj.class.is_a?(GraphQL::Client::Schema::ObjectType)
147
- unless obj.class._spreads.include?(definition_node.name)
148
- raise TypeError, "#{definition_node.name} is not included in #{obj.class.source_definition.name}"
162
+ unless obj._spreads.include?(definition_node.name)
163
+ raise TypeError, "#{definition_node.name} is not included in #{obj.source_definition.name}"
149
164
  end
150
165
  schema_class.cast(obj.to_h, obj.errors)
151
166
  else
@@ -7,7 +7,7 @@ module GraphQL
7
7
  class Client
8
8
  # Public: Basic HTTP network adapter.
9
9
  #
10
- # GraphQL::Client::Client.new(
10
+ # GraphQL::Client.new(
11
11
  # execute: GraphQL::Client::HTTP.new("http://graphql-swapi.parseapp.com/")
12
12
  # )
13
13
  #
@@ -21,7 +21,7 @@ module GraphQL
21
21
  end
22
22
 
23
23
  def define_class(definition, ast_nodes)
24
- possible_type_names = definition.client.schema.possible_types(type).map(&:graphql_name)
24
+ possible_type_names = definition.client.possible_types(type).map(&:graphql_name)
25
25
  possible_types = possible_type_names.map { |concrete_type_name|
26
26
  schema_module.get_class(concrete_type_name).define_class(definition, ast_nodes)
27
27
  }
@@ -16,6 +16,53 @@ module GraphQL
16
16
 
17
17
  define_singleton_method(:type) { type }
18
18
  define_singleton_method(:fields) { fields }
19
+
20
+ const_set(:READERS, {})
21
+ const_set(:PREDICATES, {})
22
+ end
23
+ end
24
+
25
+ class WithDefinition
26
+ include BaseType
27
+ include ObjectType
28
+
29
+ EMPTY_SET = Set.new.freeze
30
+
31
+ attr_reader :klass, :defined_fields, :definition
32
+
33
+ def type
34
+ @klass.type
35
+ end
36
+
37
+ def fields
38
+ @klass.fields
39
+ end
40
+
41
+ def spreads
42
+ if defined?(@spreads)
43
+ @spreads
44
+ else
45
+ EMPTY_SET
46
+ end
47
+ end
48
+
49
+ def initialize(klass, defined_fields, definition, spreads)
50
+ @klass = klass
51
+ @defined_fields = defined_fields.map do |k, v|
52
+ [-k.to_s, v]
53
+ end.to_h
54
+ @definition = definition
55
+ @spreads = spreads unless spreads.empty?
56
+
57
+ @defined_fields.keys.each do |attr|
58
+ name = ActiveSupport::Inflector.underscore(attr)
59
+ @klass::READERS[:"#{name}"] ||= attr
60
+ @klass::PREDICATES[:"#{name}?"] ||= attr
61
+ end
62
+ end
63
+
64
+ def new(data = {}, errors = Errors.new)
65
+ @klass.new(data, errors, self)
19
66
  end
20
67
  end
21
68
 
@@ -46,56 +93,9 @@ module GraphQL
46
93
  field_classes[result_name.to_sym] = schema_module.define_class(definition, field_ast_nodes, field_return_type)
47
94
  end
48
95
 
49
- klass = Class.new(self)
50
- klass.define_fields(field_classes)
51
- klass.instance_variable_set(:@source_definition, definition)
52
- klass.instance_variable_set(:@_spreads, definition.indexes[:spreads][ast_nodes.first])
53
-
54
- if definition.client.enforce_collocated_callers
55
- keys = field_classes.keys.map { |key| ActiveSupport::Inflector.underscore(key) }
56
- Client.enforce_collocated_callers(klass, keys, definition.source_location[0])
57
- end
58
-
59
- klass
60
- end
61
-
62
- PREDICATE_CACHE = Hash.new { |h, name|
63
- h[name] = -> { @data[name] ? true : false }
64
- }
96
+ spreads = definition.indexes[:spreads][ast_nodes.first]
65
97
 
66
- METHOD_CACHE = Hash.new { |h, key|
67
- h[key] = -> {
68
- name = key.to_s
69
- type = self.class::FIELDS[key]
70
- @casted_data.fetch(name) do
71
- @casted_data[name] = type.cast(@data[name], @errors.filter_by_path(name))
72
- end
73
- }
74
- }
75
-
76
- MODULE_CACHE = Hash.new do |h, fields|
77
- h[fields] = Module.new do
78
- fields.each do |name|
79
- GraphQL::Client::Schema::ObjectType.define_cached_field(name, self)
80
- end
81
- end
82
- end
83
-
84
- FIELDS_CACHE = Hash.new { |h, k| h[k] = k }
85
-
86
- def define_fields(fields)
87
- const_set :FIELDS, FIELDS_CACHE[fields]
88
- mod = MODULE_CACHE[fields.keys.sort]
89
- include mod
90
- end
91
-
92
- def self.define_cached_field(name, ctx)
93
- key = name
94
- name = -name.to_s
95
- method_name = ActiveSupport::Inflector.underscore(name)
96
-
97
- ctx.send(:define_method, method_name, &METHOD_CACHE[key])
98
- ctx.send(:define_method, "#{method_name}?", &PREDICATE_CACHE[name])
98
+ WithDefinition.new(self, field_classes, definition, spreads)
99
99
  end
100
100
 
101
101
  def define_field(name, type)
@@ -134,9 +134,8 @@ module GraphQL
134
134
  continue_selection = if selected_ast_node.type.nil?
135
135
  true
136
136
  else
137
- schema = definition.client.schema
138
137
  type_condition = definition.client.get_type(selected_ast_node.type.name)
139
- applicable_types = schema.possible_types(type_condition)
138
+ applicable_types = definition.client.possible_types(type_condition)
140
139
  # continue if this object type is one of the types matching the fragment condition
141
140
  applicable_types.include?(type)
142
141
  end
@@ -150,10 +149,8 @@ module GraphQL
150
149
  fragment_definition = definition.document.definitions.find do |defn|
151
150
  defn.is_a?(GraphQL::Language::Nodes::FragmentDefinition) && defn.name == selected_ast_node.name
152
151
  end
153
-
154
- schema = definition.client.schema
155
152
  type_condition = definition.client.get_type(fragment_definition.type.name)
156
- applicable_types = schema.possible_types(type_condition)
153
+ applicable_types = definition.client.possible_types(type_condition)
157
154
  # continue if this object type is one of the types matching the fragment condition
158
155
  continue_selection = applicable_types.include?(type)
159
156
 
@@ -177,17 +174,16 @@ module GraphQL
177
174
  end
178
175
 
179
176
  class ObjectClass
180
- module ClassMethods
181
- attr_reader :source_definition
182
- attr_reader :_spreads
183
- end
184
-
185
- extend ClassMethods
186
-
187
- def initialize(data = {}, errors = Errors.new)
177
+ def initialize(data = {}, errors = Errors.new, definer = nil)
188
178
  @data = data
189
179
  @casted_data = {}
190
180
  @errors = errors
181
+
182
+ # If we are not provided a definition, we can use this empty default
183
+ definer ||= ObjectType::WithDefinition.new(self.class, {}, nil, [])
184
+
185
+ @definer = definer
186
+ @enforce_collocated_callers = source_definition && source_definition.client.enforce_collocated_callers
191
187
  end
192
188
 
193
189
  # Public: Returns the raw response data
@@ -197,42 +193,85 @@ module GraphQL
197
193
  @data
198
194
  end
199
195
 
200
- # Public: Return errors associated with data.
201
- #
202
- # Returns Errors collection.
203
- attr_reader :errors
196
+ def _definer
197
+ @definer
198
+ end
204
199
 
205
- def method_missing(*args)
206
- super
207
- rescue NoMethodError => e
208
- type = self.class.type
200
+ def _spreads
201
+ @definer.spreads
202
+ end
209
203
 
210
- if ActiveSupport::Inflector.underscore(e.name.to_s) != e.name.to_s
211
- raise e
212
- end
204
+ def source_definition
205
+ @definer.definition
206
+ end
213
207
 
214
- all_fields = type.respond_to?(:all_fields) ? type.all_fields : type.fields.values
215
- field = all_fields.find do |f|
216
- f.name == e.name.to_s || ActiveSupport::Inflector.underscore(f.name) == e.name.to_s
208
+ def respond_to_missing?(name, priv)
209
+ if (attr = self.class::READERS[name]) || (attr = self.class::PREDICATES[name])
210
+ @definer.defined_fields.key?(attr) || super
211
+ else
212
+ super
217
213
  end
214
+ end
218
215
 
219
- unless field
220
- raise UnimplementedFieldError, "undefined field `#{e.name}' on #{type.graphql_name} type. https://git.io/v1y3m"
216
+ # Public: Return errors associated with data.
217
+ #
218
+ # It's possible to define "errors" as a field. Ideally this shouldn't
219
+ # happen, but if it does we should prefer the field rather than the
220
+ # builtin error type.
221
+ #
222
+ # Returns Errors collection.
223
+ def errors
224
+ if type = @definer.defined_fields["errors"]
225
+ read_attribute("errors", type)
226
+ else
227
+ @errors
221
228
  end
229
+ end
222
230
 
223
- if @data.key?(field.name)
224
- error_class = ImplicitlyFetchedFieldError
225
- message = "implicitly fetched field `#{field.name}' on #{type} type. https://git.io/v1yGL"
231
+ def method_missing(name, *args)
232
+ if (attr = self.class::READERS[name]) && (type = @definer.defined_fields[attr])
233
+ if @enforce_collocated_callers
234
+ verify_collocated_path do
235
+ read_attribute(attr, type)
236
+ end
237
+ else
238
+ read_attribute(attr, type)
239
+ end
240
+ elsif (attr = self.class::PREDICATES[name]) && @definer.defined_fields[attr]
241
+ has_attribute?(attr)
226
242
  else
227
- error_class = UnfetchedFieldError
228
- message = "unfetched field `#{field.name}' on #{type} type. https://git.io/v1y3U"
229
- end
243
+ begin
244
+ super
245
+ rescue NoMethodError => e
246
+ type = self.class.type
230
247
 
231
- raise error_class, message
248
+ if ActiveSupport::Inflector.underscore(e.name.to_s) != e.name.to_s
249
+ raise e
250
+ end
251
+
252
+ all_fields = type.respond_to?(:all_fields) ? type.all_fields : type.fields.values
253
+ field = all_fields.find do |f|
254
+ f.name == e.name.to_s || ActiveSupport::Inflector.underscore(f.name) == e.name.to_s
255
+ end
256
+
257
+ unless field
258
+ raise UnimplementedFieldError, "undefined field `#{e.name}' on #{type.graphql_name} type. https://git.io/v1y3m"
259
+ end
260
+
261
+ if @data.key?(field.name)
262
+ raise ImplicitlyFetchedFieldError, "implicitly fetched field `#{field.name}' on #{type} type. https://git.io/v1yGL"
263
+ else
264
+ raise UnfetchedFieldError, "unfetched field `#{field.name}' on #{type} type. https://git.io/v1y3U"
265
+ end
266
+ end
267
+ end
232
268
  end
233
269
 
234
270
  def inspect
235
- parent = self.class.ancestors.select { |m| m.is_a?(ObjectType) }.last
271
+ parent = self.class
272
+ until parent.superclass == ObjectClass
273
+ parent = parent.superclass
274
+ end
236
275
 
237
276
  ivars = @data.map { |key, value|
238
277
  if value.is_a?(Hash) || value.is_a?(Array)
@@ -247,6 +286,26 @@ module GraphQL
247
286
  buf << ">"
248
287
  buf
249
288
  end
289
+
290
+ private
291
+
292
+ def verify_collocated_path
293
+ location = caller_locations(2, 1)[0]
294
+
295
+ CollocatedEnforcement.verify_collocated_path(location, source_definition.source_location[0]) do
296
+ yield
297
+ end
298
+ end
299
+
300
+ def read_attribute(attr, type)
301
+ @casted_data.fetch(attr) do
302
+ @casted_data[attr] = type.cast(@data[attr], @errors.filter_by_path(attr))
303
+ end
304
+ end
305
+
306
+ def has_attribute?(attr)
307
+ !!@data[attr]
308
+ end
250
309
  end
251
310
  end
252
311
  end
@@ -21,7 +21,7 @@ module GraphQL
21
21
  end
22
22
 
23
23
  def define_class(definition, ast_nodes)
24
- possible_type_names = definition.client.schema.possible_types(type).map(&:graphql_name)
24
+ possible_type_names = definition.client.possible_types(type).map(&:graphql_name)
25
25
  possible_types = possible_type_names.map { |concrete_type_name|
26
26
  schema_module.get_class(concrete_type_name).define_class(definition, ast_nodes)
27
27
  }
@@ -133,7 +133,7 @@ module GraphQL
133
133
 
134
134
  remove_const(name) if placeholder
135
135
  const_set(name, mod)
136
- mod.unloadable
136
+ mod.unloadable if mod.respond_to?(:unloadable)
137
137
  mod
138
138
  end
139
139
 
@@ -49,12 +49,12 @@ module GraphQL
49
49
  when GraphQL::Schema, Class
50
50
  schema
51
51
  when Hash
52
- GraphQL::Schema::Loader.load(schema)
52
+ GraphQL::Schema.from_introspection(schema)
53
53
  when String
54
54
  if schema.end_with?(".json") && File.exist?(schema)
55
55
  load_schema(File.read(schema))
56
56
  elsif schema =~ /\A\s*{/
57
- load_schema(JSON.parse(schema))
57
+ load_schema(JSON.parse(schema, freeze: true))
58
58
  end
59
59
  else
60
60
  if schema.respond_to?(:execute)
@@ -97,10 +97,31 @@ module GraphQL
97
97
  @document_tracking_enabled = false
98
98
  @allow_dynamic_queries = false
99
99
  @enforce_collocated_callers = enforce_collocated_callers
100
-
100
+ if schema.is_a?(Class)
101
+ @possible_types = schema.possible_types
102
+ end
101
103
  @types = Schema.generate(@schema)
102
104
  end
103
105
 
106
+ # A cache of the schema's merged possible types
107
+ # @param type_condition [Class, String] a type definition or type name
108
+ def possible_types(type_condition = nil)
109
+ if type_condition
110
+ if defined?(@possible_types)
111
+ if type_condition.respond_to?(:graphql_name)
112
+ type_condition = type_condition.graphql_name
113
+ end
114
+ @possible_types[type_condition]
115
+ else
116
+ @schema.possible_types(type_condition)
117
+ end
118
+ elsif defined?(@possible_types)
119
+ @possible_types
120
+ else
121
+ @schema.possible_types(type_condition)
122
+ end
123
+ end
124
+
104
125
  def parse(str, filename = nil, lineno = nil)
105
126
  if filename.nil? && lineno.nil?
106
127
  location = caller_locations(1, 1).first
@@ -135,11 +156,7 @@ module GraphQL
135
156
  # which corresponds to the spread.
136
157
  # We depend on ActiveSupport to either find the already-loaded
137
158
  # constant, or to load the constant by name
138
- begin
139
- fragment = ActiveSupport::Inflector.constantize(const_name)
140
- rescue NameError
141
- fragment = nil
142
- end
159
+ fragment = ActiveSupport::Inflector.safe_constantize(const_name)
143
160
 
144
161
  case fragment
145
162
  when FragmentDefinition
@@ -173,12 +190,8 @@ module GraphQL
173
190
 
174
191
  doc.definitions.each do |node|
175
192
  if node.name.nil?
176
- if node.respond_to?(:merge) # GraphQL 1.9 +
177
- node_with_name = node.merge(name: "__anonymous__")
178
- doc = doc.replace_child(node, node_with_name)
179
- else
180
- node.name = "__anonymous__"
181
- end
193
+ node_with_name = node.merge(name: "__anonymous__")
194
+ doc = doc.replace_child(node, node_with_name)
182
195
  end
183
196
  end
184
197
 
@@ -200,37 +213,13 @@ module GraphQL
200
213
  raise error
201
214
  end
202
215
 
203
- definitions = {}
204
- doc.definitions.each do |node|
205
- sliced_document = Language::DefinitionSlice.slice(document_dependencies, node.name)
206
- definition = Definition.for(
207
- client: self,
208
- ast_node: node,
209
- document: sliced_document,
210
- source_document: doc,
211
- source_location: source_location
212
- )
213
- definitions[node.name] = definition
214
- end
216
+ definitions = sliced_definitions(document_dependencies, doc, source_location: source_location)
215
217
 
216
- if @document.respond_to?(:merge) # GraphQL 1.9+
217
- visitor = RenameNodeVisitor.new(document_dependencies, definitions: definitions)
218
- visitor.visit
219
- else
220
- name_hook = RenameNodeHook.new(definitions)
221
- visitor = Language::Visitor.new(document_dependencies)
222
- visitor[Language::Nodes::FragmentDefinition].leave << name_hook.method(:rename_node)
223
- visitor[Language::Nodes::OperationDefinition].leave << name_hook.method(:rename_node)
224
- visitor[Language::Nodes::FragmentSpread].leave << name_hook.method(:rename_node)
225
- visitor.visit
226
- end
218
+ visitor = RenameNodeVisitor.new(document_dependencies, definitions: definitions)
219
+ visitor.visit
227
220
 
228
221
  if document_tracking_enabled
229
- if @document.respond_to?(:merge) # GraphQL 1.9+
230
- @document = @document.merge(definitions: @document.definitions + doc.definitions)
231
- else
232
- @document.definitions.concat(doc.definitions)
233
- end
222
+ @document = @document.merge(definitions: @document.definitions + doc.definitions)
234
223
  end
235
224
 
236
225
  if definitions["__anonymous__"]
@@ -276,27 +265,9 @@ module GraphQL
276
265
  end
277
266
  end
278
267
 
279
- class RenameNodeHook
280
- def initialize(definitions)
281
- @definitions = definitions
282
- end
283
-
284
- def rename_node(node, _parent)
285
- definition = @definitions[node.name]
286
- if definition
287
- node.extend(LazyName)
288
- node._definition = definition
289
- end
290
- end
291
- end
292
-
293
268
  # Public: A wrapper to use the more-efficient `.get_type` when it's available from GraphQL-Ruby (1.10+)
294
269
  def get_type(type_name)
295
- if @schema.respond_to?(:get_type)
296
- @schema.get_type(type_name)
297
- else
298
- @schema.types[type_name]
299
- end
270
+ @schema.get_type(type_name)
300
271
  end
301
272
 
302
273
  # Public: Create operation definition from a fragment definition.
@@ -424,6 +395,44 @@ module GraphQL
424
395
 
425
396
  private
426
397
 
398
+ def sliced_definitions(document_dependencies, doc, source_location:)
399
+ dependencies = document_dependencies.definitions.map do |node|
400
+ [node.name, find_definition_dependencies(node)]
401
+ end.to_h
402
+
403
+ doc.definitions.map do |node|
404
+ deps = Set.new
405
+ definitions = document_dependencies.definitions.map { |x| [x.name, x] }.to_h
406
+
407
+ queue = [node.name]
408
+ while name = queue.shift
409
+ next if deps.include?(name)
410
+ deps.add(name)
411
+ queue.concat dependencies[name]
412
+ end
413
+
414
+ definitions = document_dependencies.definitions.select { |x| deps.include?(x.name) }
415
+ sliced_document = Language::Nodes::Document.new(definitions: definitions)
416
+ definition = Definition.for(
417
+ client: self,
418
+ ast_node: node,
419
+ document: sliced_document,
420
+ source_document: doc,
421
+ source_location: source_location
422
+ )
423
+
424
+ [node.name, definition]
425
+ end.to_h
426
+ end
427
+
428
+ def find_definition_dependencies(node)
429
+ names = []
430
+ visitor = Language::Visitor.new(node)
431
+ visitor[Language::Nodes::FragmentSpread] << -> (node, parent) { names << node.name }
432
+ visitor.visit
433
+ names.uniq
434
+ end
435
+
427
436
  def deep_freeze_json_object(obj)
428
437
  case obj
429
438
  when String
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-09 00:00:00.000000000 Z
11
+ date: 2022-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: graphql
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.8'
33
+ version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '1.8'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: actionpack
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -115,6 +115,9 @@ dependencies:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
117
  version: '0.10'
118
+ - - "<="
119
+ - !ruby/object:Gem::Version
120
+ version: 0.16.0
118
121
  type: :development
119
122
  prerelease: false
120
123
  version_requirements: !ruby/object:Gem::Requirement
@@ -122,6 +125,9 @@ dependencies:
122
125
  - - "~>"
123
126
  - !ruby/object:Gem::Version
124
127
  version: '0.10'
128
+ - - "<="
129
+ - !ruby/object:Gem::Version
130
+ version: 0.16.0
125
131
  - !ruby/object:Gem::Dependency
126
132
  name: rubocop
127
133
  requirement: !ruby/object:Gem::Requirement
@@ -198,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
204
  - !ruby/object:Gem::Version
199
205
  version: '0'
200
206
  requirements: []
201
- rubygems_version: 3.0.3
207
+ rubygems_version: 3.2.9
202
208
  signing_key:
203
209
  specification_version: 4
204
210
  summary: GraphQL Client