graphql-client 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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