graphql-remote_loader 0.0.1
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 +7 -0
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +52 -0
- data/lib/graphql/remote_loader.rb +9 -0
- data/lib/graphql/remote_loader/loader.rb +66 -0
- data/lib/graphql/remote_loader/query_merger.rb +301 -0
- data/lib/graphql/remote_loader/version.rb +5 -0
- data/remote_graphql_loader.gemspec +31 -0
- metadata +138 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 19a40470289cc44591c9356bf76c842507b9ac83
|
4
|
+
data.tar.gz: 043fe953bb41e7b8c19568463ef0679e00b35995
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 90d83694e6182d95af2f20291def1f80edf8535eebf5738250585f9e1e6cb5e6c212e1ea9f27efe76bef9e79092863b1647b4122f6ad009ca91f23c4e9ef27bf
|
7
|
+
data.tar.gz: 69db4b3fa095275f946a23b4ce6edbe0d97bd2461d0282e283540338543bba77dd43d1669af299c786776ef8feefda7fb8f9e63e594d103fb7f56f8b68baedb4
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.byebug_history
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
graphql-remote_loader (0.0.1)
|
5
|
+
graphql (~> 1.6)
|
6
|
+
graphql-batch (~> 0.3)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
byebug (9.1.0)
|
12
|
+
coderay (1.1.2)
|
13
|
+
diff-lcs (1.3)
|
14
|
+
graphql (1.7.14)
|
15
|
+
graphql-batch (0.3.9)
|
16
|
+
graphql (>= 0.8, < 2)
|
17
|
+
promise.rb (~> 0.7.2)
|
18
|
+
method_source (0.9.0)
|
19
|
+
promise.rb (0.7.4)
|
20
|
+
pry (0.11.3)
|
21
|
+
coderay (~> 1.1.0)
|
22
|
+
method_source (~> 0.9.0)
|
23
|
+
pry-byebug (3.5.0)
|
24
|
+
byebug (~> 9.1)
|
25
|
+
pry (~> 0.10)
|
26
|
+
rake (10.4.2)
|
27
|
+
rspec (3.6.0)
|
28
|
+
rspec-core (~> 3.6.0)
|
29
|
+
rspec-expectations (~> 3.6.0)
|
30
|
+
rspec-mocks (~> 3.6.0)
|
31
|
+
rspec-core (3.6.0)
|
32
|
+
rspec-support (~> 3.6.0)
|
33
|
+
rspec-expectations (3.6.0)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.6.0)
|
36
|
+
rspec-mocks (3.6.0)
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
+
rspec-support (~> 3.6.0)
|
39
|
+
rspec-support (3.6.0)
|
40
|
+
|
41
|
+
PLATFORMS
|
42
|
+
ruby
|
43
|
+
|
44
|
+
DEPENDENCIES
|
45
|
+
bundler (~> 1.14)
|
46
|
+
graphql-remote_loader!
|
47
|
+
pry-byebug (~> 3.4)
|
48
|
+
rake (~> 10.0)
|
49
|
+
rspec
|
50
|
+
|
51
|
+
BUNDLED WITH
|
52
|
+
1.15.4
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "prime"
|
2
|
+
require_relative "query_merger"
|
3
|
+
|
4
|
+
module GraphQL
|
5
|
+
module RemoteLoader
|
6
|
+
class Loader < GraphQL::Batch::Loader
|
7
|
+
# Delegates to GraphQL::Batch::Loader#load
|
8
|
+
# We include a unique prime as part of the batch key to use as part
|
9
|
+
# of the alias on all fields. This is used to
|
10
|
+
# a) Avoid name collisions in the generated query
|
11
|
+
# b) Determine which fields in the result JSON should be
|
12
|
+
# handed fulfilled to each promise
|
13
|
+
def self.load(query)
|
14
|
+
@index ||= 1
|
15
|
+
@index += 1
|
16
|
+
|
17
|
+
prime = Prime.take(@index - 1).last
|
18
|
+
|
19
|
+
self.for.load([query, prime])
|
20
|
+
end
|
21
|
+
|
22
|
+
# Given a query string, return a response JSON
|
23
|
+
def query(query_string)
|
24
|
+
raise NotImplementedError,
|
25
|
+
"RemoteLoader::Loader should be subclassed and #query must be defined"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def perform(queries_and_primes)
|
31
|
+
query_string = QueryMerger.merge(queries_and_primes)
|
32
|
+
response = query(query_string)
|
33
|
+
|
34
|
+
queries_and_primes.each do |query, prime|
|
35
|
+
fulfill([query, prime], filter_keys_on_response(response.to_h, prime))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def filter_keys_on_response(obj, prime)
|
40
|
+
return obj unless obj.is_a? Hash
|
41
|
+
filtered_results = {}
|
42
|
+
|
43
|
+
# Select field keys on the results hash
|
44
|
+
fields = obj.keys.select { |k| k.match? /\Ap[0-9]+.*[^?]\z/ }
|
45
|
+
|
46
|
+
# Filter methods that were not requested in this sub-query
|
47
|
+
fields = fields.select do |field|
|
48
|
+
prime_factor = field.match(/\Ap([0-9]+)/)[1].to_i
|
49
|
+
(prime_factor % prime) == 0
|
50
|
+
end
|
51
|
+
|
52
|
+
# redefine methods on new obj, recursively filter sub-selections
|
53
|
+
fields.each do |method|
|
54
|
+
method_name = method.match(/\Ap[0-9]+(.*)/)[1]
|
55
|
+
|
56
|
+
method_value = obj[method]
|
57
|
+
filtered_value = filter_keys_on_response(method_value, prime)
|
58
|
+
|
59
|
+
filtered_results[method_name.underscore] = filtered_value
|
60
|
+
end
|
61
|
+
|
62
|
+
filtered_results
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module RemoteLoader
|
3
|
+
|
4
|
+
# Given a list of queries and their prime UIDs, generate the merged and labeled
|
5
|
+
# GraphQL query to be sent off to the remote backend.
|
6
|
+
class QueryMerger
|
7
|
+
class << self
|
8
|
+
def merge(queries_and_primes)
|
9
|
+
new_ast = []
|
10
|
+
|
11
|
+
queries_and_primes.each do |query, prime|
|
12
|
+
parsed_query = [parse(query)]
|
13
|
+
attach_primes!(parsed_query, prime)
|
14
|
+
merge_query(parsed_query, new_ast)
|
15
|
+
end
|
16
|
+
|
17
|
+
query_string_from_ast(new_ast)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def merge_query(query, ast)
|
23
|
+
query.each do |query_field|
|
24
|
+
matching_field = ast.find do |ast_field|
|
25
|
+
query_field[:field] == ast_field[:field] &&
|
26
|
+
query_field[:arguments] == ast_field[:arguments]
|
27
|
+
end
|
28
|
+
|
29
|
+
if matching_field
|
30
|
+
matching_field[:primes] *= query_field[:primes]
|
31
|
+
merge_query(query_field[:body], matching_field[:body])
|
32
|
+
else
|
33
|
+
ast << query_field
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def attach_primes!(query, prime)
|
39
|
+
query.each do |field|
|
40
|
+
field[:primes] = prime
|
41
|
+
attach_primes!(field[:body], prime)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def query_string_from_ast(ast)
|
46
|
+
ast_node_strings = ast.map do |ast_node|
|
47
|
+
query_string_for_node(ast_node)
|
48
|
+
end.join(" ")
|
49
|
+
ast_node_strings
|
50
|
+
end
|
51
|
+
|
52
|
+
def query_string_for_node(node)
|
53
|
+
field = node[:field]
|
54
|
+
|
55
|
+
# TODO: This won't work for fields named `query`. Fix once
|
56
|
+
# I move to Roberts non-jank parser
|
57
|
+
unless field.include?("...") || field == "query"
|
58
|
+
# assign preix if not a spread
|
59
|
+
field = "p#{node[:primes]}#{field}: #{field}"
|
60
|
+
end
|
61
|
+
|
62
|
+
args = node[:arguments].map do |arg, value|
|
63
|
+
value = "\"#{value}\"" if value.is_a? String
|
64
|
+
|
65
|
+
"#{arg}: #{value}"
|
66
|
+
end
|
67
|
+
|
68
|
+
arg_string = unless args.empty?
|
69
|
+
"(#{args.join(",")})"
|
70
|
+
else
|
71
|
+
""
|
72
|
+
end
|
73
|
+
|
74
|
+
body_string = unless node[:body].empty?
|
75
|
+
str = node[:body].map do |node|
|
76
|
+
query_string_for_node(node)
|
77
|
+
end
|
78
|
+
|
79
|
+
"{ #{str.join(" ")} }"
|
80
|
+
end
|
81
|
+
|
82
|
+
"#{field}#{arg_string} #{body_string}".strip
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse(query)
|
86
|
+
tokenizer = QueryTokenizer.new("query { #{query} }")
|
87
|
+
QueryAST.build(tokenizer)
|
88
|
+
end
|
89
|
+
|
90
|
+
class QueryAST
|
91
|
+
class ParseException < Exception; end
|
92
|
+
|
93
|
+
class << self
|
94
|
+
def build(tokenizer)
|
95
|
+
result = build_node(tokenizer)
|
96
|
+
end
|
97
|
+
|
98
|
+
def build_node(tokenizer)
|
99
|
+
{
|
100
|
+
field: get_field(tokenizer),
|
101
|
+
arguments: get_args(tokenizer),
|
102
|
+
body: build_body(tokenizer)
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_field(tokenizer)
|
107
|
+
token = tokenizer.pop
|
108
|
+
|
109
|
+
if token[:type] == :spread
|
110
|
+
validate_token_type(tokenizer.pop, :on)
|
111
|
+
spread_type = tokenizer.pop[:string]
|
112
|
+
|
113
|
+
return "... on #{spread_type}"
|
114
|
+
end
|
115
|
+
|
116
|
+
validate_token_type(token, :field)
|
117
|
+
|
118
|
+
throw_parse_exception(token, "field") unless (token[:type] == :field)
|
119
|
+
|
120
|
+
token[:string]
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_args(tokenizer)
|
124
|
+
args = {}
|
125
|
+
return args unless (tokenizer.peek[:type] == :left_paren)
|
126
|
+
|
127
|
+
# remove "("
|
128
|
+
tokenizer.pop
|
129
|
+
|
130
|
+
while true
|
131
|
+
token = tokenizer.pop
|
132
|
+
validate_token_type(token, :key)
|
133
|
+
|
134
|
+
# Remove :
|
135
|
+
key = token[:string].sub(":", "")
|
136
|
+
|
137
|
+
token = tokenizer.pop
|
138
|
+
value = case token[:type]
|
139
|
+
when :string
|
140
|
+
token[:string][1..-2] # remove ""
|
141
|
+
when :int
|
142
|
+
Integer(token[:string])
|
143
|
+
when :float
|
144
|
+
Float(token[:string])
|
145
|
+
when :true
|
146
|
+
true
|
147
|
+
when :false
|
148
|
+
false
|
149
|
+
else
|
150
|
+
raise ParseException, "Expected string, int, float, or boolean, got #{token[:string]}"
|
151
|
+
end
|
152
|
+
|
153
|
+
args[key.to_sym] = value
|
154
|
+
|
155
|
+
if tokenizer.peek[:type] == :comma
|
156
|
+
tokenizer.pop
|
157
|
+
elsif tokenizer.peek[:type] == :right_paren
|
158
|
+
tokenizer.pop
|
159
|
+
break
|
160
|
+
else
|
161
|
+
raise ParseException, "Expected comma or right paren, got #{tokenizer[:string]}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
args
|
166
|
+
end
|
167
|
+
|
168
|
+
def build_body(tokenizer)
|
169
|
+
body = []
|
170
|
+
return body unless (tokenizer.peek[:type] == :left_brace)
|
171
|
+
|
172
|
+
# remove "{"
|
173
|
+
tokenizer.pop
|
174
|
+
|
175
|
+
while true
|
176
|
+
body << build_node(tokenizer)
|
177
|
+
break if (tokenizer.peek[:type] == :right_brace)
|
178
|
+
end
|
179
|
+
|
180
|
+
# remove "}"
|
181
|
+
tokenizer.pop
|
182
|
+
|
183
|
+
body
|
184
|
+
end
|
185
|
+
|
186
|
+
def validate_token_type(token, expected_type)
|
187
|
+
unless token[:type] == expected_type
|
188
|
+
raise ParseException, "Expected #{expected_type.to_s} token, got #{token[:string]}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
class QueryTokenizer
|
195
|
+
def initialize(query)
|
196
|
+
@query = query.chars
|
197
|
+
set_next_token
|
198
|
+
end
|
199
|
+
|
200
|
+
# Get next token, without removing from stream
|
201
|
+
def peek
|
202
|
+
@next_token
|
203
|
+
end
|
204
|
+
|
205
|
+
# Pop next token
|
206
|
+
def pop
|
207
|
+
token = @next_token
|
208
|
+
set_next_token if token
|
209
|
+
|
210
|
+
token
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
# Read @query to set @next_token
|
216
|
+
def set_next_token
|
217
|
+
next_token_string = extract_token_string_from_query
|
218
|
+
@next_token = tokenize_string(next_token_string)
|
219
|
+
end
|
220
|
+
|
221
|
+
def extract_token_string_from_query
|
222
|
+
trim_leading_whitespace
|
223
|
+
|
224
|
+
token_string = ""
|
225
|
+
while true
|
226
|
+
next_char = @query.first || break
|
227
|
+
|
228
|
+
if is_brace_or_bracket_or_comma?(next_char)
|
229
|
+
if token_string.length == 0
|
230
|
+
token_string << @query.shift
|
231
|
+
end
|
232
|
+
|
233
|
+
break
|
234
|
+
else
|
235
|
+
if !is_whitespace?(@query.first)
|
236
|
+
token_string << @query.shift
|
237
|
+
else
|
238
|
+
break
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
token_string
|
244
|
+
end
|
245
|
+
|
246
|
+
def tokenize_string(next_token_string)
|
247
|
+
token = case next_token_string
|
248
|
+
when '{'
|
249
|
+
:left_brace
|
250
|
+
when '}'
|
251
|
+
:right_brace
|
252
|
+
when '('
|
253
|
+
:left_paren
|
254
|
+
when ')'
|
255
|
+
:right_paren
|
256
|
+
when ','
|
257
|
+
:comma
|
258
|
+
when /\A[[:digit:]]+\z/
|
259
|
+
:int
|
260
|
+
when /\A[[:digit:]]+\.[[:digit:]]+\z/
|
261
|
+
:float
|
262
|
+
when 'true'
|
263
|
+
:true
|
264
|
+
when 'false'
|
265
|
+
:false
|
266
|
+
when 'on'
|
267
|
+
:on
|
268
|
+
when /\A".*"\z/
|
269
|
+
:string
|
270
|
+
when /\A.+:\z/
|
271
|
+
:key
|
272
|
+
when ''
|
273
|
+
next_token_string = "END OF STREAM"
|
274
|
+
:empty
|
275
|
+
when '...'
|
276
|
+
:spread
|
277
|
+
else
|
278
|
+
:field
|
279
|
+
end
|
280
|
+
|
281
|
+
{ type: token, string: next_token_string }
|
282
|
+
end
|
283
|
+
|
284
|
+
def trim_leading_whitespace
|
285
|
+
while is_whitespace?(@query.first)
|
286
|
+
@query.shift
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def is_whitespace?(char)
|
291
|
+
char == "\n" || char == " "
|
292
|
+
end
|
293
|
+
|
294
|
+
def is_brace_or_bracket_or_comma?(char)
|
295
|
+
char == "{" || char == "}" || char == "(" || char == ")" || char == ","
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'graphql/remote_loader/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "graphql-remote_loader"
|
8
|
+
spec.version = GraphQL::RemoteLoader::VERSION
|
9
|
+
spec.authors = ["Nathaniel Woodthorpe"]
|
10
|
+
spec.email = ["d12@github.com", "njwoodthorpe@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = "Performant remote GraphQL queries from within a Ruby GraphQL API."
|
13
|
+
spec.description = "GraphQL::RemoteLoader allows performantly fetching data from remote GraphQL APIs in the resolvers of a graphql-ruby API."
|
14
|
+
spec.homepage = "http://nwoodthorpe.com"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
# spec.bindir = "bin"
|
21
|
+
# spec.executables = [""]
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "graphql", "~> 1.6"
|
25
|
+
spec.add_dependency "graphql-batch", "~> 0.3"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.6"
|
30
|
+
spec.add_development_dependency "pry-byebug", "~> 3.4"
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: graphql-remote_loader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nathaniel Woodthorpe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: graphql
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: graphql-batch
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.14'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.14'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.6'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry-byebug
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.4'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.4'
|
97
|
+
description: GraphQL::RemoteLoader allows performantly fetching data from remote GraphQL
|
98
|
+
APIs in the resolvers of a graphql-ruby API.
|
99
|
+
email:
|
100
|
+
- d12@github.com
|
101
|
+
- njwoodthorpe@gmail.com
|
102
|
+
executables: []
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- Gemfile
|
108
|
+
- Gemfile.lock
|
109
|
+
- lib/graphql/remote_loader.rb
|
110
|
+
- lib/graphql/remote_loader/loader.rb
|
111
|
+
- lib/graphql/remote_loader/query_merger.rb
|
112
|
+
- lib/graphql/remote_loader/version.rb
|
113
|
+
- remote_graphql_loader.gemspec
|
114
|
+
homepage: http://nwoodthorpe.com
|
115
|
+
licenses:
|
116
|
+
- MIT
|
117
|
+
metadata: {}
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 2.5.2
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: Performant remote GraphQL queries from within a Ruby GraphQL API.
|
138
|
+
test_files: []
|