batch-loader-active-record 0.3.1 → 0.4.0
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 +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +26 -3
- data/lib/batch_loader_active_record.rb +20 -7
- data/lib/batch_loader_active_record/association_manager.rb +30 -5
- data/lib/batch_loader_active_record/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 677a8d4077a4cb32b88a1622cf0ff134b8ee496f
|
4
|
+
data.tar.gz: 1379446245d1cfda4b6e24b533ea6cbf10dde8ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d4084ac2f43f85b4a1cb67cd7ed122b23ed79af1f62abb6a61afe9c09ef094f4b5934541799417d0e81f32e2d0b330c0d5b708fc5baed3f4136242efec357b1
|
7
|
+
data.tar.gz: 5fb6c5f42f3144b2b65af518efb714f372f2b48270dead47fa0892616daceca53e4d5a7c91b23095ea25a57a3b72977659154e02df7448f39a0e08622490131e
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -21,6 +21,7 @@ It is also possible to use one of the macros below in replacement of the origina
|
|
21
21
|
* `belongs_to_lazy`
|
22
22
|
* `has_one_lazy`
|
23
23
|
* `has_many_lazy`
|
24
|
+
* `has_and_belongs_to_many_lazy`
|
24
25
|
|
25
26
|
As soon as your lazy association accessor needs to do more than fetch all records of an association (using a scope or not), you're going to want to directly use the batch-loader gem. For more details on N+1 queries read the [batch-loader gem README](https://github.com/exAspArk/batch-loader/#why).
|
26
27
|
|
@@ -133,7 +134,7 @@ end
|
|
133
134
|
|
134
135
|
class PhoneNumber < ActiveRecord::Base
|
135
136
|
belongs_to :contact
|
136
|
-
scope :
|
137
|
+
scope :enabled, -> { where(enabled: true) }
|
137
138
|
end
|
138
139
|
```
|
139
140
|
|
@@ -143,10 +144,10 @@ This time we want the list of phone numbers for a collection of contacts.
|
|
143
144
|
contacts.map(&:phone_numbers_lazy).flatten
|
144
145
|
```
|
145
146
|
|
146
|
-
It is also possible to apply scopes and conditions to a lazy has_many association. For instance if we want to only fetch
|
147
|
+
It is also possible to apply scopes and conditions to a lazy has_many association. For instance if we want to only fetch enabled phone numbers in the example above, you would specify the scope like so:
|
147
148
|
|
148
149
|
```ruby
|
149
|
-
contacts.map { |contact| contact.phone_numbers_lazy(PhoneNumber.
|
150
|
+
contacts.map { |contact| contact.phone_numbers_lazy(PhoneNumber.enabled) }.flatten
|
150
151
|
```
|
151
152
|
|
152
153
|
|
@@ -194,6 +195,28 @@ INNER JOIN agents ON agents.ID = phones.agent_id
|
|
194
195
|
WHERE (agents. ID IN(4212, 265, 2309))
|
195
196
|
```
|
196
197
|
|
198
|
+
### Has And Belongs To Many ###
|
199
|
+
|
200
|
+
Consider the following data model:
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
class User < ActiveRecord::Base
|
204
|
+
include BatchLoaderActiveRecord
|
205
|
+
has_and_belongs_to_many :roles
|
206
|
+
association_accessor :roles
|
207
|
+
end
|
208
|
+
|
209
|
+
class Role < ActiveRecord::Base
|
210
|
+
has_and_belongs_to_many :users
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
214
|
+
This time we want the list of roles for a collection of users.
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
users.map(&:roles_lazy).flatten
|
218
|
+
```
|
219
|
+
|
197
220
|
|
198
221
|
## Development
|
199
222
|
|
@@ -10,13 +10,6 @@ module BatchLoaderActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
-
def belongs_to_lazy(*args)
|
14
|
-
belongs_to(*args).tap do |reflections|
|
15
|
-
manager = AssociationManager.new(model: self, reflection: reflections.values.last)
|
16
|
-
define_method(manager.accessor_name) { manager.belongs_to_batch_loader(self) }
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
13
|
def association_accessor(name)
|
21
14
|
reflection = reflect_on_association(name) or raise "Can't find association #{name.inspect}"
|
22
15
|
manager = AssociationManager.new(model: self, reflection: reflection)
|
@@ -29,11 +22,22 @@ module BatchLoaderActiveRecord
|
|
29
22
|
define_method(manager.accessor_name) do |instance_scope = nil|
|
30
23
|
manager.has_many_to_batch_loader(self, instance_scope)
|
31
24
|
end
|
25
|
+
when :has_and_belongs_to_many
|
26
|
+
define_method(manager.accessor_name) do |instance_scope = nil|
|
27
|
+
manager.has_and_belongs_to_many_to_batch_loader(self, instance_scope)
|
28
|
+
end
|
32
29
|
else
|
33
30
|
raise NotImplementedError, "association kind #{reflection.macro.inspect} is not yet supported"
|
34
31
|
end
|
35
32
|
end
|
36
33
|
|
34
|
+
def belongs_to_lazy(*args)
|
35
|
+
belongs_to(*args).tap do |reflections|
|
36
|
+
manager = AssociationManager.new(model: self, reflection: reflections.values.last)
|
37
|
+
define_method(manager.accessor_name) { manager.belongs_to_batch_loader(self) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
37
41
|
def has_one_lazy(*args)
|
38
42
|
has_one(*args).tap do |reflections|
|
39
43
|
manager = AssociationManager.new(model: self, reflection: reflections.values.last)
|
@@ -49,5 +53,14 @@ module BatchLoaderActiveRecord
|
|
49
53
|
end
|
50
54
|
end
|
51
55
|
end
|
56
|
+
|
57
|
+
def has_and_belongs_to_many_lazy(*args)
|
58
|
+
has_and_belongs_to_many(*args).tap do |reflections|
|
59
|
+
manager = AssociationManager.new(model: self, reflection: reflect_on_all_associations.last)
|
60
|
+
define_method(manager.accessor_name) do |instance_scope = nil|
|
61
|
+
manager.has_and_belongs_to_many_to_batch_loader(self, instance_scope)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
52
65
|
end
|
53
66
|
end
|
@@ -31,11 +31,7 @@ module BatchLoaderActiveRecord
|
|
31
31
|
custom_key = batch_key
|
32
32
|
custom_key += [instance_scope.to_sql.hash] unless instance_scope.nil?
|
33
33
|
BatchLoader.for(instance.id).batch(default_value: [], key: custom_key) do |model_ids, loader|
|
34
|
-
relation =
|
35
|
-
target_scope
|
36
|
-
else
|
37
|
-
target_scope.instance_eval { instance_scope }
|
38
|
-
end
|
34
|
+
relation = relation_with_scope(instance_scope)
|
39
35
|
if reflection.through_reflection?
|
40
36
|
instances = fetch_for_model_ids(model_ids, relation: relation)
|
41
37
|
instances.each do |instance|
|
@@ -49,8 +45,29 @@ module BatchLoaderActiveRecord
|
|
49
45
|
end
|
50
46
|
end
|
51
47
|
|
48
|
+
def has_and_belongs_to_many_to_batch_loader(instance, instance_scope)
|
49
|
+
BatchLoader.for(instance.id).batch(default_value: [], key: batch_key) do |model_ids, loader|
|
50
|
+
instance_id_path = "#{reflection.join_table}.#{reflection.foreign_key}"
|
51
|
+
relation_with_scope(instance_scope)
|
52
|
+
.joins(habtm_join(reflection))
|
53
|
+
.where("#{reflection.join_table}.#{reflection.foreign_key} IN (?)", model_ids)
|
54
|
+
.select("#{target_scope.table_name}.*, #{instance_id_path} AS _instance_id")
|
55
|
+
.each do |instance|
|
56
|
+
loader.call(instance.public_send(:_instance_id)) { |value| value << instance }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
52
61
|
private
|
53
62
|
|
63
|
+
def relation_with_scope(instance_scope)
|
64
|
+
if instance_scope.nil?
|
65
|
+
target_scope
|
66
|
+
else
|
67
|
+
target_scope.instance_eval { instance_scope }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
54
71
|
def target_scope
|
55
72
|
@target_scope ||= if reflection.scope.nil?
|
56
73
|
reflection.klass
|
@@ -115,5 +132,13 @@ module BatchLoaderActiveRecord
|
|
115
132
|
end
|
116
133
|
"#{model_class.table_name}.#{id_column}"
|
117
134
|
end
|
135
|
+
|
136
|
+
def habtm_join(reflection)
|
137
|
+
<<~SQL
|
138
|
+
INNER JOIN #{reflection.join_table}
|
139
|
+
ON #{reflection.join_table}.#{reflection.association_foreign_key} =
|
140
|
+
#{reflection.klass.table_name}.#{reflection.active_record.primary_key}
|
141
|
+
SQL
|
142
|
+
end
|
118
143
|
end
|
119
144
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: batch-loader-active-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mathieul
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: batch-loader
|