jit_preloader 2.1.0 → 3.1.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/.github/workflows/ci.yml +2 -3
- data/.github/workflows/gem-push.yml +2 -2
- data/.gitignore +1 -0
- data/Gemfile +1 -0
- data/jit_preloader.gemspec +4 -1
- data/lib/jit_preloader/active_record/associations/singular_association.rb +5 -1
- data/lib/jit_preloader/active_record/base.rb +2 -0
- data/lib/jit_preloader/version.rb +1 -1
- data/lib/jit_preloader.rb +1 -3
- data/spec/lib/jit_preloader/active_record/base_spec.rb +22 -0
- data/spec/lib/jit_preloader/preloader_spec.rb +24 -0
- metadata +7 -8
- data/Gemfile.5.2 +0 -6
- data/Gemfile.5.2.lock +0 -72
- data/lib/jit_preloader/active_record/associations/preloader/ar5_association.rb +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dac1fcb3b291d3064e2f06455c08d0b25d7c4875bb4f7c5938c194c6ca9dc232
|
4
|
+
data.tar.gz: 5704f5b84dfd9b99898bf145622cd3175afbbf5a4cee3ecb2a4599d7a7e5fbc5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1b603e9e2fc4748b8765b8329ac435a17cdf5753b9a69479c44fdd78f3398293e1ffccd666667bf93a83db15dc326fa9d389a06bc930d00998032111c2dcea4
|
7
|
+
data.tar.gz: 88c93ec8fb2ad3e24b2209ed4c456389b0f93c0b8b729f60ccccd5f00e7960cb12251f4cfaf364d0f718cfe788803cc2dbd55799ae61832d2ee3f07d6291fe50
|
data/.github/workflows/ci.yml
CHANGED
@@ -9,13 +9,12 @@ on:
|
|
9
9
|
jobs:
|
10
10
|
test:
|
11
11
|
runs-on: ubuntu-latest
|
12
|
+
timeout-minutes: 5
|
12
13
|
strategy:
|
13
14
|
fail-fast: false
|
14
15
|
matrix:
|
15
16
|
gemfile:
|
16
17
|
- Gemfile
|
17
|
-
- Gemfile.5.2
|
18
|
-
- Gemfile.6.0
|
19
18
|
- Gemfile.6.1
|
20
19
|
env:
|
21
20
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
@@ -24,7 +23,7 @@ jobs:
|
|
24
23
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
25
24
|
uses: ruby/setup-ruby@v1
|
26
25
|
with:
|
27
|
-
ruby-version:
|
26
|
+
ruby-version: 3.1
|
28
27
|
- name: Install dependencies
|
29
28
|
run: bundle install
|
30
29
|
- name: Run tests
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/jit_preloader.gemspec
CHANGED
@@ -10,7 +10,10 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["kyle.doliveira@clio.com"]
|
11
11
|
spec.summary = %q{Tool to understand N+1 queries and to remove them}
|
12
12
|
spec.description = %q{The JitPreloader has the ability to send notifications when N+1 queries occur to help guage how problematic they are for your code base and a way to remove all of the commons explicitly or automatically}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/clio/jit_preloader"
|
14
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
15
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
16
|
+
|
14
17
|
spec.license = "MIT"
|
15
18
|
|
16
19
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -18,13 +18,17 @@ module JitPreloader
|
|
18
18
|
# always an N+1 query.
|
19
19
|
record.jit_n_plus_one_tracking ||= owner.jit_n_plus_one_tracking if record
|
20
20
|
|
21
|
-
if !jit_loaded && owner.jit_n_plus_one_tracking
|
21
|
+
if !jit_loaded && owner.jit_n_plus_one_tracking && !is_polymorphic_association_without_type
|
22
22
|
ActiveSupport::Notifications.publish("n_plus_one_query",
|
23
23
|
source: owner, association: reflection.name)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
|
+
|
29
|
+
private def is_polymorphic_association_without_type
|
30
|
+
self.is_a?(ActiveRecord::Associations::BelongsToPolymorphicAssociation) && self.klass.nil?
|
31
|
+
end
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
@@ -22,6 +22,7 @@ module JitPreloadExtension
|
|
22
22
|
def preload_scoped_relation(name:, base_association:, preload_scope: nil)
|
23
23
|
return jit_preload_scoped_relations[name] if jit_preload_scoped_relations&.key?(name)
|
24
24
|
|
25
|
+
base_association = base_association.to_sym
|
25
26
|
records = jit_preloader&.records || [self]
|
26
27
|
previous_association_values = {}
|
27
28
|
|
@@ -55,6 +56,7 @@ module JitPreloadExtension
|
|
55
56
|
def preload_scoped_relation(name:, base_association:, preload_scope: nil)
|
56
57
|
return jit_preload_scoped_relations[name] if jit_preload_scoped_relations&.key?(name)
|
57
58
|
|
59
|
+
base_association = base_association.to_sym
|
58
60
|
records = jit_preloader&.records || [self]
|
59
61
|
previous_association_values = {}
|
60
62
|
|
data/lib/jit_preloader.rb
CHANGED
@@ -11,10 +11,8 @@ require 'jit_preloader/active_record/associations/singular_association'
|
|
11
11
|
if Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new("7.0.0")
|
12
12
|
require 'jit_preloader/active_record/associations/preloader/ar7_association'
|
13
13
|
require 'jit_preloader/active_record/associations/preloader/ar7_branch'
|
14
|
-
elsif Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new("6.
|
14
|
+
elsif Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new("6.1.0")
|
15
15
|
require 'jit_preloader/active_record/associations/preloader/ar6_association'
|
16
|
-
elsif Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new("5.2.2")
|
17
|
-
require 'jit_preloader/active_record/associations/preloader/ar5_association'
|
18
16
|
else
|
19
17
|
require 'jit_preloader/active_record/associations/preloader/collection_association'
|
20
18
|
require 'jit_preloader/active_record/associations/preloader/singular_association'
|
@@ -106,5 +106,27 @@ RSpec.describe "ActiveRecord::Base Extensions" do
|
|
106
106
|
expect(value).to eq([])
|
107
107
|
end
|
108
108
|
end
|
109
|
+
|
110
|
+
context "when preload_scoped_relation with string base_association name" do
|
111
|
+
it "preload properly" do
|
112
|
+
contacts = Contact.jit_preload.limit(2).to_a
|
113
|
+
|
114
|
+
call_with_string = lambda { |contact| contact.preload_scoped_relation(
|
115
|
+
name: "American Addresses",
|
116
|
+
base_association: "addresses",
|
117
|
+
preload_scope: Address.where(country: usa)
|
118
|
+
) }
|
119
|
+
|
120
|
+
usa_addresses = contacts.first.addresses.where(country: usa).to_a
|
121
|
+
expect do
|
122
|
+
expect(call_with_string.call(contacts.first)).to match_array usa_addresses
|
123
|
+
end.to make_database_queries(count: 1)
|
124
|
+
|
125
|
+
usa_addresses = contacts.last.addresses.where(country: usa).to_a
|
126
|
+
expect do
|
127
|
+
expect(call_with_string.call(contacts.last)).to match_array usa_addresses
|
128
|
+
end.to_not make_database_queries
|
129
|
+
end
|
130
|
+
end
|
109
131
|
end
|
110
132
|
end
|
@@ -165,6 +165,30 @@ RSpec.describe JitPreloader::Preloader do
|
|
165
165
|
expect(Contact.jit_preload.map(&:contact_owner)).to eq [nil, ContactOwner.first, Address.first]
|
166
166
|
end
|
167
167
|
end
|
168
|
+
|
169
|
+
context "when a record has a polymorphic association type is nil" do
|
170
|
+
before do
|
171
|
+
contact1.update!(contact_owner_type: nil, contact_owner_id: nil)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "successfully load the rest of association values and does not publish a n+1 notification" do
|
175
|
+
contacts = Contact.jit_preload.to_a
|
176
|
+
ActiveSupport::Notifications.subscribed(callback, "n_plus_one_query") do
|
177
|
+
expect(contacts.first.contact_owner).to eq(nil)
|
178
|
+
end
|
179
|
+
|
180
|
+
expect(source_map).to eql({})
|
181
|
+
|
182
|
+
expect do
|
183
|
+
contacts.first.contact_owner
|
184
|
+
contacts.second.contact_owner
|
185
|
+
contacts.third.contact_owner
|
186
|
+
end.not_to make_database_queries
|
187
|
+
|
188
|
+
expect(contacts.second.contact_owner).to eq(ContactOwner.first)
|
189
|
+
expect(contacts.third.contact_owner).to eq(Address.first)
|
190
|
+
end
|
191
|
+
end
|
168
192
|
end
|
169
193
|
end
|
170
194
|
|
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:
|
4
|
+
version: 3.1.0
|
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: 2024-04
|
11
|
+
date: 2024-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -151,8 +151,6 @@ files:
|
|
151
151
|
- ".gitignore"
|
152
152
|
- ".rspec"
|
153
153
|
- Gemfile
|
154
|
-
- Gemfile.5.2
|
155
|
-
- Gemfile.5.2.lock
|
156
154
|
- Gemfile.6.0
|
157
155
|
- Gemfile.6.0.lock
|
158
156
|
- Gemfile.6.1
|
@@ -163,7 +161,6 @@ files:
|
|
163
161
|
- jit_preloader.gemspec
|
164
162
|
- lib/jit_preloader.rb
|
165
163
|
- lib/jit_preloader/active_record/associations/collection_association.rb
|
166
|
-
- lib/jit_preloader/active_record/associations/preloader/ar5_association.rb
|
167
164
|
- lib/jit_preloader/active_record/associations/preloader/ar6_association.rb
|
168
165
|
- lib/jit_preloader/active_record/associations/preloader/ar7_association.rb
|
169
166
|
- lib/jit_preloader/active_record/associations/preloader/ar7_branch.rb
|
@@ -179,10 +176,12 @@ files:
|
|
179
176
|
- spec/spec_helper.rb
|
180
177
|
- spec/support/database.rb
|
181
178
|
- spec/support/models.rb
|
182
|
-
homepage:
|
179
|
+
homepage: https://github.com/clio/jit_preloader
|
183
180
|
licenses:
|
184
181
|
- MIT
|
185
|
-
metadata:
|
182
|
+
metadata:
|
183
|
+
homepage_uri: https://github.com/clio/jit_preloader
|
184
|
+
source_code_uri: https://github.com/clio/jit_preloader
|
186
185
|
post_install_message:
|
187
186
|
rdoc_options: []
|
188
187
|
require_paths:
|
@@ -198,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
197
|
- !ruby/object:Gem::Version
|
199
198
|
version: '0'
|
200
199
|
requirements: []
|
201
|
-
rubygems_version: 3.
|
200
|
+
rubygems_version: 3.3.27
|
202
201
|
signing_key:
|
203
202
|
specification_version: 4
|
204
203
|
summary: Tool to understand N+1 queries and to remove them
|
data/Gemfile.5.2
DELETED
data/Gemfile.5.2.lock
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
jit_preloader (1.0.3)
|
5
|
-
activerecord (>= 5.2, < 7)
|
6
|
-
activesupport
|
7
|
-
|
8
|
-
GEM
|
9
|
-
remote: https://rubygems.org/
|
10
|
-
specs:
|
11
|
-
activemodel (5.2.6)
|
12
|
-
activesupport (= 5.2.6)
|
13
|
-
activerecord (5.2.6)
|
14
|
-
activemodel (= 5.2.6)
|
15
|
-
activesupport (= 5.2.6)
|
16
|
-
arel (>= 9.0)
|
17
|
-
activesupport (5.2.6)
|
18
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
19
|
-
i18n (>= 0.7, < 2)
|
20
|
-
minitest (~> 5.1)
|
21
|
-
tzinfo (~> 1.1)
|
22
|
-
arel (9.0.0)
|
23
|
-
byebug (11.1.3)
|
24
|
-
concurrent-ruby (1.1.9)
|
25
|
-
database_cleaner (2.0.1)
|
26
|
-
database_cleaner-active_record (~> 2.0.0)
|
27
|
-
database_cleaner-active_record (2.0.1)
|
28
|
-
activerecord (>= 5.a)
|
29
|
-
database_cleaner-core (~> 2.0.0)
|
30
|
-
database_cleaner-core (2.0.1)
|
31
|
-
db-query-matchers (0.10.0)
|
32
|
-
activesupport (>= 4.0, < 7)
|
33
|
-
rspec (~> 3.0)
|
34
|
-
diff-lcs (1.4.4)
|
35
|
-
i18n (1.8.10)
|
36
|
-
concurrent-ruby (~> 1.0)
|
37
|
-
minitest (5.14.4)
|
38
|
-
rake (13.0.6)
|
39
|
-
rspec (3.10.0)
|
40
|
-
rspec-core (~> 3.10.0)
|
41
|
-
rspec-expectations (~> 3.10.0)
|
42
|
-
rspec-mocks (~> 3.10.0)
|
43
|
-
rspec-core (3.10.1)
|
44
|
-
rspec-support (~> 3.10.0)
|
45
|
-
rspec-expectations (3.10.1)
|
46
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
47
|
-
rspec-support (~> 3.10.0)
|
48
|
-
rspec-mocks (3.10.2)
|
49
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
-
rspec-support (~> 3.10.0)
|
51
|
-
rspec-support (3.10.2)
|
52
|
-
sqlite3 (1.4.2)
|
53
|
-
thread_safe (0.3.6)
|
54
|
-
tzinfo (1.2.9)
|
55
|
-
thread_safe (~> 0.1)
|
56
|
-
|
57
|
-
PLATFORMS
|
58
|
-
x86_64-darwin-19
|
59
|
-
|
60
|
-
DEPENDENCIES
|
61
|
-
activerecord (~> 5.2)
|
62
|
-
bundler
|
63
|
-
byebug
|
64
|
-
database_cleaner
|
65
|
-
db-query-matchers
|
66
|
-
jit_preloader!
|
67
|
-
rake (~> 13.0)
|
68
|
-
rspec
|
69
|
-
sqlite3
|
70
|
-
|
71
|
-
BUNDLED WITH
|
72
|
-
2.2.12
|
@@ -1,66 +0,0 @@
|
|
1
|
-
module JitPreloader
|
2
|
-
module PreloaderAssociation
|
3
|
-
|
4
|
-
# A monkey patch to ActiveRecord. The old method looked like the snippet
|
5
|
-
# below. Our changes here are that we remove records that are already
|
6
|
-
# part of the target, then attach all of the records to a new jit preloader.
|
7
|
-
#
|
8
|
-
# def run(preloader)
|
9
|
-
# records = load_records do |record|
|
10
|
-
# owner = owners_by_key[convert_key(record[association_key_name])]
|
11
|
-
# association = owner.association(reflection.name)
|
12
|
-
# association.set_inverse_instance(record)
|
13
|
-
# end
|
14
|
-
|
15
|
-
# owners.each do |owner|
|
16
|
-
# associate_records_to_owner(owner, records[convert_key(owner[owner_key_name])] || [])
|
17
|
-
# end
|
18
|
-
# end
|
19
|
-
|
20
|
-
def run(preloader)
|
21
|
-
return unless (reflection.scope.nil? || reflection.scope.arity == 0) && klass.ancestors.include?(ActiveRecord::Base)
|
22
|
-
|
23
|
-
super.tap do
|
24
|
-
if preloaded_records.any? && preloaded_records.none?(&:jit_preloader)
|
25
|
-
JitPreloader::Preloader.attach(preloaded_records) if owners.any?(&:jit_preloader) || JitPreloader.globally_enabled?
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# Original method:
|
31
|
-
# def associate_records_to_owner(owner, records)
|
32
|
-
# association = owner.association(reflection.name)
|
33
|
-
# association.loaded!
|
34
|
-
# if reflection.collection?
|
35
|
-
# association.target.concat(records)
|
36
|
-
# else
|
37
|
-
# association.target = records.first unless records.empty?
|
38
|
-
# end
|
39
|
-
# end
|
40
|
-
def associate_records_to_owner(owner, records)
|
41
|
-
association = owner.association(reflection.name)
|
42
|
-
association.loaded!
|
43
|
-
|
44
|
-
if reflection.collection?
|
45
|
-
# It is possible that some of the records are loaded already.
|
46
|
-
# We don't want to duplicate them, but we also want to preserve
|
47
|
-
# the original copy so that we don't blow away in-memory changes.
|
48
|
-
new_records = association.target.any? ? records - association.target : records
|
49
|
-
association.target.concat(new_records)
|
50
|
-
association.loaded!
|
51
|
-
else
|
52
|
-
association.target = records.first unless records.empty?
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
def build_scope
|
58
|
-
super.tap do |scope|
|
59
|
-
scope.jit_preload! if owners.any?(&:jit_preloader) || JitPreloader.globally_enabled?
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
ActiveRecord::Associations::Preloader::Association.prepend(JitPreloader::PreloaderAssociation)
|
66
|
-
ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(JitPreloader::PreloaderAssociation)
|