batch-loader 1.0.2 → 1.0.3

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: bb37551b8e0c52cb65a331801fa0110e311aebf9
4
- data.tar.gz: 9f5a59bd9626c0341103c2d5c422119ff71aedba
3
+ metadata.gz: daca6a2439fe33924b7377eb3b37c15f9226e6ae
4
+ data.tar.gz: 11a8c50e28d6153de19f3ce7c76703a14995ac91
5
5
  SHA512:
6
- metadata.gz: 92e829a4416c25050380ef59dbe43873f89911140ade537b9420bc05fb6bb45364591781010b0b0ecab5f71e1a5ec1bb56fa15f4cdb6ef4859547687e3bb30d0
7
- data.tar.gz: 6101f58d4292906aa512ae16e88f3c563c351984c2edea960fc1d4ff7ddd87a95dbb69e0fdd201376e93d0f189638f27ad0a76ff191c30dc79ba6d5fbf61b5c2
6
+ metadata.gz: 8ec2f1daee17b1d27f8bd8f30b542a5590634f1726e48913e8d6ca8e6d1e32455cadf4172a43e769627a44aa7e38a04a4e87f36838623de43e8f56077066d035
7
+ data.tar.gz: 285454c49fb476088ab0d1cbe1a56671da695b5a1f3d9e69ac61b1070bb74d196653714ed01b457d8fab97e1443ffbcc0e17b2407348146fec16c424d278dd73
@@ -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.2...HEAD)
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.
@@ -1,7 +1,5 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
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
@@ -1 +1 @@
1
- require "batch_loader"
1
+ require_relative "./batch_loader"
@@ -1,17 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
- require "forwardable"
5
4
 
6
- require "batch_loader/version"
7
- require "batch_loader/executor_proxy"
8
- require "batch_loader/middleware"
9
- require "batch_loader/graphql"
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
- extend Forwardable
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
- executor_proxy.add(item: @item)
28
+ __executor_proxy.add(item: @item)
32
29
 
33
- singleton_class.class_eval { undef_method(:batch) }
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
- sync!.public_send(method_name, *args, &block)
61
+ __sync!.public_send(method_name, *args, &block)
54
62
  end
55
63
 
56
- def sync!
57
- return self if @synced
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
- replace_with!(loaded_value)
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 ensure_batched
73
- return if executor_proxy.value_loaded?(item: @item)
74
+ def __ensure_batched
75
+ return if __executor_proxy.value_loaded?(item: @item)
74
76
 
75
- items = executor_proxy.list_items
76
- loader = ->(item, value) { executor_proxy.load(item: item, value: 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 executor_proxy.value_loaded?(item: item)
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
- executor_proxy.delete(items: items)
85
+ __executor_proxy.delete(items: items)
84
86
  end
85
87
 
86
- def singleton_class
88
+ def __singleton_class
87
89
  class << self ; self ; end
88
90
  end
89
91
 
90
- def replace_with!(value)
91
- @loaded_value = value
92
- singleton_class.class_eval { def_delegators :@loaded_value, *(value.methods - LEFT_INSTANCE_METHODS) }
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 purge_cache
96
- executor_proxy.unload_value(item: @item)
97
- executor_proxy.add(item: @item)
104
+ def __purge_cache
105
+ __executor_proxy.unload_value(item: @item)
106
+ __executor_proxy.add(item: @item)
98
107
  end
99
108
 
100
- def executor_proxy
101
- @executor_proxy ||= begin
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "batch_loader/executor"
3
+ require_relative "./executor"
4
4
 
5
5
  class BatchLoader
6
6
  class ExecutorProxy
@@ -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?(:batch_loader?) ? BatchLoader::GraphQL::Wrapper.new(result) : result
24
+ result.respond_to?(:__sync) ? BatchLoader::GraphQL::Wrapper.new(result) : result
25
25
  end
26
26
 
27
27
  field.redefine { resolve(new_resolve_proc) }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class BatchLoader
4
- VERSION = "1.0.2"
4
+ VERSION = "1.0.3"
5
5
  end
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.2
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-14 00:00:00.000000000 Z
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