batch-loader 1.0.2 → 1.0.3
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/CHANGELOG.md +54 -9
- data/batch-loader.gemspec +2 -3
- data/lib/batch-loader.rb +1 -1
- data/lib/batch_loader.rb +48 -39
- data/lib/batch_loader/executor_proxy.rb +1 -1
- data/lib/batch_loader/graphql.rb +2 -2
- data/lib/batch_loader/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: daca6a2439fe33924b7377eb3b37c15f9226e6ae
|
4
|
+
data.tar.gz: 11a8c50e28d6153de19f3ce7c76703a14995ac91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ec2f1daee17b1d27f8bd8f30b542a5590634f1726e48913e8d6ca8e6d1e32455cadf4172a43e769627a44aa7e38a04a4e87f36838623de43e8f56077066d035
|
7
|
+
data.tar.gz: 285454c49fb476088ab0d1cbe1a56671da695b5a1f3d9e69ac61b1070bb74d196653714ed01b457d8fab97e1443ffbcc0e17b2407348146fec16c424d278dd73
|
data/CHANGELOG.md
CHANGED
@@ -8,13 +8,44 @@ one of the following labels: `Added`, `Changed`, `Deprecated`,
|
|
8
8
|
to manage the versions of this gem so
|
9
9
|
that you can set version constraints properly.
|
10
10
|
|
11
|
-
#### [Unreleased](https://github.com/exAspArk/batch-loader/compare/v1.0.
|
11
|
+
#### [Unreleased](https://github.com/exAspArk/batch-loader/compare/v1.0.3...HEAD)
|
12
12
|
|
13
13
|
* WIP
|
14
14
|
|
15
|
+
#### [v1.0.3](https://github.com/exAspArk/batch-loader/compare/v1.0.2...v1.0.3) – 2017-09-18
|
16
|
+
|
17
|
+
* `Fixed`: auto syncing performance up to 30x times compared to [v1.0.2](https://github.com/exAspArk/batch-loader/blob/master/CHANGELOG.md#v102--2017-09-14). Ruby `Forwardable` with `def_delegators` is too slow.
|
18
|
+
* `Fixed`: GraphQL performance up to 3x times by disabling auto syncing in favor of syncing with [graphql-ruby](https://github.com/rmosolgo/graphql-ruby) `lazy_resolve`.
|
19
|
+
* `Added`: more benchmarks.
|
20
|
+
|
15
21
|
#### [v1.0.2](https://github.com/exAspArk/batch-loader/compare/v1.0.1...v1.0.2) – 2017-09-14
|
16
22
|
|
17
23
|
* `Added`: `BatchLoader#inspect` method because of Pry, which [swallows errors](https://github.com/pry/pry/issues/1642).
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# Before:
|
27
|
+
require 'pry'
|
28
|
+
binding.pry
|
29
|
+
|
30
|
+
pry(main)> result = BatchLoader.for(1).batch { |ids, loader| raise "Oops" };
|
31
|
+
pry(main)> result # Pry called result.inspect and swallowed the "Oops" error
|
32
|
+
# => #<BatchLoader:0x8>
|
33
|
+
pry(main)> result.id
|
34
|
+
# => NoMethodError: undefined method `id' for nil:NilClass
|
35
|
+
```
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# After:
|
39
|
+
require 'pry'
|
40
|
+
binding.pry
|
41
|
+
|
42
|
+
pry(main)> result = BatchLoader.for(1).batch { |ids, loader| raise "Oops" };
|
43
|
+
pry(main)> result
|
44
|
+
# => #<BatchLoader:0x140653946335160>
|
45
|
+
pry(main)> result.id
|
46
|
+
# => RuntimeError: Oops
|
47
|
+
```
|
48
|
+
|
18
49
|
* `Added`: benchmarks.
|
19
50
|
* `Fixed`: caching `nil`s for not loaded values only after successful `#batch` execution.
|
20
51
|
* `Changed`: internal implementation with Ruby `Forwardable`, don't delegate methods like `object_id` and `__send__`.
|
@@ -27,42 +58,56 @@ that you can set version constraints properly.
|
|
27
58
|
|
28
59
|
* `Removed`: `BatchLoader.sync!` and `BatchLoader#sync`. Now syncing is done implicitly when you call any method on the lazy object.
|
29
60
|
|
30
|
-
Before:
|
31
|
-
|
32
61
|
```ruby
|
33
62
|
def load_user(user_id)
|
34
63
|
BatchLoader.for(user_id).batch { ... }
|
35
64
|
end
|
36
65
|
|
66
|
+
# Before:
|
37
67
|
users = [load_user(1), load_user(2), load_user(3)]
|
38
68
|
puts BatchLoader.sync!(users) # or users.map!(&:sync)
|
39
69
|
```
|
40
70
|
|
41
|
-
After:
|
42
|
-
|
43
71
|
```ruby
|
72
|
+
# After:
|
44
73
|
users = [load_user(1), load_user(2), load_user(3)]
|
45
74
|
puts users
|
46
75
|
```
|
47
76
|
|
48
77
|
* `Removed`: `BatchLoader#load`. Use `loader` lambda instead:
|
49
78
|
|
50
|
-
Before:
|
51
|
-
|
52
79
|
```ruby
|
80
|
+
# Before:
|
53
81
|
BatchLoader.for(user_id).batch do |user_ids, batch_loader|
|
54
82
|
user_ids.each { |user_id| batch_loader.load(user_id, user_id) }
|
55
83
|
end
|
56
84
|
```
|
57
85
|
|
58
|
-
After:
|
59
|
-
|
60
86
|
```ruby
|
87
|
+
# After:
|
61
88
|
BatchLoader.for(user_id).batch do |user_ids, loader|
|
62
89
|
user_ids.each { |user_id| loader.call(user_id, user_id) }
|
63
90
|
end
|
64
91
|
```
|
65
92
|
|
93
|
+
* `Changed`: use `BatchLoader::GraphQL` in GraphQL schema:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# Before:
|
97
|
+
Schema = GraphQL::Schema.define do
|
98
|
+
# ...
|
99
|
+
lazy_resolve BatchLoader, :sync
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
# After:
|
105
|
+
Schema = GraphQL::Schema.define do
|
106
|
+
# ...
|
107
|
+
use BatchLoader::GraphQL
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
66
111
|
#### [v0.3.0](https://github.com/exAspArk/batch-loader/compare/v0.2.0...v0.3.0) – 2017-08-03
|
67
112
|
|
68
113
|
* `Added`: `BatchLoader::Executor.clear_current` to clear cache manually.
|
data/batch-loader.gemspec
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "batch_loader/version"
|
2
|
+
require_relative "./lib/batch_loader/version"
|
5
3
|
|
6
4
|
Gem::Specification.new do |spec|
|
7
5
|
spec.name = "batch-loader"
|
@@ -29,4 +27,5 @@ Gem::Specification.new do |spec|
|
|
29
27
|
spec.add_development_dependency "graphql", "~> 1.6"
|
30
28
|
spec.add_development_dependency "pry-byebug", "~> 3.4"
|
31
29
|
spec.add_development_dependency "benchmark-ips", "~> 2.7"
|
30
|
+
spec.add_development_dependency "ruby-prof", "~> 0.16"
|
32
31
|
end
|
data/lib/batch-loader.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require_relative "./batch_loader"
|
data/lib/batch_loader.rb
CHANGED
@@ -1,17 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "set"
|
4
|
-
require "forwardable"
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
require_relative "./batch_loader/version"
|
6
|
+
require_relative "./batch_loader/executor_proxy"
|
7
|
+
require_relative "./batch_loader/middleware"
|
8
|
+
require_relative "./batch_loader/graphql"
|
10
9
|
|
11
10
|
class BatchLoader
|
12
|
-
|
13
|
-
|
14
|
-
IMPLEMENTED_INSTANCE_METHODS = %i[object_id __id__ __send__ singleton_method_added batch_loader? respond_to? batch inspect].freeze
|
11
|
+
IMPLEMENTED_INSTANCE_METHODS = %i[object_id __id__ __send__ singleton_method_added __sync respond_to? batch inspect].freeze
|
15
12
|
REPLACABLE_INSTANCE_METHODS = %i[batch inspect].freeze
|
16
13
|
LEFT_INSTANCE_METHODS = (IMPLEMENTED_INSTANCE_METHODS - REPLACABLE_INSTANCE_METHODS).freeze
|
17
14
|
|
@@ -28,17 +25,13 @@ class BatchLoader
|
|
28
25
|
def batch(cache: true, &batch_block)
|
29
26
|
@cache = cache
|
30
27
|
@batch_block = batch_block
|
31
|
-
|
28
|
+
__executor_proxy.add(item: @item)
|
32
29
|
|
33
|
-
|
30
|
+
__singleton_class.class_eval { undef_method(:batch) }
|
34
31
|
|
35
32
|
self
|
36
33
|
end
|
37
34
|
|
38
|
-
def batch_loader?
|
39
|
-
true
|
40
|
-
end
|
41
|
-
|
42
35
|
def respond_to?(method_name)
|
43
36
|
LEFT_INSTANCE_METHODS.include?(method_name) || method_missing(:respond_to?, method_name)
|
44
37
|
end
|
@@ -47,58 +40,74 @@ class BatchLoader
|
|
47
40
|
"#<BatchLoader:0x#{(object_id << 1)}>"
|
48
41
|
end
|
49
42
|
|
43
|
+
def __sync
|
44
|
+
return @loaded_value if @synced
|
45
|
+
|
46
|
+
__ensure_batched
|
47
|
+
@loaded_value = __executor_proxy.loaded_value(item: @item)
|
48
|
+
|
49
|
+
if @cache
|
50
|
+
@synced = true
|
51
|
+
else
|
52
|
+
__purge_cache
|
53
|
+
end
|
54
|
+
|
55
|
+
@loaded_value
|
56
|
+
end
|
57
|
+
|
50
58
|
private
|
51
59
|
|
52
60
|
def method_missing(method_name, *args, &block)
|
53
|
-
|
61
|
+
__sync!.public_send(method_name, *args, &block)
|
54
62
|
end
|
55
63
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
ensure_batched
|
60
|
-
loaded_value = executor_proxy.loaded_value(item: @item)
|
64
|
+
def __sync!
|
65
|
+
loaded_value = __sync
|
61
66
|
|
62
67
|
if @cache
|
63
|
-
|
64
|
-
@synced = true
|
65
|
-
self
|
68
|
+
__replace_with!(loaded_value)
|
66
69
|
else
|
67
|
-
purge_cache
|
68
70
|
loaded_value
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
|
-
def
|
73
|
-
return if
|
74
|
+
def __ensure_batched
|
75
|
+
return if __executor_proxy.value_loaded?(item: @item)
|
74
76
|
|
75
|
-
items =
|
76
|
-
loader = ->(item, value) {
|
77
|
+
items = __executor_proxy.list_items
|
78
|
+
loader = ->(item, value) { __executor_proxy.load(item: item, value: value) }
|
77
79
|
|
78
80
|
@batch_block.call(items, loader)
|
79
81
|
items.each do |item|
|
80
|
-
next if
|
82
|
+
next if __executor_proxy.value_loaded?(item: item)
|
81
83
|
loader.call(item, nil) # use "nil" for not loaded item after succesfull batching
|
82
84
|
end
|
83
|
-
|
85
|
+
__executor_proxy.delete(items: items)
|
84
86
|
end
|
85
87
|
|
86
|
-
def
|
88
|
+
def __singleton_class
|
87
89
|
class << self ; self ; end
|
88
90
|
end
|
89
91
|
|
90
|
-
def
|
91
|
-
|
92
|
-
|
92
|
+
def __replace_with!(value)
|
93
|
+
__singleton_class.class_eval do
|
94
|
+
(value.methods - LEFT_INSTANCE_METHODS).each do |method_name|
|
95
|
+
define_method(method_name) do |*args, &block|
|
96
|
+
value.public_send(method_name, *args, &block)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
self
|
93
102
|
end
|
94
103
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
104
|
+
def __purge_cache
|
105
|
+
__executor_proxy.unload_value(item: @item)
|
106
|
+
__executor_proxy.add(item: @item)
|
98
107
|
end
|
99
108
|
|
100
|
-
def
|
101
|
-
@
|
109
|
+
def __executor_proxy
|
110
|
+
@__executor_proxy ||= begin
|
102
111
|
raise NoBatchError.new("Please provide a batch block first") unless @batch_block
|
103
112
|
BatchLoader::ExecutorProxy.new(&@batch_block)
|
104
113
|
end
|
data/lib/batch_loader/graphql.rb
CHANGED
@@ -8,7 +8,7 @@ class BatchLoader
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def sync
|
11
|
-
@batch_loader
|
11
|
+
@batch_loader.__sync
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -21,7 +21,7 @@ class BatchLoader
|
|
21
21
|
old_resolve_proc = field.resolve_proc
|
22
22
|
new_resolve_proc = ->(object, arguments, context) do
|
23
23
|
result = old_resolve_proc.call(object, arguments, context)
|
24
|
-
result.respond_to?(:
|
24
|
+
result.respond_to?(:__sync) ? BatchLoader::GraphQL::Wrapper.new(result) : result
|
25
25
|
end
|
26
26
|
|
27
27
|
field.redefine { resolve(new_resolve_proc) }
|
data/lib/batch_loader/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: batch-loader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- exAspArk
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '2.7'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: ruby-prof
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.16'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.16'
|
97
111
|
description: Powerful tool to avoid N+1 DB or HTTP queries
|
98
112
|
email:
|
99
113
|
- exaspark@gmail.com
|