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 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