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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2ee4c1ca2f39afd8e9ecc36c867cc389357169f9
4
- data.tar.gz: c0282358b5bf23cef97f6055af9b6010743a00c5
3
+ metadata.gz: dc3c6396e81315bc1c3a008f3b19e8e249eb2c46
4
+ data.tar.gz: 02272cc3f0ba19341379ea4d2534fda470698c1c
5
5
  SHA512:
6
- metadata.gz: a0374a2cfdca4c4b61738914c21966582edb27920ea1ae986bc780dae9c1eb2f159f56228a4098c2d7205103f2125dba161e5c71e6d3f1ace468a7bb1e37f494
7
- data.tar.gz: fac574fdb4ea3466c46c6a9ac6eda5ee229ecb3882ff2f9f11f65a47c9a987baac869643e319830ef14f4b6b4ef552243bc4f6584ea09e6c27e04f0962197a63
6
+ metadata.gz: f9f63fc31a2231ce25bcf11c8f8f9b96d38131712b6977d1b8bba6f288b168720450fbdbf07a926044f34a6bfeb6e3f16b561a0065d0adb80bb64df7a4a19a69
7
+ data.tar.gz: 04e5caec058af7eb05c441d519c76a63a33b7c6108f1cfb96b8f42e19f0b54f936d548b23f14c2b09f694ca9b87315a7526c120781fd2b8abadf4ad1b10ed549
data/.gitignore CHANGED
@@ -1,6 +1,6 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
3
+ /Gemfile*.lock
4
4
  /_yardoc/
5
5
  /coverage/
6
6
  /doc/
data/.travis.yml CHANGED
@@ -2,4 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 2.1
4
4
  - 2.2
5
- before_install: gem install bundler -v 1.11.2
5
+ gemfile:
6
+ - Gemfile
7
+ - Gemfile.graphql13
8
+ before_install: gem install bundler -v 1.13.3
data/Gemfile.graphql13 ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'graphql', github: 'rmosolgo/graphql-ruby', branch: 'master'
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.new(query: MyQueryType)
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
- GraphQL::Batch::Promise#sync can be used to wait for a promise to be resolved and return its result. This can be useful for debugging and unit testing loaders.
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
- query = RecordLoader.for(Product).load(args["id"]).then(&:title)
118
- assert_equal product.title, query.sync
119
- end
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
 
@@ -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.0.rc2"
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 = Class.new(StandardError)
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/execution_strategy"
15
- require_relative "batch/mutation_execution_strategy"
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
- as_promise(super).sync
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
- private
15
-
16
- def as_promise(result)
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] ||= new
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
- with_loading(true) { shift.resolve }
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
@@ -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.loaders[loader_key] ||= new(*group_args).tap do |loader|
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(BrokenPromiseError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
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
- return super if execution_context.strategy.disable_batching
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
- result = super(raw_value)
12
- GraphQL::Batch::Executor.current.wait_all
13
- result
11
+ strategy.enable_batching = true
12
+ strategy.deep_sync(Promise.sync(super))
14
13
  ensure
15
- execution_context.strategy.disable_batching = false
14
+ strategy.enable_batching = false
16
15
  end
17
16
  end
18
17
  end
@@ -1,11 +1,8 @@
1
1
  module GraphQL::Batch
2
2
  class Promise < ::Promise
3
- def wait
4
- Executor.current.wait(self)
5
- end
6
-
7
3
  def defer
8
- Executor.current.defer { super }
4
+ executor = Executor.current
5
+ executor ? executor.defer { super } : super
9
6
  end
10
7
  end
11
8
  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
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
2
  module Batch
3
- VERSION = "0.2.5"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  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.2.5
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-08 00:00:00.000000000 Z
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.0.rc2
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.0.rc2
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: