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 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