n1_loader 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +15 -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 +3 -3
- data/lib/n1_loader/core/loader.rb +44 -12
- data/lib/n1_loader/core/loader_collection.rb +2 -2
- data/lib/n1_loader/version.rb +1 -1
- data/lib/n1_loader.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d0f96166ae91c856012ea26c933cc56c7d33f68065b753a18adc3c6b9da7e87
|
4
|
+
data.tar.gz: adccc88c382d9283c2af9b41059298fa15563e47615678ef20629a0183a51253
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad3b1273627cf7abb5922cc7139c34e5e9543dc91d296d7e44deea690367d32082dd1ddc028ba40a4ed7fa402a65eb8f5aed36b6d8df0436937e59aaa6cef65a
|
7
|
+
data.tar.gz: fde03e325f2b780a070786f1648a98d278933c84b3266cedf4db50cd268b029839ff06290416846a9ab2a85a25a0795c30684845e4da15909417389dbd1a7a82
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## [1.4.0] - 2022-02-22
|
2
|
+
|
3
|
+
- add support of optional arguments
|
4
|
+
|
5
|
+
BREAKING CHANGES:
|
6
|
+
- rework arguments to use single definition through `argument <name>` only
|
7
|
+
- use keyword arguments
|
8
|
+
|
1
9
|
## [1.3.0] - 2022-02-22
|
2
10
|
|
3
11
|
- add support of named arguments with `argument <name>`
|
data/README.md
CHANGED
@@ -188,21 +188,25 @@ users.map(&:orders_count) # perform will be used once without N+1
|
|
188
188
|
class User
|
189
189
|
include N1Loader::Loadable
|
190
190
|
|
191
|
-
n1_optimized :orders_count do
|
192
|
-
|
193
|
-
|
194
|
-
|
191
|
+
n1_optimized :orders_count do
|
192
|
+
argument :type
|
193
|
+
|
194
|
+
def perform(users)
|
195
|
+
orders_per_user = Order.where(type: type, user: users).group(:user_id).count
|
196
|
+
|
197
|
+
users.each { |user| fulfill(user, orders_per_user[user.id]) }
|
198
|
+
end
|
195
199
|
end
|
196
200
|
end
|
197
201
|
|
198
202
|
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
|
203
|
+
user.orders_count(type: :gifts) # The loader will be performed first time for this argument
|
204
|
+
user.orders_count(type: :sales) # The loader will be performed first time for this argument
|
205
|
+
user.orders_count(type: :gifts) # The cached value will be used
|
202
206
|
|
203
207
|
users = [User.new, User.new]
|
204
208
|
N1Loader::Preloader.new(users).preload(:orders_count)
|
205
|
-
users.map { |user| user.orders_count(:gifts) } # No N+1 here
|
209
|
+
users.map { |user| user.orders_count(type: :gifts) } # No N+1 here
|
206
210
|
```
|
207
211
|
|
208
212
|
_Note_: By default, we use `arguments.map(&:object_id)` to identify arguments but in some cases,
|
@@ -217,7 +221,7 @@ class User
|
|
217
221
|
|
218
222
|
cache_key { sale.id }
|
219
223
|
|
220
|
-
def perform(users
|
224
|
+
def perform(users)
|
221
225
|
orders_per_user = Order.where(sale: sale, user: users).group(:user_id).count
|
222
226
|
|
223
227
|
users.each { |user| fulfill(user, orders_per_user[user.id]) }
|
@@ -226,8 +230,8 @@ class User
|
|
226
230
|
end
|
227
231
|
|
228
232
|
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
|
233
|
+
user.orders_count(sale: Sale.first) # perform will be executed and value will be cached
|
234
|
+
user.orders_count(sale: Sale.first) # the cached value will be returned
|
231
235
|
```
|
232
236
|
|
233
237
|
|
@@ -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
|
@@ -47,7 +47,7 @@ module N1Loader
|
|
47
47
|
|
48
48
|
def n1_optimized(name, loader = nil, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
49
49
|
loader ||= Class.new(N1Loader::Loader) do
|
50
|
-
if block
|
50
|
+
if block.arity == 1
|
51
51
|
define_method(:perform, &block)
|
52
52
|
else
|
53
53
|
class_eval(&block)
|
@@ -73,10 +73,10 @@ module N1Loader
|
|
73
73
|
instance_variable_get(loader_variable_name) || send("#{loader_name}_reload")
|
74
74
|
end
|
75
75
|
|
76
|
-
define_method(name) do
|
76
|
+
define_method(name) do |reload: false, **args|
|
77
77
|
send("#{loader_name}_reload") if reload
|
78
78
|
|
79
|
-
send(loader_name).with(
|
79
|
+
send(loader_name).with(**args).for(self)
|
80
80
|
end
|
81
81
|
|
82
82
|
[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
|
data/lib/n1_loader/version.rb
CHANGED
data/lib/n1_loader.rb
CHANGED