graphql-batch 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
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