jit_preloader 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1d42276971ec067b0aafaf2252e62e7a8f974525
4
- data.tar.gz: 5c2cc5de28be5ebedc033d704971937dcd9904d9
2
+ SHA256:
3
+ metadata.gz: e859fc3b5826b985e1b51721bb6d05dc215b2434b250e553f85606ffba196ce3
4
+ data.tar.gz: d3637cf1b4cf27da79db1ad6dea4be978d9091ae96278abc2cbd02d5c2256076
5
5
  SHA512:
6
- metadata.gz: 71cfe85e0ca02afa0632b7efcf4991ca3778fa0870bb4773c1f789ff18bb21e9d50d08b5d2bffb9590c660480bc2e57061fce6e97d5553d586b9bdfd2108a5e6
7
- data.tar.gz: 23828f4c2da51cbfb4e8e5f4026472875d29119005045d4d720908380811b0662aa0ca013e38af3a52accc8d08233ac358a2d92437184426645209a558479125
6
+ metadata.gz: a2d6a3f714be0c14a0f026d4b824f0c4f2176818af5338e94af11f5fe83af41b81181368e32ea42ad0cf00b5a78de598fd8010b77afe36b8ee01745ac9dca236
7
+ data.tar.gz: fc80b435fa33b15f6686ca061943f050497f6485edd4b63d0a7c505403cfb8fe6019b85615f195b6d6b02fd09cf325970f93e1b3e8c7503bd42ec37f1df5f2c6
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jit_preloader (0.2.0)
4
+ jit_preloader (0.2.1)
5
5
  activerecord (> 4.2, < 6)
6
6
  activesupport
7
7
 
@@ -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
- association_scope = klass
42
- association_scope = association_scope.instance_exec(&reflection.scope).reorder(nil) if reflection.scope
41
+ aggregate_association = reflection
42
+ while aggregate_association.through_reflection
43
+ aggregate_association = aggregate_association.through_reflection
44
+ end
43
45
 
44
- conditions[reflection.foreign_key] = primary_ids
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
- .where(conditions)
52
- .group(reflection.foreign_key)
53
- .send(aggregate, field)
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 ||= {}
@@ -1,3 +1,3 @@
1
1
  module JitPreloader
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -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] }
@@ -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.0
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-03-04 00:00:00.000000000 Z
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
- rubyforge_project:
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