jit_preloader 2.1.0 → 3.1.0

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
2
  SHA256:
3
- metadata.gz: bca887aa87a283d55ee26b1fe606948733ecfecc7ce40d4cedc8b89d90efb834
4
- data.tar.gz: 9a7c463ccacf8daeebc23d2eaa375d8c4f908b333c492baaa4c4c931bbc7d1b3
3
+ metadata.gz: dac1fcb3b291d3064e2f06455c08d0b25d7c4875bb4f7c5938c194c6ca9dc232
4
+ data.tar.gz: 5704f5b84dfd9b99898bf145622cd3175afbbf5a4cee3ecb2a4599d7a7e5fbc5
5
5
  SHA512:
6
- metadata.gz: 30920baa0426de8bc5188fc80ccfacf6e040f6bf1f7c934214bb75600080a090756ca957da9a59327a63b30550d7a4efdf5f4e66c873783e0c5aa627fe558e9f
7
- data.tar.gz: 9adfa72af9bfcc684c3c22f6e82b353af9b36081224cae92e248e5c52db7f028a9c6f49dc3760d059f476980a48f4852b6f94c5e5faf82dbef28087e0b8dd3cd
6
+ metadata.gz: e1b603e9e2fc4748b8765b8329ac435a17cdf5753b9a69479c44fdd78f3398293e1ffccd666667bf93a83db15dc326fa9d389a06bc930d00998032111c2dcea4
7
+ data.tar.gz: 88c93ec8fb2ad3e24b2209ed4c456389b0f93c0b8b729f60ccccd5f00e7960cb12251f4cfaf364d0f718cfe788803cc2dbd55799ae61832d2ee3f07d6291fe50
@@ -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: 2.7
26
+ ruby-version: 3.1
28
27
  - name: Install dependencies
29
28
  run: bundle install
30
29
  - name: Run tests
@@ -13,10 +13,10 @@ jobs:
13
13
 
14
14
  steps:
15
15
  - uses: actions/checkout@v4
16
- - name: Set up Ruby 2.7
16
+ - name: Set up Ruby 3.1
17
17
  uses: ruby/setup-ruby@v1
18
18
  with:
19
- ruby-version: 2.7
19
+ ruby-version: 3.1
20
20
 
21
21
  - name: Publish to RubyGems
22
22
  env:
data/.gitignore CHANGED
@@ -50,3 +50,4 @@ Gemfile.lock
50
50
 
51
51
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
52
52
  .rvmrc
53
+ .ruby-version
data/Gemfile CHANGED
@@ -1,5 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem "activerecord", ">=7"
4
+ gem "sqlite3", "~> 1.4"
4
5
  # Specify your gem's dependencies in jit_preloader.gemspec
5
6
  gemspec
@@ -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
 
@@ -1,3 +1,3 @@
1
1
  module JitPreloader
2
- VERSION = "2.1.0"
2
+ VERSION = "3.1.0"
3
3
  end
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.0.0")
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: 2.1.0
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-29 00:00:00.000000000 Z
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.1.6
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
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem "activerecord", "~>5.2"
4
-
5
- # Specify your gem's dependencies in jit_preloader.gemspec
6
- gemspec
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)