graphql-client 0.1.2 → 0.2.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 +4 -4
- data/lib/graphql/client.rb +12 -3
- data/lib/graphql/client/errors.rb +200 -0
- data/lib/graphql/client/hash_with_indifferent_access.rb +57 -0
- data/lib/graphql/client/list.rb +19 -0
- data/lib/graphql/client/query_result.rb +22 -13
- data/lib/graphql/client/response.rb +8 -115
- data/lib/graphql/language/nodes/deep_freeze_ext.rb +6 -0
- metadata +9 -7
- data/lib/graphql/language/operation_slice.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2383236dd4b9c7a7589212a7ec4fa057d0355cb
|
4
|
+
data.tar.gz: 0e858ee34e190846593cadec92238c406b9903dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fa1b4185938e68e90082eaa6be4ce1c814503b2254aea005c44cfe636f2c2ea358fc97250ef868ad5271e4609359296eb7e18b32a9dbf697b323becea67866d
|
7
|
+
data.tar.gz: 3f3ae9c9a78dddfcc5b8758a072dd173b47d5fe5a1bfa1a853443bebcd5386250a360862ff3c6482a1a40e3671f9664215dbbf7f46351323fdc3401a98ce109a
|
data/lib/graphql/client.rb
CHANGED
@@ -2,10 +2,10 @@ require "active_support/inflector"
|
|
2
2
|
require "active_support/notifications"
|
3
3
|
require "graphql"
|
4
4
|
require "graphql/client/error"
|
5
|
+
require "graphql/client/errors"
|
5
6
|
require "graphql/client/query_result"
|
6
7
|
require "graphql/client/response"
|
7
8
|
require "graphql/language/nodes/deep_freeze_ext"
|
8
|
-
require "graphql/language/operation_slice"
|
9
9
|
require "json"
|
10
10
|
|
11
11
|
module GraphQL
|
@@ -203,7 +203,7 @@ module GraphQL
|
|
203
203
|
definitions = {}
|
204
204
|
doc.definitions.each do |node|
|
205
205
|
node.name = nil if node.name == "__anonymous__"
|
206
|
-
sliced_document = Language::
|
206
|
+
sliced_document = Language::DefinitionSlice.slice(document_dependencies, node.name)
|
207
207
|
definition = Definition.for(node: node, document: sliced_document)
|
208
208
|
definitions[node.name] = definition
|
209
209
|
end
|
@@ -265,7 +265,16 @@ module GraphQL
|
|
265
265
|
)
|
266
266
|
end
|
267
267
|
|
268
|
-
|
268
|
+
data, errors, extensions = result.values_at("data", "errors", "extensions")
|
269
|
+
|
270
|
+
errors ||= []
|
271
|
+
GraphQL::Client::Errors.normalize_error_paths(data, errors)
|
272
|
+
|
273
|
+
Response.new(
|
274
|
+
data: definition.new(data, Errors.new(errors, ["data"])),
|
275
|
+
errors: Errors.new(errors),
|
276
|
+
extensions: extensions
|
277
|
+
)
|
269
278
|
end
|
270
279
|
|
271
280
|
# Internal: FragmentSpread and FragmentDefinition extension to allow its
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require "graphql/client/hash_with_indifferent_access"
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Client
|
5
|
+
# Public: Collection of errors associated with GraphQL object type.
|
6
|
+
#
|
7
|
+
# Inspired by ActiveModel::Errors.
|
8
|
+
class Errors
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# Internal: Normalize GraphQL Error "path" ensuring the path exists.
|
12
|
+
#
|
13
|
+
# Records "normalizedPath" value to error object.
|
14
|
+
#
|
15
|
+
# data - Hash of response data
|
16
|
+
# errors - Array of error Hashes
|
17
|
+
#
|
18
|
+
# Returns nothing.
|
19
|
+
def self.normalize_error_paths(data = nil, errors = [])
|
20
|
+
errors.each do |error|
|
21
|
+
path = ["data"]
|
22
|
+
current = data
|
23
|
+
error.fetch("path", []).each do |key|
|
24
|
+
break unless current
|
25
|
+
path << key
|
26
|
+
current = current[key]
|
27
|
+
end
|
28
|
+
error["normalizedPath"] = path
|
29
|
+
end
|
30
|
+
errors
|
31
|
+
end
|
32
|
+
|
33
|
+
# Internal: Initalize from collection of errors.
|
34
|
+
#
|
35
|
+
# errors - Array of GraphQL Hash error objects
|
36
|
+
# path - Array of String|Integer fields to data
|
37
|
+
# all - Boolean flag if all nested errors should be available
|
38
|
+
def initialize(errors = [], path = [], all = false)
|
39
|
+
@ast_path = path
|
40
|
+
@all = all
|
41
|
+
@raw_errors = errors
|
42
|
+
end
|
43
|
+
|
44
|
+
# Public: Return collection of all nested errors.
|
45
|
+
#
|
46
|
+
# data.errors[:node]
|
47
|
+
# data.errors.all[:node]
|
48
|
+
#
|
49
|
+
# Returns Errors collection.
|
50
|
+
def all
|
51
|
+
if @all
|
52
|
+
self
|
53
|
+
else
|
54
|
+
self.class.new(@raw_errors, @ast_path, true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Internal: Return collection of errors for a given subfield.
|
59
|
+
#
|
60
|
+
# data.errors.filter_by_path("node")
|
61
|
+
#
|
62
|
+
# Returns Errors collection.
|
63
|
+
def filter_by_path(field)
|
64
|
+
self.class.new(@raw_errors, @ast_path + [field], @all)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Public: Access Hash of error messages.
|
68
|
+
#
|
69
|
+
# data.errors.messages["node"]
|
70
|
+
# data.errors.messages[:node]
|
71
|
+
#
|
72
|
+
# Returns HashWithIndifferentAccess.
|
73
|
+
def messages
|
74
|
+
return @messages if defined? @messages
|
75
|
+
|
76
|
+
messages = {}
|
77
|
+
|
78
|
+
details.each do |field, errors|
|
79
|
+
messages[field] ||= []
|
80
|
+
errors.each do |error|
|
81
|
+
messages[field] << error.fetch("message")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
@messages = HashWithIndifferentAccess.new(messages)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Public: Access Hash of error objects.
|
89
|
+
#
|
90
|
+
# data.errors.details["node"]
|
91
|
+
# data.errors.details[:node]
|
92
|
+
#
|
93
|
+
# Returns HashWithIndifferentAccess.
|
94
|
+
def details
|
95
|
+
return @details if defined? @details
|
96
|
+
|
97
|
+
details = {}
|
98
|
+
|
99
|
+
@raw_errors.each do |error|
|
100
|
+
path = error.fetch("normalizedPath", [])
|
101
|
+
matched_path = @all ? path[0, @ast_path.length] : path[0...-1]
|
102
|
+
next unless @ast_path == matched_path
|
103
|
+
|
104
|
+
field = path[@ast_path.length]
|
105
|
+
next unless field
|
106
|
+
|
107
|
+
details[field] ||= []
|
108
|
+
details[field] << error
|
109
|
+
end
|
110
|
+
|
111
|
+
@details = HashWithIndifferentAccess.new(details)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Public: When passed a symbol or a name of a field, returns an array of
|
115
|
+
# errors for the method.
|
116
|
+
#
|
117
|
+
# data.errors[:node] # => ["couldn't find node by id"]
|
118
|
+
# data.errors['node'] # => ["couldn't find node by id"]
|
119
|
+
#
|
120
|
+
# Returns Array of errors.
|
121
|
+
def [](key)
|
122
|
+
messages.fetch(key, [])
|
123
|
+
end
|
124
|
+
|
125
|
+
# Public: Iterates through each error key, value pair in the error
|
126
|
+
# messages hash. Yields the field and the error for that attribute. If the
|
127
|
+
# field has more than one error message, yields once for each error
|
128
|
+
# message.
|
129
|
+
def each
|
130
|
+
return enum_for(:each) unless block_given?
|
131
|
+
messages.keys.each do |field|
|
132
|
+
messages[field].each { |error| yield field, error }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Public: Check if there are any errors on a given field.
|
137
|
+
#
|
138
|
+
# data.errors.messages # => {"node"=>["couldn't find node by id", "unauthorized"]}
|
139
|
+
# data.errors.include?("node") # => true
|
140
|
+
# data.errors.include?("version") # => false
|
141
|
+
#
|
142
|
+
# Returns true if the error messages include an error for the given field,
|
143
|
+
# otherwise false.
|
144
|
+
def include?(field)
|
145
|
+
self[field].any?
|
146
|
+
end
|
147
|
+
alias has_key? include?
|
148
|
+
alias key? include?
|
149
|
+
|
150
|
+
# Public: Count the number of errors on object.
|
151
|
+
#
|
152
|
+
# data.errors.messages # => {"node"=>["couldn't find node by id", "unauthorized"]}
|
153
|
+
# data.errors.size # => 2
|
154
|
+
#
|
155
|
+
# Returns the number of error messages.
|
156
|
+
def size
|
157
|
+
values.flatten.size
|
158
|
+
end
|
159
|
+
alias count size
|
160
|
+
|
161
|
+
# Public: Check if there are no errors on object.
|
162
|
+
#
|
163
|
+
# data.errors.messages # => {"node"=>["couldn't find node by id"]}
|
164
|
+
# data.errors.empty? # => false
|
165
|
+
#
|
166
|
+
# Returns true if no errors are found, otherwise false.
|
167
|
+
def empty?
|
168
|
+
size.zero?
|
169
|
+
end
|
170
|
+
alias blank? empty?
|
171
|
+
|
172
|
+
# Public: Returns all message keys.
|
173
|
+
#
|
174
|
+
# data.errors.messages # => {"node"=>["couldn't find node by id"]}
|
175
|
+
# data.errors.values # => ["node"]
|
176
|
+
#
|
177
|
+
# Returns Array of String field names.
|
178
|
+
def keys
|
179
|
+
messages.keys
|
180
|
+
end
|
181
|
+
|
182
|
+
# Public: Returns all message values.
|
183
|
+
#
|
184
|
+
# data.errors.messages # => {"node"=>["couldn't find node by id"]}
|
185
|
+
# data.errors.values # => [["couldn't find node by id"]]
|
186
|
+
#
|
187
|
+
# Returns Array of Array String messages.
|
188
|
+
def values
|
189
|
+
messages.values
|
190
|
+
end
|
191
|
+
|
192
|
+
# Public: Display console friendly representation of errors collection.
|
193
|
+
#
|
194
|
+
# Returns String.
|
195
|
+
def inspect
|
196
|
+
"#<#{self.class} @messages=#{messages.inspect} @details=#{details.inspect}>"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "active_support/inflector"
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Client
|
5
|
+
# Public: Implements a read only hash where keys can be accessed by
|
6
|
+
# strings, symbols, snake or camel case.
|
7
|
+
#
|
8
|
+
# Also see ActiveSupport::HashWithIndifferentAccess.
|
9
|
+
class HashWithIndifferentAccess
|
10
|
+
extend Forwardable
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
def initialize(hash = {})
|
14
|
+
@hash = hash
|
15
|
+
@aliases = {}
|
16
|
+
|
17
|
+
hash.keys.each do |key|
|
18
|
+
if key.is_a?(String)
|
19
|
+
key_alias = ActiveSupport::Inflector.underscore(key)
|
20
|
+
@aliases[key_alias] = key if key != key_alias
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
def_delegators :@hash, :each, :empty?, :inspect, :keys, :length, :size, :to_h, :to_hash, :values
|
28
|
+
|
29
|
+
def [](key)
|
30
|
+
@hash[convert_value(key)]
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch(key, *args, &block)
|
34
|
+
@hash.fetch(convert_value(key), *args, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def key?(key)
|
38
|
+
@hash.key?(convert_value(key))
|
39
|
+
end
|
40
|
+
alias include? key?
|
41
|
+
alias has_key? key?
|
42
|
+
alias member? key?
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def convert_value(key)
|
47
|
+
case key
|
48
|
+
when String, Symbol
|
49
|
+
key = key.to_s
|
50
|
+
@aliases.fetch(key, key)
|
51
|
+
else
|
52
|
+
key
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "graphql/client/errors"
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Client
|
5
|
+
# Public: Array wrapper for value returned from GraphQL List.
|
6
|
+
class List < Array
|
7
|
+
def initialize(values, errors = Errors.new)
|
8
|
+
super(values)
|
9
|
+
@errors = errors
|
10
|
+
freeze
|
11
|
+
end
|
12
|
+
|
13
|
+
# Public: Return errors associated with list of data.
|
14
|
+
#
|
15
|
+
# Returns Errors collection.
|
16
|
+
attr_reader :errors
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require "active_support/inflector"
|
2
2
|
require "graphql"
|
3
|
+
require "graphql/client/errors"
|
4
|
+
require "graphql/client/list"
|
3
5
|
require "set"
|
4
6
|
|
5
7
|
module GraphQL
|
@@ -58,18 +60,18 @@ module GraphQL
|
|
58
60
|
|
59
61
|
next unless field == "edges"
|
60
62
|
class_eval <<-RUBY, __FILE__, __LINE__
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
def each_node
|
64
|
+
return enum_for(:each_node) unless block_given?
|
65
|
+
edges.each { |edge| yield edge.node }
|
66
|
+
self
|
67
|
+
end
|
66
68
|
RUBY
|
67
69
|
end
|
68
70
|
|
69
71
|
assigns = fields.map do |field, type|
|
70
72
|
if type
|
71
73
|
<<-RUBY
|
72
|
-
@#{field} = self.class.fields[:#{field}].cast(@data["#{field}"])
|
74
|
+
@#{field} = self.class.fields[:#{field}].cast(@data["#{field}"], @errors.filter_by_path("#{field}"))
|
73
75
|
RUBY
|
74
76
|
else
|
75
77
|
<<-RUBY
|
@@ -79,8 +81,10 @@ module GraphQL
|
|
79
81
|
end
|
80
82
|
|
81
83
|
class_eval <<-RUBY, __FILE__, __LINE__
|
82
|
-
def initialize(data)
|
84
|
+
def initialize(data, errors = Errors.new)
|
83
85
|
@data = data
|
86
|
+
@errors = errors
|
87
|
+
|
84
88
|
#{assigns.join("\n")}
|
85
89
|
freeze
|
86
90
|
end
|
@@ -106,10 +110,10 @@ module GraphQL
|
|
106
110
|
"#<#{name} fields=#{@fields.keys.inspect}>"
|
107
111
|
end
|
108
112
|
|
109
|
-
def self.cast(obj)
|
113
|
+
def self.cast(obj, errors = Errors.new)
|
110
114
|
case obj
|
111
115
|
when Hash
|
112
|
-
new(obj)
|
116
|
+
new(obj, errors)
|
113
117
|
when self
|
114
118
|
return obj
|
115
119
|
when QueryResult
|
@@ -121,9 +125,9 @@ module GraphQL
|
|
121
125
|
message << GraphQL::Language::Generation.generate(obj.class.source_node).sub(/\n}$/, "#{suggestion}\n}")
|
122
126
|
raise TypeError, message
|
123
127
|
end
|
124
|
-
cast(obj.to_h)
|
128
|
+
cast(obj.to_h, obj.errors)
|
125
129
|
when Array
|
126
|
-
obj.map { |e| cast(e) }
|
130
|
+
List.new(obj.each_with_index.map { |e, idx| cast(e, errors.filter_by_path(idx)) }, errors)
|
127
131
|
when NilClass
|
128
132
|
nil
|
129
133
|
else
|
@@ -145,12 +149,12 @@ module GraphQL
|
|
145
149
|
end
|
146
150
|
end
|
147
151
|
|
148
|
-
def self.new(obj)
|
152
|
+
def self.new(obj, *args)
|
149
153
|
case obj
|
150
154
|
when Hash
|
151
155
|
super
|
152
156
|
else
|
153
|
-
cast(obj)
|
157
|
+
cast(obj, *args)
|
154
158
|
end
|
155
159
|
end
|
156
160
|
|
@@ -167,6 +171,11 @@ module GraphQL
|
|
167
171
|
define(name: self.name, source_node: source_node, fields: new_fields)
|
168
172
|
end
|
169
173
|
|
174
|
+
# Public: Return errors associated with data.
|
175
|
+
#
|
176
|
+
# Returns Errors collection.
|
177
|
+
attr_reader :errors
|
178
|
+
|
170
179
|
attr_reader :data
|
171
180
|
alias to_h data
|
172
181
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "graphql/client/
|
1
|
+
require "graphql/client/errors"
|
2
2
|
|
3
3
|
module GraphQL
|
4
4
|
class Client
|
@@ -6,45 +6,6 @@ module GraphQL
|
|
6
6
|
#
|
7
7
|
# https://facebook.github.io/graphql/#sec-Response-Format
|
8
8
|
class Response
|
9
|
-
# Internal: Initialize Response subclass.
|
10
|
-
def self.for(definition, result)
|
11
|
-
data, errors, extensions = result.values_at("data", "errors", "extensions")
|
12
|
-
|
13
|
-
if data && errors
|
14
|
-
PartialResponse.new(
|
15
|
-
data: definition.new(data),
|
16
|
-
errors: ResponseErrors.new(definition, errors),
|
17
|
-
extensions: extensions
|
18
|
-
)
|
19
|
-
elsif data && !errors
|
20
|
-
SuccessfulResponse.new(
|
21
|
-
data: definition.new(data),
|
22
|
-
extensions: extensions
|
23
|
-
)
|
24
|
-
elsif !data && errors
|
25
|
-
FailedResponse.new(
|
26
|
-
errors: ResponseErrors.new(definition, errors),
|
27
|
-
extensions: extensions
|
28
|
-
)
|
29
|
-
else
|
30
|
-
FailedResponse.new(
|
31
|
-
errors: ResponseErrors.new(definition, [{ "message" => "invalid GraphQL response" }])
|
32
|
-
)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# Public: Hash of server specific extension metadata.
|
37
|
-
attr_reader :extensions
|
38
|
-
|
39
|
-
# Internal: Initialize base class.
|
40
|
-
def initialize(extensions: nil)
|
41
|
-
@extensions = extensions || {}
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Public: A successful response means the query executed without any errors
|
46
|
-
# and returned all the requested data.
|
47
|
-
class SuccessfulResponse < Response
|
48
9
|
# Public: Wrapped QueryResult of data returned from the server.
|
49
10
|
#
|
50
11
|
# https://facebook.github.io/graphql/#sec-Data
|
@@ -52,89 +13,21 @@ module GraphQL
|
|
52
13
|
# Returns instance of QueryResult subclass.
|
53
14
|
attr_reader :data
|
54
15
|
|
55
|
-
# Internal: Initialize SuccessfulResponse.
|
56
|
-
def initialize(data:, **kargs)
|
57
|
-
@data = data
|
58
|
-
super(**kargs)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# Public: A partial response means the query executed with some errors but
|
63
|
-
# returned all non-nullable fields. PartialResponse is still considered a
|
64
|
-
# SuccessfulResponse as it returns data and the client may still proceed
|
65
|
-
# with its normal render flow.
|
66
|
-
class PartialResponse < SuccessfulResponse
|
67
16
|
# Public: Get partial failures from response.
|
68
17
|
#
|
69
18
|
# https://facebook.github.io/graphql/#sec-Errors
|
70
19
|
#
|
71
|
-
# Returns
|
20
|
+
# Returns Errors collection object with zero or more errors.
|
72
21
|
attr_reader :errors
|
73
22
|
|
74
|
-
#
|
75
|
-
|
76
|
-
@errors = errors
|
77
|
-
super(**kargs)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Public: A failed response returns no data and at least one error message.
|
82
|
-
# Cases may likely be a query validation error, missing authorization,
|
83
|
-
# or internal server crash.
|
84
|
-
class FailedResponse < Response
|
85
|
-
# Public: Get errors from response.
|
86
|
-
#
|
87
|
-
# https://facebook.github.io/graphql/#sec-Errors
|
88
|
-
#
|
89
|
-
# Returns ResponseErrors collection object with one or more errors.
|
90
|
-
attr_reader :errors
|
23
|
+
# Public: Hash of server specific extension metadata.
|
24
|
+
attr_reader :extensions
|
91
25
|
|
92
|
-
# Internal: Initialize
|
93
|
-
def initialize(errors
|
26
|
+
# Internal: Initialize base class.
|
27
|
+
def initialize(data: nil, errors: Errors.new, extensions: {})
|
28
|
+
@data = data
|
94
29
|
@errors = errors
|
95
|
-
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Public: An error received from the server on execution.
|
100
|
-
#
|
101
|
-
# Extends StandardError hierarchy so you may raise this instance.
|
102
|
-
#
|
103
|
-
# Examples
|
104
|
-
#
|
105
|
-
# raise response.errors.first
|
106
|
-
#
|
107
|
-
class ResponseError < Error
|
108
|
-
# Internal: Initialize ResponseError.
|
109
|
-
def initialize(definition, error)
|
110
|
-
@request_definition = definition
|
111
|
-
@locations = error["locations"]
|
112
|
-
super error["message"]
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Public: A collection of errors received from the server on execution.
|
117
|
-
#
|
118
|
-
# Extends StandardError hierarchy so you may raise this instance.
|
119
|
-
#
|
120
|
-
# Examples
|
121
|
-
#
|
122
|
-
# raise response.errors
|
123
|
-
#
|
124
|
-
class ResponseErrors < Error
|
125
|
-
include Enumerable
|
126
|
-
|
127
|
-
attr_reader :errors
|
128
|
-
|
129
|
-
# Internal: Initialize ResponseErrors.
|
130
|
-
def initialize(definition, errors)
|
131
|
-
@request_definition = definition
|
132
|
-
@errors = errors.map { |error| ResponseError.new(definition, error) }
|
133
|
-
super @errors.map(&:message).join(", ")
|
134
|
-
end
|
135
|
-
|
136
|
-
def each(&block)
|
137
|
-
errors.each(&block)
|
30
|
+
@extensions = extensions
|
138
31
|
end
|
139
32
|
end
|
140
33
|
end
|
@@ -12,6 +12,12 @@ module GraphQL
|
|
12
12
|
self.class.child_attributes.each do |attr_name|
|
13
13
|
public_send(attr_name).freeze.each(&:deep_freeze)
|
14
14
|
end
|
15
|
+
|
16
|
+
self.class.scalar_attributes.each do |attr_name|
|
17
|
+
object = public_send(attr_name)
|
18
|
+
object.freeze if object
|
19
|
+
end
|
20
|
+
|
15
21
|
freeze
|
16
22
|
end
|
17
23
|
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.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -36,20 +36,20 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '0.
|
39
|
+
version: '0.19'
|
40
40
|
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: 0.
|
42
|
+
version: 0.19.2
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: '0.
|
49
|
+
version: '0.19'
|
50
50
|
- - ">="
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: 0.
|
52
|
+
version: 0.19.2
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: actionpack
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,15 +122,17 @@ files:
|
|
122
122
|
- README.md
|
123
123
|
- lib/graphql/client.rb
|
124
124
|
- lib/graphql/client/error.rb
|
125
|
+
- lib/graphql/client/errors.rb
|
125
126
|
- lib/graphql/client/erubis.rb
|
127
|
+
- lib/graphql/client/hash_with_indifferent_access.rb
|
126
128
|
- lib/graphql/client/http.rb
|
129
|
+
- lib/graphql/client/list.rb
|
127
130
|
- lib/graphql/client/log_subscriber.rb
|
128
131
|
- lib/graphql/client/query_result.rb
|
129
132
|
- lib/graphql/client/railtie.rb
|
130
133
|
- lib/graphql/client/response.rb
|
131
134
|
- lib/graphql/client/view_module.rb
|
132
135
|
- lib/graphql/language/nodes/deep_freeze_ext.rb
|
133
|
-
- lib/graphql/language/operation_slice.rb
|
134
136
|
homepage: https://github.com/github/graphql-client
|
135
137
|
licenses:
|
136
138
|
- MIT
|
@@ -1,44 +0,0 @@
|
|
1
|
-
require "graphql"
|
2
|
-
|
3
|
-
module GraphQL
|
4
|
-
module Language
|
5
|
-
# Public: Document transformations to return a minimal document to represent
|
6
|
-
# operation.
|
7
|
-
module OperationSlice
|
8
|
-
# Public: Return's minimal document to represent operation.
|
9
|
-
#
|
10
|
-
# Find's target operation and any fragment dependencies and returns a
|
11
|
-
# new document with just those definitions.
|
12
|
-
#
|
13
|
-
# document - The Nodes::Document to find definitions.
|
14
|
-
# operation_name - The String name of Nodes::OperationDefinition
|
15
|
-
#
|
16
|
-
# Returns new Nodes::Document.
|
17
|
-
def self.slice(document, operation_name)
|
18
|
-
seen = Set.new([operation_name])
|
19
|
-
stack = [operation_name]
|
20
|
-
|
21
|
-
until stack.empty?
|
22
|
-
name = stack.pop
|
23
|
-
names = find_definition_fragment_spreads(document, name)
|
24
|
-
seen.merge(names)
|
25
|
-
stack.concat(names.to_a)
|
26
|
-
end
|
27
|
-
|
28
|
-
Nodes::Document.new(definitions: document.definitions.select { |node| seen.include?(node.name) })
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.find_definition_fragment_spreads(document, definition_name)
|
32
|
-
definition = document.definitions.find { |node| node.name == definition_name }
|
33
|
-
raise "missing definition: #{definition_name}" unless definition
|
34
|
-
spreads = Set.new
|
35
|
-
visitor = Visitor.new(definition)
|
36
|
-
visitor[Nodes::FragmentSpread].enter << -> (node, _parent) do
|
37
|
-
spreads << node.name
|
38
|
-
end
|
39
|
-
visitor.visit
|
40
|
-
spreads
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|