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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e858070a0f23a5d42dc654bbc6760974bce89b24
4
- data.tar.gz: d4771d661e223aced98ffaac3bf0314251274806
3
+ metadata.gz: 2ee4c1ca2f39afd8e9ecc36c867cc389357169f9
4
+ data.tar.gz: c0282358b5bf23cef97f6055af9b6010743a00c5
5
5
  SHA512:
6
- metadata.gz: 898be9f7e40085e62777a1ed73b89149104b7b95c202201862f1f218d751815384521900eac19dad215cb4ba7b714cc093f48de0c5ae1054966d469a8c7996f8
7
- data.tar.gz: 2800cf159f9441d9d88b03ccfb9239a1217349cf177e3477a23946440b41752b58de716b03811fe7928bfb546192feca9031967fa7e88399b8c13e9467fd21e8
6
+ metadata.gz: a0374a2cfdca4c4b61738914c21966582edb27920ea1ae986bc780dae9c1eb2f159f56228a4098c2d7205103f2125dba161e5c71e6d3f1ace468a7bb1e37f494
7
+ data.tar.gz: fac574fdb4ea3466c46c6a9ac6eda5ee229ecb3882ff2f9f11f65a47c9a987baac869643e319830ef14f4b6b4ef552243bc4f6584ea09e6c27e04f0962197a63
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # GraphQL::Batch
2
2
 
3
+ [![Build Status](https://travis-ci.org/Shopify/graphql-batch.svg?branch=master)](https://travis-ci.org/Shopify/graphql-batch)
4
+ [![Gem Version](https://badge.fury.io/rb/graphql-batch.svg)](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 an perform method for performing the batch load.
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)
@@ -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 = ["Dylan.Smith@shopify.com"]
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", "~> 0.8"
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
- def execute(_, _, _)
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
@@ -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 promises_by_key
9
- @promises_by_key ||= {}
10
+ def self.load(key)
11
+ self.for.load(key)
10
12
  end
11
13
 
12
- def keys
13
- promises_by_key.keys
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
- promises_by_key[key] ||= Promise.new
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
- promises_by_key[key].fulfill(value)
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
- promises_by_key[key].fulfilled?
56
+ promise_for(key).fulfilled?
30
57
  end
31
58
 
32
- # batch load keys and fulfill promises
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
- def resolve
38
- perform(keys)
39
- check_for_broken_promises
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 check_for_broken_promises
49
- promises_by_key.each do |key, promise|
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
- promise.reject(BrokenPromiseError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
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::Query::SerialExecution
3
- class FieldResolution < GraphQL::Query::SerialExecution::FieldResolution
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
- context = execution_context.query.context
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
- context.execution_strategy = old_execution_strategy
15
+ execution_context.strategy.disable_batching = false
16
16
  end
17
17
  end
18
18
  end
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
2
  module Batch
3
- VERSION = "0.2.4"
3
+ VERSION = "0.2.5"
4
4
  end
5
5
  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
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-07-06 00:00:00.000000000 Z
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
- - Dylan.Smith@shopify.com
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.2.3
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