graphql-client 0.9.0 → 0.10.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.
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql/client/schema/base_type"
4
+
5
+ module GraphQL
6
+ class Client
7
+ module Schema
8
+ class ScalarType
9
+ include BaseType
10
+
11
+ # Internal: Construct type wrapper from another GraphQL::BaseType.
12
+ #
13
+ # type - GraphQL::BaseType instance
14
+ def initialize(type)
15
+ unless type.is_a?(GraphQL::ScalarType)
16
+ raise "expected type to be a GraphQL::ScalarType, but was #{type.class}"
17
+ end
18
+
19
+ @type = type
20
+ end
21
+
22
+ def define_class(definition, irep_node)
23
+ self
24
+ end
25
+
26
+ # Internal: Cast raw JSON value to Ruby scalar object.
27
+ #
28
+ # value - JSON value
29
+ # errors - Errors instance
30
+ #
31
+ # Returns casted Object.
32
+ def cast(value, _errors = nil)
33
+ case value
34
+ when NilClass
35
+ nil
36
+ else
37
+ if type.respond_to?(:coerce_isolated_input)
38
+ type.coerce_isolated_input(value)
39
+ else
40
+ type.coerce_input(value)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql/client/schema/base_type"
4
+
5
+ module GraphQL
6
+ class Client
7
+ module Schema
8
+ class SkipDirective
9
+ include BaseType
10
+
11
+ # Internal: Construct list wrapper from other BaseType.
12
+ #
13
+ # of_klass - BaseType instance
14
+ def initialize(of_klass)
15
+ unless of_klass.is_a?(BaseType)
16
+ raise TypeError, "expected #{of_klass.inspect} to be a #{BaseType}"
17
+ end
18
+
19
+ @of_klass = of_klass
20
+ end
21
+
22
+ # Internal: Get wrapped klass.
23
+ #
24
+ # Returns BaseType instance.
25
+ attr_reader :of_klass
26
+
27
+ # Internal: Cast JSON value to wrapped value.
28
+ #
29
+ # values - JSON value
30
+ # errors - Errors instance
31
+ #
32
+ # Returns List instance or nil.
33
+ def cast(value, errors)
34
+ case value
35
+ when NilClass
36
+ nil
37
+ else
38
+ of_klass.cast(value, errors)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql/client/schema/possible_types"
4
+
5
+ module GraphQL
6
+ class Client
7
+ module Schema
8
+ class UnionType < Module
9
+ include BaseType
10
+
11
+ def initialize(type)
12
+ unless type.is_a?(GraphQL::UnionType)
13
+ raise "expected type to be a GraphQL::UnionType, but was #{type.class}"
14
+ end
15
+
16
+ @type = type
17
+ end
18
+
19
+ def new(types)
20
+ PossibleTypes.new(type, types)
21
+ end
22
+
23
+ def define_class(definition, irep_node)
24
+ new(irep_node.typed_children.keys.map { |ctype|
25
+ schema_module.const_get(ctype.name).define_class(definition, irep_node)
26
+ })
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
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.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-11 00:00:00.000000000 Z
11
+ date: 2017-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -173,10 +173,21 @@ files:
173
173
  - lib/graphql/client/list.rb
174
174
  - lib/graphql/client/log_subscriber.rb
175
175
  - lib/graphql/client/operation_definition.rb
176
- - lib/graphql/client/query_result.rb
177
176
  - lib/graphql/client/query_typename.rb
178
177
  - lib/graphql/client/railtie.rb
179
178
  - lib/graphql/client/response.rb
179
+ - lib/graphql/client/schema.rb
180
+ - lib/graphql/client/schema/base_type.rb
181
+ - lib/graphql/client/schema/enum_type.rb
182
+ - lib/graphql/client/schema/include_directive.rb
183
+ - lib/graphql/client/schema/interface_type.rb
184
+ - lib/graphql/client/schema/list_type.rb
185
+ - lib/graphql/client/schema/non_null_type.rb
186
+ - lib/graphql/client/schema/object_type.rb
187
+ - lib/graphql/client/schema/possible_types.rb
188
+ - lib/graphql/client/schema/scalar_type.rb
189
+ - lib/graphql/client/schema/skip_directive.rb
190
+ - lib/graphql/client/schema/union_type.rb
180
191
  - lib/graphql/client/view_module.rb
181
192
  - lib/graphql/language/nodes/deep_freeze_ext.rb
182
193
  - lib/rubocop/cop/graphql/heredoc.rb
@@ -1,402 +0,0 @@
1
- # frozen_string_literal: true
2
- require "active_support/inflector"
3
- require "graphql"
4
- require "graphql/client/errors"
5
- require "graphql/client/list"
6
- require "set"
7
-
8
- module GraphQL
9
- class Client
10
- # A QueryResult struct wraps data returned from a GraphQL response.
11
- #
12
- # Wrapping the JSON-like Hash allows access with nice Ruby accessor methods
13
- # rather than using `obj["key"]` access.
14
- #
15
- # Wrappers also limit field visibility to fragment definitions.
16
- class QueryResult
17
- # Internal: Get QueryResult class for result of query.
18
- #
19
- # Returns subclass of QueryResult or nil.
20
- def self.wrap(source_definition, node, type, name: nil)
21
- case type
22
- when GraphQL::NonNullType
23
- NonNullWrapper.new(wrap(source_definition, node, type.of_type, name: name))
24
- when GraphQL::ListType
25
- ListWrapper.new(wrap(source_definition, node, type.of_type, name: name))
26
- when GraphQL::ScalarType
27
- ScalarWrapper.new(type)
28
- when GraphQL::EnumType
29
- EnumWrapper.new(type)
30
- # when GraphQL::UnionType
31
- # types = {}
32
- #
33
- # node.selections.each do |selection|
34
- # case selection
35
- # when Language::Nodes::InlineFragment
36
- # selection_type = source_definition.document_types[selection]
37
- # selection_wrapper = wrap(source_definition, selection, selection_type, name: name)
38
- # if types[selection_type]
39
- # p [:merge, selection_type]
40
- # types[selection_type.name] |= selection_wrapper
41
- # else
42
- # types[selection_type.name] = selection_wrapper
43
- # end
44
- # end
45
- # end
46
- #
47
- # UnionWrapper.new(types)
48
- when GraphQL::ObjectType, GraphQL::InterfaceType, GraphQL::UnionType
49
- fields = {}
50
-
51
- node.selections.each do |selection|
52
- case selection
53
- when Language::Nodes::FragmentSpread
54
- nil
55
- when Language::Nodes::Field
56
- field_name = selection.alias || selection.name
57
- selection_type = source_definition.document_types[selection]
58
- selection_type = GraphQL::STRING_TYPE if field_name == "__typename"
59
- field_klass = wrap(source_definition, selection, selection_type, name: "#{name}[:#{field_name}]")
60
- fields[field_name] ? fields[field_name] |= field_klass : fields[field_name] = field_klass
61
- when Language::Nodes::InlineFragment
62
- selection_type = source_definition.document_types[selection]
63
- wrap(source_definition, selection, selection_type, name: name).fields.each do |fragment_name, klass|
64
- fields[fragment_name.to_s] ? fields[fragment_name.to_s] |= klass : fields[fragment_name.to_s] = klass
65
- end
66
- end
67
- end
68
-
69
- define(name: name, type: type, source_definition: source_definition, source_node: node, fields: fields)
70
- else
71
- raise TypeError, "unexpected #{type.class}"
72
- end
73
- end
74
-
75
- class UnionWrapper
76
- def initialize(possible_types)
77
- @possible_types = possible_types
78
- end
79
-
80
- def cast(value, errors = nil)
81
- typename = value && value["__typename"]
82
- if wrapper = @possible_types[typename]
83
- wrapper.cast(value, errors)
84
- else
85
- raise TypeError, "expected union value to be #{@possible_types.keys.join(", ")}, but was #{typename}"
86
- end
87
- end
88
-
89
- def |(_other)
90
- # XXX: How would union merge?
91
- self
92
- end
93
- end
94
-
95
- class ListWrapper
96
- attr_reader :of_klass
97
-
98
- def initialize(type)
99
- @of_klass = type
100
- end
101
-
102
- def cast(value, errors)
103
- case value
104
- when Array
105
- List.new(value.each_with_index.map { |e, idx|
106
- @of_klass.cast(e, errors.filter_by_path(idx))
107
- }, errors)
108
- when NilClass
109
- nil
110
- else
111
- raise ArgumentError, "expected list value to be an Array, but was #{value.class}"
112
- end
113
- end
114
-
115
- def |(other)
116
- if self.class == other.class
117
- self.of_klass | other.of_klass
118
- else
119
- raise TypeError, "expected other to be a #{self.class}"
120
- end
121
- end
122
- end
123
-
124
- class NonNullWrapper
125
- attr_reader :of_klass
126
-
127
- def initialize(type)
128
- @of_klass = type
129
- end
130
-
131
- def cast(value, errors)
132
- case value
133
- when NilClass
134
- # TODO
135
- # raise ArgumentError, "expected non-nullable value to be present"
136
- nil
137
- else
138
- @of_klass.cast(value, errors)
139
- end
140
- end
141
-
142
- def |(other)
143
- if self.class == other.class
144
- self.of_klass | other.of_klass
145
- else
146
- raise TypeError, "expected other to be a #{self.class}"
147
- end
148
- end
149
- end
150
-
151
- class EnumWrapper
152
- def initialize(type)
153
- @type = type
154
- end
155
-
156
- def cast(value, _errors = nil)
157
- value
158
- end
159
-
160
- def |(_other)
161
- # XXX: How would enums merge?
162
- self
163
- end
164
- end
165
-
166
- # :nodoc:
167
- class ScalarWrapper
168
- def initialize(type)
169
- @type = type
170
- end
171
-
172
- def cast(value, _errors = nil)
173
- if value.is_a? Array
174
- value.map { |item|
175
- if @type.respond_to?(:coerce_isolated_input)
176
- @type.coerce_isolated_input(item)
177
- else
178
- @type.coerce_input(item)
179
- end
180
- }
181
- else
182
- if @type.respond_to?(:coerce_isolated_input)
183
- @type.coerce_isolated_input(value)
184
- else
185
- @type.coerce_input(value)
186
- end
187
- end
188
- end
189
-
190
- def |(_other)
191
- # XXX: How would scalars merge?
192
- self
193
- end
194
- end
195
-
196
- # Internal
197
- def self.define(name:, type:, source_definition:, source_node:, fields: {})
198
- Class.new(self) do
199
- @name = name
200
- @type = type
201
- @source_node = source_node
202
- @source_definition = source_definition
203
- @fields = {}
204
-
205
- field_readers = Set.new
206
-
207
- fields.each do |field, klass|
208
- @fields[field.to_sym] = klass
209
-
210
- send :attr_reader, field
211
- field_readers << field.to_sym
212
-
213
- # Convert GraphQL camelcase to snake case: commitComments -> commit_comments
214
- field_alias = ActiveSupport::Inflector.underscore(field)
215
- send :alias_method, field_alias, field if field != field_alias
216
- field_readers << field_alias.to_sym
217
-
218
- class_eval <<-RUBY, __FILE__, __LINE__
219
- def #{field_alias}?
220
- #{field_alias} ? true : false
221
- end
222
- RUBY
223
- field_readers << "#{field_alias}?".to_sym
224
- end
225
-
226
- assigns = @fields.map do |field, klass|
227
- <<-RUBY
228
- @#{field} = self.class.fields[:#{field}].cast(@data["#{field}"], @errors.filter_by_path("#{field}"))
229
- RUBY
230
- end
231
-
232
- if @type.is_a?(GraphQL::ObjectType)
233
- assigns.unshift "@__typename = self.class.type.name"
234
- end
235
-
236
- class_eval <<-RUBY, __FILE__, __LINE__
237
- def initialize(data, errors = Errors.new)
238
- @data = data
239
- @errors = errors
240
-
241
- #{assigns.join("\n")}
242
- freeze
243
- end
244
- RUBY
245
-
246
- if @source_definition.enforce_collocated_callers
247
- Client.enforce_collocated_callers(self, field_readers, source_definition.source_location[0])
248
- end
249
- end
250
- end
251
-
252
- class << self
253
- attr_reader :type
254
-
255
- attr_reader :source_definition
256
-
257
- attr_reader :source_node
258
-
259
- attr_reader :fields
260
-
261
- def schema
262
- source_definition.schema
263
- end
264
- end
265
-
266
- def self.name
267
- @name || super || GraphQL::Client::QueryResult.name
268
- end
269
-
270
- def self.inspect
271
- "#<#{name} fields=#{@fields.keys.inspect}>"
272
- end
273
-
274
- def self.cast(obj, errors = Errors.new)
275
- case obj
276
- when Hash
277
- new(obj, errors)
278
- when Array
279
- obj.map { |o| new(o, errors) }
280
- when self
281
- obj
282
- when QueryResult
283
- spreads = Set.new(self.spreads(obj.class.source_node).map(&:name))
284
-
285
- unless spreads.include?(source_node.name)
286
- raise TypeError, "#{self.source_definition.name} is not included in #{obj.class.source_definition.name}"
287
- end
288
- cast(obj.to_h, obj.errors)
289
- when NilClass
290
- nil
291
- else
292
- raise TypeError, "expected #{obj.inspect} to be a Hash"
293
- end
294
- end
295
-
296
- # Internal
297
- def self.spreads(node)
298
- node.selections.flat_map do |selection|
299
- case selection
300
- when Language::Nodes::FragmentSpread
301
- selection
302
- when Language::Nodes::InlineFragment
303
- spreads(selection)
304
- else
305
- []
306
- end
307
- end
308
- end
309
-
310
- def self.new(obj, *args)
311
- case obj
312
- when Hash
313
- super
314
- else
315
- cast(obj, *args)
316
- end
317
- end
318
-
319
- def self.|(other)
320
- new_fields = fields.dup
321
- other.fields.each do |name, value|
322
- if new_fields[name]
323
- new_fields[name] |= value
324
- else
325
- new_fields[name] = value
326
- end
327
- end
328
- # TODO: Picking first source node seems error prone
329
- define(name: self.name, type: self.type, source_definition: source_definition, source_node: source_node, fields: new_fields)
330
- end
331
-
332
- # Public: Return errors associated with data.
333
- #
334
- # Returns Errors collection.
335
- attr_reader :errors
336
-
337
- attr_reader :__typename
338
- alias typename __typename
339
-
340
- # Public: Returns the raw response data
341
- #
342
- # Returns Hash
343
- def to_h
344
- @data
345
- end
346
-
347
- def type_of?(*types)
348
- types.any? do |type|
349
- if type = self.class.schema.types.fetch(type.to_s, nil)
350
- self.class.schema.possible_types(type).any? { |t| @__typename == t.name }
351
- else
352
- false
353
- end
354
- end
355
- end
356
-
357
- def inspect
358
- ivars = self.class.fields.keys.map do |sym|
359
- value = instance_variable_get("@#{sym}")
360
- if value.is_a?(QueryResult)
361
- "#{sym}=#<#{value.class.name}>"
362
- else
363
- "#{sym}=#{value.inspect}"
364
- end
365
- end
366
- buf = "#<#{self.class.name}".dup
367
- buf << " " << ivars.join(" ") if ivars.any?
368
- buf << ">"
369
- buf
370
- end
371
-
372
- def method_missing(*args)
373
- super
374
- rescue NoMethodError => e
375
- type = self.class.type
376
- raise e unless type
377
-
378
- field = type.all_fields.find do |f|
379
- f.name == e.name.to_s || ActiveSupport::Inflector.underscore(f.name) == e.name.to_s
380
- end
381
-
382
- unless field
383
- raise UnimplementedFieldError, "undefined field `#{e.name}' on #{type} type. https://git.io/v1y3m"
384
- end
385
-
386
- if @data[field.name]
387
- error_class = ImplicitlyFetchedFieldError
388
- message = "implicitly fetched field `#{field.name}' on #{type} type. https://git.io/v1yGL"
389
- else
390
- error_class = UnfetchedFieldError
391
- message = "unfetched field `#{field.name}' on #{type} type. https://git.io/v1y3U"
392
- end
393
-
394
- raise error_class, message
395
- end
396
-
397
- def respond_to_missing?(*args)
398
- super
399
- end
400
- end
401
- end
402
- end