jit_preloader 0.2.0 → 0.2.1
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 +5 -5
- data/Gemfile.lock +1 -1
- data/lib/jit_preloader/active_record/base.rb +12 -7
- data/lib/jit_preloader/version.rb +1 -1
- data/spec/lib/jit_preloader/preloader_spec.rb +105 -0
- data/spec/support/models.rb +8 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e859fc3b5826b985e1b51721bb6d05dc215b2434b250e553f85606ffba196ce3
|
4
|
+
data.tar.gz: d3637cf1b4cf27da79db1ad6dea4be978d9091ae96278abc2cbd02d5c2256076
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2d6a3f714be0c14a0f026d4b824f0c4f2176818af5338e94af11f5fe83af41b81181368e32ea42ad0cf00b5a78de598fd8010b77afe36b8ee01745ac9dca236
|
7
|
+
data.tar.gz: fc80b435fa33b15f6686ca061943f050497f6485edd4b63d0a7c505403cfb8fe6019b85615f195b6d6b02fd09cf325970f93e1b3e8c7503bd42ec37f1df5f2c6
|
data/Gemfile.lock
CHANGED
@@ -38,20 +38,25 @@ module JitPreloadExtension
|
|
38
38
|
primary_ids = jit_preloader.records.collect{|r| r[reflection.active_record_primary_key] }
|
39
39
|
klass = reflection.klass
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
aggregate_association = reflection
|
42
|
+
while aggregate_association.through_reflection
|
43
|
+
aggregate_association = aggregate_association.through_reflection
|
44
|
+
end
|
43
45
|
|
44
|
-
|
46
|
+
association_scope = klass.all.merge(association(assoc).scope).unscope(where: aggregate_association.foreign_key)
|
47
|
+
association_scope = association_scope.instance_exec(&reflection.scope).reorder(nil) if reflection.scope
|
45
48
|
|
49
|
+
conditions[aggregate_association.table_name] = { aggregate_association.foreign_key => primary_ids }
|
46
50
|
if reflection.type.present?
|
47
51
|
conditions[reflection.type] = self.class.name
|
48
52
|
end
|
53
|
+
group_by = "#{aggregate_association.table_name}.#{aggregate_association.foreign_key}"
|
49
54
|
|
50
55
|
preloaded_data = Hash[association_scope
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
56
|
+
.where(conditions)
|
57
|
+
.group(group_by)
|
58
|
+
.send(aggregate, field)
|
59
|
+
]
|
55
60
|
|
56
61
|
jit_preloader.records.each do |record|
|
57
62
|
record.jit_preload_aggregates ||= {}
|
@@ -85,6 +85,111 @@ RSpec.describe JitPreloader::Preloader do
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
context "when preloading an aggregate on a has_many through relationship" do
|
89
|
+
let(:country_contacts_counts) { [2, 3] }
|
90
|
+
|
91
|
+
context "without jit preload" do
|
92
|
+
it "generates N+1 query notifications for each one" do
|
93
|
+
ActiveSupport::Notifications.subscribed(callback, "n_plus_one_query") do
|
94
|
+
Country.all.each_with_index do |c, i|
|
95
|
+
expect(c.contacts_count).to eql country_contacts_counts[i]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
country_contact_queries = [canada, usa].product([["contacts.count"]])
|
100
|
+
expect(source_map).to eql(Hash[country_contact_queries])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "with jit_preload" do
|
105
|
+
it "does NOT generate N+1 query notifications" do
|
106
|
+
ActiveSupport::Notifications.subscribed(callback, "n_plus_one_query") do
|
107
|
+
Country.all.jit_preload.each_with_index do |c, i|
|
108
|
+
expect(c.contacts_count).to eql country_contacts_counts[i]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
expect(source_map).to eql({})
|
113
|
+
end
|
114
|
+
|
115
|
+
it "can handle queries" do
|
116
|
+
Country.all.jit_preload.each_with_index do |c, i|
|
117
|
+
expect(c.contacts_count).to eql country_contacts_counts[i]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when preloading an aggregate on a polymorphic has_many through relationship" do
|
124
|
+
let(:contact_owner_addresses_counts) { [3] }
|
125
|
+
|
126
|
+
context "without jit preload" do
|
127
|
+
it "generates N+1 query notifications for each one" do
|
128
|
+
ActiveSupport::Notifications.subscribed(callback, "n_plus_one_query") do
|
129
|
+
ContactOwner.all.each_with_index do |c, i|
|
130
|
+
expect(c.addresses_count).to eql contact_owner_addresses_counts[i]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
contact_owner_addresses_queries = [contact_owner].product([["addresses.count"]])
|
135
|
+
expect(source_map).to eql(Hash[contact_owner_addresses_queries])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "with jit_preload" do
|
140
|
+
it "does NOT generate N+1 query notifications" do
|
141
|
+
ActiveSupport::Notifications.subscribed(callback, "n_plus_one_query") do
|
142
|
+
ContactOwner.all.jit_preload.each_with_index do |c, i|
|
143
|
+
expect(c.addresses_count).to eql contact_owner_addresses_counts[i]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
expect(source_map).to eql({})
|
148
|
+
end
|
149
|
+
|
150
|
+
it "can handle queries" do
|
151
|
+
ContactOwner.all.jit_preload.each_with_index do |c, i|
|
152
|
+
expect(c.addresses_count).to eql contact_owner_addresses_counts[i]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when preloading a has_many through polymorphic aggregate where the through class has a polymorphic relationship to the target class" do
|
159
|
+
let(:contact_owner_counts) { [1, 2] }
|
160
|
+
|
161
|
+
context "without jit preload" do
|
162
|
+
it "generates N+1 query notifications for each one" do
|
163
|
+
ActiveSupport::Notifications.subscribed(callback, "n_plus_one_query") do
|
164
|
+
Country.all.each_with_index do |c, i|
|
165
|
+
expect(c.contact_owners_count).to eql contact_owner_counts[i]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
contact_owner_queries = [canada, usa].product([["contact_owners.count"]])
|
170
|
+
expect(source_map).to eql(Hash[contact_owner_queries])
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "with jit_preload" do
|
175
|
+
it "does NOT generate N+1 query notifications" do
|
176
|
+
ActiveSupport::Notifications.subscribed(callback, "n_plus_one_query") do
|
177
|
+
Country.all.jit_preload.each_with_index do |c, i|
|
178
|
+
expect(c.contact_owners_count).to eql contact_owner_counts[i]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
expect(source_map).to eql({})
|
183
|
+
end
|
184
|
+
|
185
|
+
it "can handle queries" do
|
186
|
+
Country.all.jit_preload.each_with_index do |c, i|
|
187
|
+
expect(c.contact_owners_count).to eql contact_owner_counts[i]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
88
193
|
context "when preloading an aggregate" do
|
89
194
|
let(:addresses_counts) { [3, 0, 2] }
|
90
195
|
let(:phone_number_counts) { [2, 0, 1] }
|
data/spec/support/models.rb
CHANGED
@@ -5,7 +5,6 @@ class Contact < ActiveRecord::Base
|
|
5
5
|
has_many :phone_numbers
|
6
6
|
has_one :email_address
|
7
7
|
|
8
|
-
|
9
8
|
has_many_aggregate :addresses, :max_street_length, :maximum, "LENGTH(street)"
|
10
9
|
has_many_aggregate :phone_numbers, :count, :count, "id"
|
11
10
|
has_many_aggregate :addresses, :count, :count, "*"
|
@@ -26,9 +25,17 @@ end
|
|
26
25
|
|
27
26
|
class Country < ActiveRecord::Base
|
28
27
|
has_many :addresses
|
28
|
+
has_many :contacts, through: :addresses
|
29
|
+
has_many :contact_owners, through: :contacts, source_type: 'ContactOwner'
|
30
|
+
|
31
|
+
has_many_aggregate :contacts, :count, :count, "*"
|
32
|
+
has_many_aggregate :contact_owners, :count, :count, "*"
|
29
33
|
end
|
30
34
|
|
31
35
|
class ContactOwner < ActiveRecord::Base
|
32
36
|
has_many :contacts, as: :contact_owner
|
37
|
+
has_many :addresses, through: :contacts
|
38
|
+
|
33
39
|
has_many_aggregate :contacts, :count, :count, "*"
|
40
|
+
has_many_aggregate :addresses, :count, :count, "*"
|
34
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jit_preloader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kyle d'Oliveira
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -179,8 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
181
|
requirements: []
|
182
|
-
|
183
|
-
rubygems_version: 2.6.14
|
182
|
+
rubygems_version: 3.0.3
|
184
183
|
signing_key:
|
185
184
|
specification_version: 4
|
186
185
|
summary: Tool to understand N+1 queries and to remove them
|