n1_loader 1.3.0 → 1.4.2
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 +16 -0
- data/README.md +18 -11
- data/lib/n1_loader/active_record/loader_collection.rb +1 -3
- data/lib/n1_loader/ar_lazy_preload/loader_collection_patch.rb +1 -1
- data/lib/n1_loader/core/loadable.rb +15 -3
- data/lib/n1_loader/core/loader.rb +44 -12
- data/lib/n1_loader/core/loader_collection.rb +2 -2
- data/lib/n1_loader/core/preloader.rb +12 -2
- data/lib/n1_loader/version.rb +1 -1
- data/lib/n1_loader.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f72c7428f78489c25222fb2ab8d56fde519c680fdc010115b952637c2291ca93
|
|
4
|
+
data.tar.gz: 7149852481ffed8d1fd0fc25e28fc045eda0a0f2936e748461dd7f43d4f709f1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0d0802914b84d4c25a97c8c328ff2ae4453cf8fcd4eee87de1955b06b810c874d9982498b5fc80ecf1fa77238971e51ef5f162f809914876a1811084b366ddfe
|
|
7
|
+
data.tar.gz: fa001ab34b7165ee35ba31ef449c837418d538e52977ee984b38e35b767df06c05ef0cb01949c9290c17ae40bcd3e3644f00374cf7f66d7e6811f094515c37da
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [1.4.2] - 2022-03-01
|
|
2
|
+
|
|
3
|
+
- Add n1_clear_cache method which is useful for cases like reload in ActiveRecord
|
|
4
|
+
|
|
5
|
+
## [1.4.1] - 2022-02-24
|
|
6
|
+
|
|
7
|
+
- Fix preloading of invalid objects
|
|
8
|
+
|
|
9
|
+
## [1.4.0] - 2022-02-22
|
|
10
|
+
|
|
11
|
+
- add support of optional arguments
|
|
12
|
+
|
|
13
|
+
BREAKING CHANGES:
|
|
14
|
+
- rework arguments to use single definition through `argument <name>` only
|
|
15
|
+
- use keyword arguments
|
|
16
|
+
|
|
1
17
|
## [1.3.0] - 2022-02-22
|
|
2
18
|
|
|
3
19
|
- add support of named arguments with `argument <name>`
|
data/README.md
CHANGED
|
@@ -129,6 +129,9 @@ end
|
|
|
129
129
|
user = User.new
|
|
130
130
|
user.orders_count # => loader is executed first time and value was cached
|
|
131
131
|
user.orders_count(reload: true) # => loader is executed again and a new value was cached
|
|
132
|
+
# or
|
|
133
|
+
user.n1_clear_cache
|
|
134
|
+
user.orders_count
|
|
132
135
|
|
|
133
136
|
users = [User.new, User.new]
|
|
134
137
|
N1Loader::Preloader.new(users).preload(:orders_count) # => loader was initialized but not yet executed
|
|
@@ -188,21 +191,25 @@ users.map(&:orders_count) # perform will be used once without N+1
|
|
|
188
191
|
class User
|
|
189
192
|
include N1Loader::Loadable
|
|
190
193
|
|
|
191
|
-
n1_optimized :orders_count do
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
n1_optimized :orders_count do
|
|
195
|
+
argument :type
|
|
196
|
+
|
|
197
|
+
def perform(users)
|
|
198
|
+
orders_per_user = Order.where(type: type, user: users).group(:user_id).count
|
|
199
|
+
|
|
200
|
+
users.each { |user| fulfill(user, orders_per_user[user.id]) }
|
|
201
|
+
end
|
|
195
202
|
end
|
|
196
203
|
end
|
|
197
204
|
|
|
198
205
|
user = User.new
|
|
199
|
-
user.orders_count(:gifts) # The loader will be performed first time for this argument
|
|
200
|
-
user.orders_count(:sales) # The loader will be performed first time for this argument
|
|
201
|
-
user.orders_count(:gifts) # The cached value will be used
|
|
206
|
+
user.orders_count(type: :gifts) # The loader will be performed first time for this argument
|
|
207
|
+
user.orders_count(type: :sales) # The loader will be performed first time for this argument
|
|
208
|
+
user.orders_count(type: :gifts) # The cached value will be used
|
|
202
209
|
|
|
203
210
|
users = [User.new, User.new]
|
|
204
211
|
N1Loader::Preloader.new(users).preload(:orders_count)
|
|
205
|
-
users.map { |user| user.orders_count(:gifts) } # No N+1 here
|
|
212
|
+
users.map { |user| user.orders_count(type: :gifts) } # No N+1 here
|
|
206
213
|
```
|
|
207
214
|
|
|
208
215
|
_Note_: By default, we use `arguments.map(&:object_id)` to identify arguments but in some cases,
|
|
@@ -217,7 +224,7 @@ class User
|
|
|
217
224
|
|
|
218
225
|
cache_key { sale.id }
|
|
219
226
|
|
|
220
|
-
def perform(users
|
|
227
|
+
def perform(users)
|
|
221
228
|
orders_per_user = Order.where(sale: sale, user: users).group(:user_id).count
|
|
222
229
|
|
|
223
230
|
users.each { |user| fulfill(user, orders_per_user[user.id]) }
|
|
@@ -226,8 +233,8 @@ class User
|
|
|
226
233
|
end
|
|
227
234
|
|
|
228
235
|
user = User.new
|
|
229
|
-
user.orders_count(Sale.first) # perform will be executed and value will be cached
|
|
230
|
-
user.orders_count(Sale.first) # the cached value will be returned
|
|
236
|
+
user.orders_count(sale: Sale.first) # perform will be executed and value will be cached
|
|
237
|
+
user.orders_count(sale: Sale.first) # the cached value will be returned
|
|
231
238
|
```
|
|
232
239
|
|
|
233
240
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
N1Loader::LoaderCollection.define_method :preloaded_records do
|
|
4
|
-
|
|
5
|
-
raise N1Loader::ActiveRecord::InvalidPreloading, "Cannot preload loader with arguments"
|
|
6
|
-
end
|
|
4
|
+
raise N1Loader::ActiveRecord::InvalidPreloading, "Cannot preload loader with arguments" if loader_class.arguments
|
|
7
5
|
|
|
8
6
|
with.preloaded_records
|
|
9
7
|
end
|
|
@@ -32,6 +32,12 @@ module N1Loader
|
|
|
32
32
|
send("#{name}_loader=", loader_collection)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def n1_clear_cache
|
|
36
|
+
self.class.n1_loaders.each do |name|
|
|
37
|
+
n1_loader_set(name, nil)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
35
41
|
def self.included(base)
|
|
36
42
|
base.extend(ClassMethods)
|
|
37
43
|
end
|
|
@@ -45,9 +51,13 @@ module N1Loader
|
|
|
45
51
|
respond_to?("#{name}_loader")
|
|
46
52
|
end
|
|
47
53
|
|
|
54
|
+
def n1_loaders
|
|
55
|
+
@n1_loaders ||= superclass.respond_to?(:n1_loaders) ? superclass.n1_loaders.dup : []
|
|
56
|
+
end
|
|
57
|
+
|
|
48
58
|
def n1_optimized(name, loader = nil, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
49
59
|
loader ||= Class.new(N1Loader::Loader) do
|
|
50
|
-
if block
|
|
60
|
+
if block.arity == 1
|
|
51
61
|
define_method(:perform, &block)
|
|
52
62
|
else
|
|
53
63
|
class_eval(&block)
|
|
@@ -56,6 +66,8 @@ module N1Loader
|
|
|
56
66
|
loader_name = "#{name}_loader"
|
|
57
67
|
loader_variable_name = "@#{loader_name}"
|
|
58
68
|
|
|
69
|
+
n1_loaders << name
|
|
70
|
+
|
|
59
71
|
define_singleton_method(loader_name) do
|
|
60
72
|
loader
|
|
61
73
|
end
|
|
@@ -73,10 +85,10 @@ module N1Loader
|
|
|
73
85
|
instance_variable_get(loader_variable_name) || send("#{loader_name}_reload")
|
|
74
86
|
end
|
|
75
87
|
|
|
76
|
-
define_method(name) do
|
|
88
|
+
define_method(name) do |reload: false, **args|
|
|
77
89
|
send("#{loader_name}_reload") if reload
|
|
78
90
|
|
|
79
|
-
send(loader_name).with(
|
|
91
|
+
send(loader_name).with(**args).for(self)
|
|
80
92
|
end
|
|
81
93
|
|
|
82
94
|
[name, loader_name, loader_variable_name]
|
|
@@ -13,11 +13,16 @@ module N1Loader
|
|
|
13
13
|
#
|
|
14
14
|
# First defined argument will have the value of first passed argument,
|
|
15
15
|
# meaning the order is important.
|
|
16
|
-
|
|
16
|
+
#
|
|
17
|
+
# @param name [Symbol]
|
|
18
|
+
# @param opts [Hash]
|
|
19
|
+
# @option opts [Boolean] optional false by default
|
|
20
|
+
def argument(name, **opts)
|
|
17
21
|
@arguments ||= []
|
|
18
|
-
|
|
19
|
-
define_method(name) { args[
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
define_method(name) { args[name] }
|
|
24
|
+
|
|
25
|
+
@arguments << opts.merge(name: name)
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
# Defines a custom cache key that is calculated for passed arguments.
|
|
@@ -29,7 +34,7 @@ module N1Loader
|
|
|
29
34
|
end
|
|
30
35
|
end
|
|
31
36
|
|
|
32
|
-
def initialize(elements,
|
|
37
|
+
def initialize(elements, **args)
|
|
33
38
|
@elements = elements
|
|
34
39
|
@args = args
|
|
35
40
|
end
|
|
@@ -44,18 +49,45 @@ module N1Loader
|
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
def cache_key
|
|
47
|
-
|
|
52
|
+
check_arguments!
|
|
53
|
+
args.values.map(&:object_id)
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
private
|
|
51
57
|
|
|
52
58
|
attr_reader :elements, :args
|
|
53
59
|
|
|
60
|
+
def check_missing_arguments! # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
61
|
+
return unless (arguments = self.class.arguments)
|
|
62
|
+
|
|
63
|
+
min = arguments.count { |argument| !argument[:optional] }
|
|
64
|
+
max = arguments.count
|
|
65
|
+
|
|
66
|
+
return if args.size >= min && args.size <= max
|
|
67
|
+
|
|
68
|
+
str =
|
|
69
|
+
if min == max
|
|
70
|
+
max.to_s
|
|
71
|
+
else
|
|
72
|
+
"#{min}..#{max}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
raise MissingArgument, "Loader requires #{str} arguments but #{args.size} were given"
|
|
76
|
+
end
|
|
77
|
+
|
|
54
78
|
def check_arguments!
|
|
55
|
-
|
|
56
|
-
|
|
79
|
+
check_missing_arguments!
|
|
80
|
+
check_invalid_arguments!
|
|
81
|
+
end
|
|
57
82
|
|
|
58
|
-
|
|
83
|
+
def check_invalid_arguments!
|
|
84
|
+
return unless (arguments = self.class.arguments)
|
|
85
|
+
|
|
86
|
+
args.each_key do |arg|
|
|
87
|
+
next if arguments.find { |argument| argument[:name] == arg }
|
|
88
|
+
|
|
89
|
+
raise InvalidArgument, "Loader doesn't define #{arg} argument"
|
|
90
|
+
end
|
|
59
91
|
end
|
|
60
92
|
|
|
61
93
|
def perform(_elements)
|
|
@@ -66,7 +98,7 @@ module N1Loader
|
|
|
66
98
|
@loaded[element] = value
|
|
67
99
|
end
|
|
68
100
|
|
|
69
|
-
def loaded
|
|
101
|
+
def loaded
|
|
70
102
|
return @loaded if @loaded
|
|
71
103
|
|
|
72
104
|
check_arguments!
|
|
@@ -74,9 +106,9 @@ module N1Loader
|
|
|
74
106
|
@loaded = {}.compare_by_identity
|
|
75
107
|
|
|
76
108
|
if elements.size == 1 && respond_to?(:single)
|
|
77
|
-
fulfill(elements.first, single(elements.first
|
|
109
|
+
fulfill(elements.first, single(elements.first))
|
|
78
110
|
elsif elements.any?
|
|
79
|
-
perform(elements
|
|
111
|
+
perform(elements)
|
|
80
112
|
end
|
|
81
113
|
|
|
82
114
|
@loaded
|
|
@@ -17,13 +17,23 @@ module N1Loader
|
|
|
17
17
|
def preload(*keys)
|
|
18
18
|
keys.flatten(1).flat_map do |key|
|
|
19
19
|
elements
|
|
20
|
-
.group_by { |element| element
|
|
20
|
+
.group_by { |element| loader_class(element, key) }
|
|
21
21
|
.map do |loader_class, grouped_elements|
|
|
22
|
+
next unless loader_class
|
|
23
|
+
|
|
22
24
|
loader_collection = N1Loader::LoaderCollection.new(loader_class, grouped_elements)
|
|
23
25
|
grouped_elements.each { |grouped_element| grouped_element.n1_loader_set(key, loader_collection) }
|
|
24
26
|
loader_collection
|
|
25
|
-
end
|
|
27
|
+
end.compact
|
|
26
28
|
end
|
|
27
29
|
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def loader_class(element, key)
|
|
34
|
+
element.class.respond_to?(:n1_loader_defined?) &&
|
|
35
|
+
element.class.n1_loader_defined?(key) &&
|
|
36
|
+
element.class.n1_loader(key)
|
|
37
|
+
end
|
|
28
38
|
end
|
|
29
39
|
end
|
data/lib/n1_loader/version.rb
CHANGED
data/lib/n1_loader.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: n1_loader
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evgeniy Demin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-03-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|