graphql-batch 0.2.4 → 0.2.5
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/README.md +5 -2
- data/graphql-batch.gemspec +2 -2
- data/lib/graphql/batch/execution_strategy.rb +6 -1
- data/lib/graphql/batch/loader.rb +61 -19
- data/lib/graphql/batch/mutation_execution_strategy.rb +6 -6
- data/lib/graphql/batch/version.rb +1 -1
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ee4c1ca2f39afd8e9ecc36c867cc389357169f9
|
4
|
+
data.tar.gz: c0282358b5bf23cef97f6055af9b6010743a00c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0374a2cfdca4c4b61738914c21966582edb27920ea1ae986bc780dae9c1eb2f159f56228a4098c2d7205103f2125dba161e5c71e6d3f1ace468a7bb1e37f494
|
7
|
+
data.tar.gz: fac574fdb4ea3466c46c6a9ac6eda5ee229ecb3882ff2f9f11f65a47c9a987baac869643e319830ef14f4b6b4ef552243bc4f6584ea09e6c27e04f0962197a63
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# GraphQL::Batch
|
2
2
|
|
3
|
+
[](https://travis-ci.org/Shopify/graphql-batch)
|
4
|
+
[](https://rubygems.org/gems/graphql-batch)
|
5
|
+
|
3
6
|
Provides an executor for the [`graphql` gem](https://github.com/rmosolgo/graphql-ruby) which allows queries to be batched.
|
4
7
|
|
5
8
|
## Installation
|
@@ -28,7 +31,7 @@ Require the library
|
|
28
31
|
require 'graphql/batch'
|
29
32
|
```
|
30
33
|
|
31
|
-
Define a custom loader, which is initialized with arguments that are used for grouping and
|
34
|
+
Define a custom loader, which is initialized with arguments that are used for grouping and a perform method for performing the batch load.
|
32
35
|
|
33
36
|
```ruby
|
34
37
|
class RecordLoader < GraphQL::Batch::Loader
|
@@ -118,7 +121,7 @@ GraphQL::Batch::Promise#sync can be used to wait for a promise to be resolved an
|
|
118
121
|
|
119
122
|
Use GraphQL::Batch::Promise.all instead of Promise.all to be able to call sync on the returned promise.
|
120
123
|
|
121
|
-
```
|
124
|
+
```ruby
|
122
125
|
def test_batch_query
|
123
126
|
products = [products(:snowboard), products(:jacket)]
|
124
127
|
query1 = RecordLoader.for(Product).load(products(:snowboard).id).then(&:title)
|
data/graphql-batch.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.name = "graphql-batch"
|
8
8
|
spec.version = GraphQL::Batch::VERSION
|
9
9
|
spec.authors = ["Dylan Thacker-Smith"]
|
10
|
-
spec.email = ["
|
10
|
+
spec.email = ["gems@shopify.com"]
|
11
11
|
|
12
12
|
spec.summary = "A query batching executor for the graphql gem"
|
13
13
|
spec.homepage = "https://github.com/Shopify/graphql-batch"
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "graphql", "
|
21
|
+
spec.add_runtime_dependency "graphql", ">= 0.8", "< 2"
|
22
22
|
spec.add_runtime_dependency "promise.rb", "~> 0.7.0.rc2"
|
23
23
|
|
24
24
|
spec.add_development_dependency "byebug" if RUBY_ENGINE == 'ruby'
|
@@ -1,7 +1,12 @@
|
|
1
1
|
module GraphQL::Batch
|
2
2
|
class ExecutionStrategy < GraphQL::Query::SerialExecution
|
3
|
-
|
3
|
+
attr_accessor :disable_batching
|
4
|
+
|
5
|
+
def execute(_, _, query)
|
4
6
|
as_promise(super).sync
|
7
|
+
rescue GraphQL::InvalidNullError => err
|
8
|
+
err.parent_error? || query.context.errors.push(err)
|
9
|
+
nil
|
5
10
|
ensure
|
6
11
|
GraphQL::Batch::Executor.current.clear
|
7
12
|
end
|
data/lib/graphql/batch/loader.rb
CHANGED
@@ -2,55 +2,97 @@ module GraphQL::Batch
|
|
2
2
|
class Loader
|
3
3
|
def self.for(*group_args)
|
4
4
|
loader_key = [self].concat(group_args)
|
5
|
-
Executor.current.loaders[loader_key] ||= new(*group_args)
|
5
|
+
Executor.current.loaders[loader_key] ||= new(*group_args).tap do |loader|
|
6
|
+
loader.loader_key = loader_key
|
7
|
+
end
|
6
8
|
end
|
7
9
|
|
8
|
-
def
|
9
|
-
|
10
|
+
def self.load(key)
|
11
|
+
self.for.load(key)
|
10
12
|
end
|
11
13
|
|
12
|
-
def keys
|
13
|
-
|
14
|
+
def self.load_many(keys)
|
15
|
+
self.for.load_many(keys)
|
14
16
|
end
|
15
17
|
|
18
|
+
attr_accessor :loader_key
|
19
|
+
|
16
20
|
def load(key)
|
17
|
-
|
21
|
+
loader = Executor.current.loaders[loader_key] ||= self
|
22
|
+
if loader != self
|
23
|
+
raise "load called on loader that wasn't registered with executor"
|
24
|
+
end
|
25
|
+
cache[cache_key(key)] ||= begin
|
26
|
+
queue << key
|
27
|
+
Promise.new
|
28
|
+
end
|
18
29
|
end
|
19
30
|
|
20
31
|
def load_many(keys)
|
21
32
|
Promise.all(keys.map { |key| load(key) })
|
22
33
|
end
|
23
34
|
|
35
|
+
def resolve #:nodoc:
|
36
|
+
load_keys = queue
|
37
|
+
return if load_keys.empty?
|
38
|
+
@queue = nil
|
39
|
+
perform(load_keys)
|
40
|
+
check_for_broken_promises(load_keys)
|
41
|
+
rescue => err
|
42
|
+
each_pending_promise(load_keys) do |key, promise|
|
43
|
+
promise.reject(err)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
# Fulfill the key with provided value, for use in #perform
|
24
50
|
def fulfill(key, value)
|
25
|
-
|
51
|
+
promise_for(key).fulfill(value)
|
26
52
|
end
|
27
53
|
|
54
|
+
# Returns true when the key has already been fulfilled, otherwise returns false
|
28
55
|
def fulfilled?(key)
|
29
|
-
|
56
|
+
promise_for(key).fulfilled?
|
30
57
|
end
|
31
58
|
|
32
|
-
#
|
59
|
+
# Must override to load the keys and call #fulfill for each key
|
33
60
|
def perform(keys)
|
34
61
|
raise NotImplementedError
|
35
62
|
end
|
36
63
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
rescue => err
|
41
|
-
promises_by_key.each do |key, promise|
|
42
|
-
promise.reject(err)
|
43
|
-
end
|
64
|
+
# Override to use a different key for the cache than the load key
|
65
|
+
def cache_key(load_key)
|
66
|
+
load_key
|
44
67
|
end
|
45
68
|
|
46
69
|
private
|
47
70
|
|
48
|
-
def
|
49
|
-
|
71
|
+
def cache
|
72
|
+
@cache ||= {}
|
73
|
+
end
|
74
|
+
|
75
|
+
def queue
|
76
|
+
@queue ||= []
|
77
|
+
end
|
78
|
+
|
79
|
+
def promise_for(load_key)
|
80
|
+
cache.fetch(cache_key(load_key))
|
81
|
+
end
|
82
|
+
|
83
|
+
def each_pending_promise(load_keys)
|
84
|
+
load_keys.each do |key|
|
85
|
+
promise = promise_for(key)
|
50
86
|
if promise.pending?
|
51
|
-
|
87
|
+
yield key, promise
|
52
88
|
end
|
53
89
|
end
|
54
90
|
end
|
91
|
+
|
92
|
+
def check_for_broken_promises(load_keys)
|
93
|
+
each_pending_promise(load_keys) do |key, promise|
|
94
|
+
promise.reject(BrokenPromiseError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
|
95
|
+
end
|
96
|
+
end
|
55
97
|
end
|
56
98
|
end
|
@@ -1,18 +1,18 @@
|
|
1
1
|
module GraphQL::Batch
|
2
|
-
class MutationExecutionStrategy < GraphQL::
|
3
|
-
class FieldResolution < GraphQL::
|
2
|
+
class MutationExecutionStrategy < GraphQL::Batch::ExecutionStrategy
|
3
|
+
class FieldResolution < GraphQL::Batch::ExecutionStrategy::FieldResolution
|
4
4
|
def get_finished_value(raw_value)
|
5
|
+
return super if execution_context.strategy.disable_batching
|
6
|
+
|
5
7
|
raw_value = GraphQL::Batch::Promise.resolve(raw_value).sync
|
6
8
|
|
7
|
-
|
8
|
-
old_execution_strategy = context.execution_strategy
|
9
|
+
execution_context.strategy.disable_batching = true
|
9
10
|
begin
|
10
|
-
context.execution_strategy = GraphQL::Batch::ExecutionStrategy.new
|
11
11
|
result = super(raw_value)
|
12
12
|
GraphQL::Batch::Executor.current.wait_all
|
13
13
|
result
|
14
14
|
ensure
|
15
|
-
|
15
|
+
execution_context.strategy.disable_batching = false
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-batch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dylan Thacker-Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0.8'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '0.8'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: promise.rb
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,7 +102,7 @@ dependencies:
|
|
96
102
|
version: '0'
|
97
103
|
description:
|
98
104
|
email:
|
99
|
-
-
|
105
|
+
- gems@shopify.com
|
100
106
|
executables: []
|
101
107
|
extensions: []
|
102
108
|
extra_rdoc_files: []
|
@@ -137,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
143
|
version: '0'
|
138
144
|
requirements: []
|
139
145
|
rubyforge_project:
|
140
|
-
rubygems_version: 2.
|
146
|
+
rubygems_version: 2.5.1
|
141
147
|
signing_key:
|
142
148
|
specification_version: 4
|
143
149
|
summary: A query batching executor for the graphql gem
|