graphql-client 0.16.0 → 0.17.0

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: 6810301cbdd84362dc4b8584e24deec3aec4248f4471734748cf1a33feaa5232
4
- data.tar.gz: bb68c48d70c276c0443da6620506b37dbe152e20fb931a2ecf3a0cba45050f96
3
+ metadata.gz: c30231d5575e5a6012a4d8f650d69b25820d75d800c6d53a3efa1e6304577e03
4
+ data.tar.gz: c1de7029eb91165c620f05bd864eed6efd60ed36526074bfc444b47cdbff3604
5
5
  SHA512:
6
- metadata.gz: 6b7f114d4330ae5168669d7a663a0afdbd508ba056aa252a206efca9071f558c8749ecfd809ca0677ab9e3a5e95cc90eec3a6bcc2e4020875aab6621d9059dd8
7
- data.tar.gz: b1a24192dc4ee004ae6d546d4d724d0c49da4a17a4fb86a1a7e81d1aefbe16618f32ed27a63a8c2297bd4074ec8dd91794ab057d3826d08ccfefe21d689e7e76
6
+ metadata.gz: 00a451679c6f3d68b748c0f1bed6ed4ad0a3ac796954d19ad09c347ee669ff6d756d57b4390b7153c787d8a97d6f28ebf3eefde99183906159a80bc8a80bb338
7
+ data.tar.gz: ef0a5bae3219ea84c139dabfe4b96eda4621c7da4d9ef9fa752af681d637f43a0871f36c07e629fb96ef1d1368ff0352e2cacf749b08d7c7e1171185f6952e15
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.17.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: 2021-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.8'
33
+ version: '1.10'
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: '1.10'
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.1.6
202
208
  signing_key:
203
209
  specification_version: 4
204
210
  summary: GraphQL Client