graphql-batch 0.2.5 → 0.3.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/.gitignore +1 -1
- data/.travis.yml +4 -1
- data/Gemfile.graphql13 +5 -0
- data/README.md +12 -17
- data/graphql-batch.gemspec +1 -1
- data/lib/graphql/batch.rb +19 -3
- data/lib/graphql/batch/execution_strategy.rb +9 -10
- data/lib/graphql/batch/executor.rb +10 -9
- data/lib/graphql/batch/loader.rb +17 -9
- data/lib/graphql/batch/mutation_execution_strategy.rb +7 -8
- data/lib/graphql/batch/promise.rb +2 -5
- data/lib/graphql/batch/setup.rb +14 -0
- data/lib/graphql/batch/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc3c6396e81315bc1c3a008f3b19e8e249eb2c46
|
4
|
+
data.tar.gz: 02272cc3f0ba19341379ea4d2534fda470698c1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9f63fc31a2231ce25bcf11c8f8f9b96d38131712b6977d1b8bba6f288b168720450fbdbf07a926044f34a6bfeb6e3f16b561a0065d0adb80bb64df7a4a19a69
|
7
|
+
data.tar.gz: 04e5caec058af7eb05c441d519c76a63a33b7c6108f1cfb96b8f42e19f0b54f936d548b23f14c2b09f694ca9b87315a7526c120781fd2b8abadf4ad1b10ed549
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile.graphql13
ADDED
data/README.md
CHANGED
@@ -49,7 +49,9 @@ end
|
|
49
49
|
Use the batch execution strategy with your schema
|
50
50
|
|
51
51
|
```ruby
|
52
|
-
MySchema = GraphQL::Schema.
|
52
|
+
MySchema = GraphQL::Schema.define do
|
53
|
+
query MyQueryType
|
54
|
+
end
|
53
55
|
MySchema.query_execution_strategy = GraphQL::Batch::ExecutionStrategy
|
54
56
|
MySchema.mutation_execution_strategy = GraphQL::Batch::MutationExecutionStrategy
|
55
57
|
```
|
@@ -109,26 +111,19 @@ end
|
|
109
111
|
|
110
112
|
## Unit Testing
|
111
113
|
|
112
|
-
|
114
|
+
Your loaders can be tested outside of a GraphQL query by doing the
|
115
|
+
batch loads in a block passed to GraphQL::Batch.batch. That method
|
116
|
+
will set up thread-local state to store the loaders, batch load any
|
117
|
+
promise returned from the block then clear the thread-local state
|
118
|
+
to avoid leaking state between tests.
|
113
119
|
|
114
120
|
```ruby
|
115
121
|
def test_single_query
|
116
122
|
product = products(:snowboard)
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
Use GraphQL::Batch::Promise.all instead of Promise.all to be able to call sync on the returned promise.
|
123
|
-
|
124
|
-
```ruby
|
125
|
-
def test_batch_query
|
126
|
-
products = [products(:snowboard), products(:jacket)]
|
127
|
-
query1 = RecordLoader.for(Product).load(products(:snowboard).id).then(&:title)
|
128
|
-
query2 = RecordLoader.for(Product).load(products(:jacket).id).then(&:title)
|
129
|
-
results = GraphQL::Batch::Promise.all([query1, query2]).sync
|
130
|
-
assert_equal products(:snowboard).title, results[0]
|
131
|
-
assert_equal products(:jacket).title, results[1]
|
123
|
+
title = GraphQL::Batch.batch do
|
124
|
+
RecordLoader.for(Product).load(product.id).then(&:title)
|
125
|
+
end
|
126
|
+
assert_equal product.title, title
|
132
127
|
end
|
133
128
|
```
|
134
129
|
|
data/graphql-batch.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_runtime_dependency "graphql", ">= 0.8", "< 2"
|
22
|
-
spec.add_runtime_dependency "promise.rb", "~> 0.7.
|
22
|
+
spec.add_runtime_dependency "promise.rb", "~> 0.7.2"
|
23
23
|
|
24
24
|
spec.add_development_dependency "byebug" if RUBY_ENGINE == 'ruby'
|
25
25
|
spec.add_development_dependency "bundler", "~> 1.10"
|
data/lib/graphql/batch.rb
CHANGED
@@ -3,7 +3,18 @@ require "promise.rb"
|
|
3
3
|
|
4
4
|
module GraphQL
|
5
5
|
module Batch
|
6
|
-
BrokenPromiseError =
|
6
|
+
BrokenPromiseError = ::Promise::BrokenError
|
7
|
+
class NestedError < StandardError; end
|
8
|
+
|
9
|
+
def self.batch
|
10
|
+
raise NestedError if GraphQL::Batch::Executor.current
|
11
|
+
begin
|
12
|
+
GraphQL::Batch::Executor.current = GraphQL::Batch::Executor.new
|
13
|
+
Promise.sync(yield)
|
14
|
+
ensure
|
15
|
+
GraphQL::Batch::Executor.current = nil
|
16
|
+
end
|
17
|
+
end
|
7
18
|
end
|
8
19
|
end
|
9
20
|
|
@@ -11,5 +22,10 @@ require_relative "batch/version"
|
|
11
22
|
require_relative "batch/loader"
|
12
23
|
require_relative "batch/executor"
|
13
24
|
require_relative "batch/promise"
|
14
|
-
require_relative "batch/
|
15
|
-
|
25
|
+
require_relative "batch/setup"
|
26
|
+
|
27
|
+
# Allow custom execution strategies to be removed upstream
|
28
|
+
if defined?(GraphQL::Query::SerialExecution)
|
29
|
+
require_relative "batch/execution_strategy"
|
30
|
+
require_relative "batch/mutation_execution_strategy"
|
31
|
+
end
|
@@ -1,22 +1,21 @@
|
|
1
1
|
module GraphQL::Batch
|
2
2
|
class ExecutionStrategy < GraphQL::Query::SerialExecution
|
3
|
-
attr_accessor :disable_batching
|
4
|
-
|
5
3
|
def execute(_, _, query)
|
6
|
-
|
4
|
+
GraphQL::Batch.batch do
|
5
|
+
as_promise_unless_resolved(super)
|
6
|
+
end
|
7
7
|
rescue GraphQL::InvalidNullError => err
|
8
8
|
err.parent_error? || query.context.errors.push(err)
|
9
9
|
nil
|
10
|
-
ensure
|
11
|
-
GraphQL::Batch::Executor.current.clear
|
12
10
|
end
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
GraphQL::Batch::Promise.resolve(as_promise_unless_resolved(result))
|
12
|
+
# Needed for MutationExecutionStrategy
|
13
|
+
def deep_sync(result) #:nodoc:
|
14
|
+
Promise.sync(as_promise_unless_resolved(result))
|
18
15
|
end
|
19
16
|
|
17
|
+
private
|
18
|
+
|
20
19
|
def as_promise_unless_resolved(result)
|
21
20
|
all_promises = []
|
22
21
|
each_promise(result) do |obj, key, promise|
|
@@ -27,7 +26,7 @@ module GraphQL::Batch
|
|
27
26
|
end
|
28
27
|
end
|
29
28
|
return result if all_promises.empty?
|
30
|
-
Promise.all(all_promises).then { result }
|
29
|
+
::Promise.all(all_promises).then { result }
|
31
30
|
end
|
32
31
|
|
33
32
|
def each_promise(obj, &block)
|
@@ -4,7 +4,11 @@ module GraphQL::Batch
|
|
4
4
|
private_constant :THREAD_KEY
|
5
5
|
|
6
6
|
def self.current
|
7
|
-
Thread.current[THREAD_KEY]
|
7
|
+
Thread.current[THREAD_KEY]
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.current=(executor)
|
11
|
+
Thread.current[THREAD_KEY] = executor
|
8
12
|
end
|
9
13
|
|
10
14
|
attr_reader :loaders
|
@@ -19,19 +23,16 @@ module GraphQL::Batch
|
|
19
23
|
@loading = false
|
20
24
|
end
|
21
25
|
|
26
|
+
def resolve(loader)
|
27
|
+
with_loading(true) { loader.resolve }
|
28
|
+
end
|
29
|
+
|
22
30
|
def shift
|
23
31
|
@loaders.shift.last
|
24
32
|
end
|
25
33
|
|
26
34
|
def tick
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def wait(promise)
|
31
|
-
tick while promise.pending? && !loaders.empty?
|
32
|
-
if promise.pending?
|
33
|
-
promise.reject(BrokenPromiseError.new("Promise wasn't fulfilled after all queries were loaded"))
|
34
|
-
end
|
35
|
+
resolve(shift)
|
35
36
|
end
|
36
37
|
|
37
38
|
def wait_all
|
data/lib/graphql/batch/loader.rb
CHANGED
@@ -2,8 +2,10 @@ 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
|
5
|
+
executor = Executor.current
|
6
|
+
executor.loaders[loader_key] ||= new(*group_args).tap do |loader|
|
6
7
|
loader.loader_key = loader_key
|
8
|
+
loader.executor = executor
|
7
9
|
end
|
8
10
|
end
|
9
11
|
|
@@ -15,21 +17,17 @@ module GraphQL::Batch
|
|
15
17
|
self.for.load_many(keys)
|
16
18
|
end
|
17
19
|
|
18
|
-
attr_accessor :loader_key
|
20
|
+
attr_accessor :loader_key, :executor
|
19
21
|
|
20
22
|
def load(key)
|
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
23
|
cache[cache_key(key)] ||= begin
|
26
24
|
queue << key
|
27
|
-
Promise.new
|
25
|
+
Promise.new.tap { |promise| promise.source = self }
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
31
29
|
def load_many(keys)
|
32
|
-
Promise.all(keys.map { |key| load(key) })
|
30
|
+
::Promise.all(keys.map { |key| load(key) })
|
33
31
|
end
|
34
32
|
|
35
33
|
def resolve #:nodoc:
|
@@ -44,6 +42,16 @@ module GraphQL::Batch
|
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
45
|
+
# For Promise#sync
|
46
|
+
def wait #:nodoc:
|
47
|
+
if executor
|
48
|
+
executor.loaders.delete(loader_key)
|
49
|
+
executor.resolve(self)
|
50
|
+
else
|
51
|
+
resolve
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
47
55
|
protected
|
48
56
|
|
49
57
|
# Fulfill the key with provided value, for use in #perform
|
@@ -91,7 +99,7 @@ module GraphQL::Batch
|
|
91
99
|
|
92
100
|
def check_for_broken_promises(load_keys)
|
93
101
|
each_pending_promise(load_keys) do |key, promise|
|
94
|
-
promise.reject(
|
102
|
+
promise.reject(::Promise::BrokenError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
|
95
103
|
end
|
96
104
|
end
|
97
105
|
end
|
@@ -1,18 +1,17 @@
|
|
1
1
|
module GraphQL::Batch
|
2
2
|
class MutationExecutionStrategy < GraphQL::Batch::ExecutionStrategy
|
3
|
+
attr_accessor :enable_batching
|
4
|
+
|
3
5
|
class FieldResolution < GraphQL::Batch::ExecutionStrategy::FieldResolution
|
4
6
|
def get_finished_value(raw_value)
|
5
|
-
|
6
|
-
|
7
|
-
raw_value = GraphQL::Batch::Promise.resolve(raw_value).sync
|
7
|
+
strategy = execution_context.strategy
|
8
|
+
return super if strategy.enable_batching
|
8
9
|
|
9
|
-
execution_context.strategy.disable_batching = true
|
10
10
|
begin
|
11
|
-
|
12
|
-
|
13
|
-
result
|
11
|
+
strategy.enable_batching = true
|
12
|
+
strategy.deep_sync(Promise.sync(super))
|
14
13
|
ensure
|
15
|
-
|
14
|
+
strategy.enable_batching = false
|
16
15
|
end
|
17
16
|
end
|
18
17
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module GraphQL::Batch
|
2
|
+
module Setup
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def before_query(query)
|
6
|
+
raise NestedError if GraphQL::Batch::Executor.current
|
7
|
+
GraphQL::Batch::Executor.current = GraphQL::Batch::Executor.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def after_query(query)
|
11
|
+
GraphQL::Batch::Executor.current = nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-batch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
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-
|
11
|
+
date: 2016-11-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -36,14 +36,14 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 0.7.
|
39
|
+
version: 0.7.2
|
40
40
|
type: :runtime
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 0.7.
|
46
|
+
version: 0.7.2
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: byebug
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -110,6 +110,7 @@ files:
|
|
110
110
|
- ".gitignore"
|
111
111
|
- ".travis.yml"
|
112
112
|
- Gemfile
|
113
|
+
- Gemfile.graphql13
|
113
114
|
- LICENSE.txt
|
114
115
|
- README.md
|
115
116
|
- Rakefile
|
@@ -122,6 +123,7 @@ files:
|
|
122
123
|
- lib/graphql/batch/loader.rb
|
123
124
|
- lib/graphql/batch/mutation_execution_strategy.rb
|
124
125
|
- lib/graphql/batch/promise.rb
|
126
|
+
- lib/graphql/batch/setup.rb
|
125
127
|
- lib/graphql/batch/version.rb
|
126
128
|
homepage: https://github.com/Shopify/graphql-batch
|
127
129
|
licenses:
|