jit_preloader 1.0.0 → 1.0.4

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
2
  SHA256:
3
- metadata.gz: 9a3ebb3488bb221f516f32ebd2fbbedcee866b57d3ccc3c171f3843b9cc0af45
4
- data.tar.gz: 7bcaa0e57a6ee5541ca0fac7013f4188113f3425675df4ad29cab7cba3c8a540
3
+ metadata.gz: 515aa37a3edcd60ff85c7490bf7a96906a612c7b5645d3d81542c5e7997a9026
4
+ data.tar.gz: 4cab1990cd0346da95d0d0785d28b18d5db95fecf66dbfcf14be26d0b020af5d
5
5
  SHA512:
6
- metadata.gz: 7d696d0f3465be1aa319ae86c335e087b967847fefcd49a926e27c1cc24681be824ecc2a95a6365a129ae668f2abb341d570841f242cbb4b47295f974a3bf71e
7
- data.tar.gz: 5ac6f866cc9ee0fbab43e36d8bd2134e42782e6a6e9e4e635bd9b15e5951a02ed2d1d86b003afdbf5aeac736ffd3a23aadc7e9f75db1679cfd0768e132e2d5b4
6
+ metadata.gz: bf81adb00759b36e3bea5dfcaf9431c3259cac06412b57008e799585c7094d97073f836300a9631bd24fa0de7820874646861e7ea68e27cb773662d2e3826dec
7
+ data.tar.gz: b96b45f91474ca01a5235d5ba071c750268e7c029fcfa371c308747433a5cb9f54bfb2560a99051c335352c8a536a01887ae01bb840337ee2e9ab3a808f5ec84
@@ -0,0 +1,3 @@
1
+ # Own any files in the .github/workflows directory and any of its
2
+ # subdirectories.
3
+ /.github/workflows/ @clio/application-security @clio/penguins
@@ -0,0 +1,31 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ release:
6
+ types: [ published ]
7
+
8
+ jobs:
9
+ build:
10
+ name: Build + Publish
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - name: Set up Ruby 2.7
18
+ uses: actions/setup-ruby@v1
19
+ with:
20
+ ruby-version: 2.7.x
21
+
22
+ - name: Publish to RubyGems
23
+ env:
24
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
25
+ run: |
26
+ mkdir -p $HOME/.gem
27
+ touch $HOME/.gem/credentials
28
+ chmod 0600 $HOME/.gem/credentials
29
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
30
+ gem build *.gemspec
31
+ gem push *.gem
data/Gemfile.5.2 ADDED
@@ -0,0 +1,6 @@
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 ADDED
@@ -0,0 +1,72 @@
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
data/Gemfile.6.0 ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "activerecord", "~>6.0.0"
4
+
5
+ # Specify your gem's dependencies in jit_preloader.gemspec
6
+ gemspec
data/Gemfile.6.0.lock ADDED
@@ -0,0 +1,72 @@
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 (6.0.4.1)
12
+ activesupport (= 6.0.4.1)
13
+ activerecord (6.0.4.1)
14
+ activemodel (= 6.0.4.1)
15
+ activesupport (= 6.0.4.1)
16
+ activesupport (6.0.4.1)
17
+ concurrent-ruby (~> 1.0, >= 1.0.2)
18
+ i18n (>= 0.7, < 2)
19
+ minitest (~> 5.1)
20
+ tzinfo (~> 1.1)
21
+ zeitwerk (~> 2.2, >= 2.2.2)
22
+ byebug (11.1.3)
23
+ concurrent-ruby (1.1.9)
24
+ database_cleaner (2.0.1)
25
+ database_cleaner-active_record (~> 2.0.0)
26
+ database_cleaner-active_record (2.0.1)
27
+ activerecord (>= 5.a)
28
+ database_cleaner-core (~> 2.0.0)
29
+ database_cleaner-core (2.0.1)
30
+ db-query-matchers (0.10.0)
31
+ activesupport (>= 4.0, < 7)
32
+ rspec (~> 3.0)
33
+ diff-lcs (1.4.4)
34
+ i18n (1.8.10)
35
+ concurrent-ruby (~> 1.0)
36
+ minitest (5.14.4)
37
+ rake (13.0.6)
38
+ rspec (3.10.0)
39
+ rspec-core (~> 3.10.0)
40
+ rspec-expectations (~> 3.10.0)
41
+ rspec-mocks (~> 3.10.0)
42
+ rspec-core (3.10.1)
43
+ rspec-support (~> 3.10.0)
44
+ rspec-expectations (3.10.1)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.10.0)
47
+ rspec-mocks (3.10.2)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.10.0)
50
+ rspec-support (3.10.2)
51
+ sqlite3 (1.4.2)
52
+ thread_safe (0.3.6)
53
+ tzinfo (1.2.9)
54
+ thread_safe (~> 0.1)
55
+ zeitwerk (2.4.2)
56
+
57
+ PLATFORMS
58
+ x86_64-darwin-19
59
+
60
+ DEPENDENCIES
61
+ activerecord (~> 6.0.0)
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
data/Gemfile.6.1 ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "activerecord", "~>6.1"
4
+
5
+ # Specify your gem's dependencies in jit_preloader.gemspec
6
+ gemspec
data/Gemfile.6.1.lock ADDED
@@ -0,0 +1,72 @@
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 (6.1.4.1)
12
+ activesupport (= 6.1.4.1)
13
+ activerecord (6.1.4.1)
14
+ activemodel (= 6.1.4.1)
15
+ activesupport (= 6.1.4.1)
16
+ activesupport (6.1.4.1)
17
+ concurrent-ruby (~> 1.0, >= 1.0.2)
18
+ i18n (>= 1.6, < 2)
19
+ minitest (>= 5.1)
20
+ tzinfo (~> 2.0)
21
+ zeitwerk (~> 2.3)
22
+ byebug (11.1.3)
23
+ concurrent-ruby (1.1.9)
24
+ database_cleaner (2.0.1)
25
+ database_cleaner-active_record (~> 2.0.0)
26
+ database_cleaner-active_record (2.0.1)
27
+ activerecord (>= 5.a)
28
+ database_cleaner-core (~> 2.0.0)
29
+ database_cleaner-core (2.0.1)
30
+ db-query-matchers (0.10.0)
31
+ activesupport (>= 4.0, < 7)
32
+ rspec (~> 3.0)
33
+ diff-lcs (1.4.4)
34
+ i18n (1.8.10)
35
+ concurrent-ruby (~> 1.0)
36
+ minitest (5.14.4)
37
+ rake (13.0.6)
38
+ rspec (3.10.0)
39
+ rspec-core (~> 3.10.0)
40
+ rspec-expectations (~> 3.10.0)
41
+ rspec-mocks (~> 3.10.0)
42
+ rspec-core (3.10.1)
43
+ rspec-support (~> 3.10.0)
44
+ rspec-expectations (3.10.1)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.10.0)
47
+ rspec-mocks (3.10.2)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.10.0)
50
+ rspec-support (3.10.2)
51
+ sqlite3 (1.4.2)
52
+ tzinfo (2.0.4)
53
+ concurrent-ruby (~> 1.0)
54
+ zeitwerk (2.4.2)
55
+
56
+ PLATFORMS
57
+ ruby
58
+ x86_64-darwin-19
59
+
60
+ DEPENDENCIES
61
+ activerecord (~> 6.1)
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
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "activerecord", "> 5.0", "< 7"
21
+ spec.add_dependency "activerecord", ">= 5.2", "< 7"
22
22
  spec.add_dependency "activesupport"
23
23
 
24
24
  spec.add_development_dependency "bundler"
@@ -4,7 +4,7 @@ module JitPreloader
4
4
  def load_target
5
5
  was_loaded = loaded?
6
6
 
7
- if !loaded? && owner.persisted? && owner.jit_preloader
7
+ if !loaded? && owner.persisted? && owner.jit_preloader && (reflection.scope.nil? || reflection.scope.arity == 0)
8
8
  owner.jit_preloader.jit_preload(reflection.name)
9
9
  end
10
10
 
@@ -18,6 +18,8 @@ module JitPreloader
18
18
  # end
19
19
 
20
20
  def run(preloader)
21
+ return unless (reflection.scope.nil? || reflection.scope.arity == 0) && klass.ancestors.include?(ActiveRecord::Base)
22
+
21
23
  super.tap do
22
24
  if preloaded_records.any? && preloaded_records.none?(&:jit_preloader)
23
25
  JitPreloader::Preloader.attach(preloaded_records) if owners.any?(&:jit_preloader) || JitPreloader.globally_enabled?
@@ -47,7 +49,7 @@ module JitPreloader
47
49
  association.target.concat(new_records)
48
50
  association.loaded!
49
51
  else
50
- association.target ||= records.first unless records.empty?
52
+ association.target = records.first unless records.empty?
51
53
  end
52
54
  end
53
55
 
@@ -16,6 +16,8 @@ module JitPreloader
16
16
  # end
17
17
 
18
18
  def run
19
+ return unless (reflection.scope.nil? || reflection.scope.arity == 0) && klass.ancestors.include?(ActiveRecord::Base)
20
+
19
21
  super.tap do
20
22
  if preloaded_records.any? && preloaded_records.none?(&:jit_preloader)
21
23
  JitPreloader::Preloader.attach(preloaded_records) if owners.any?(&:jit_preloader) || JitPreloader.globally_enabled?
@@ -4,7 +4,7 @@ module JitPreloader
4
4
  def load_target
5
5
  was_loaded = loaded?
6
6
 
7
- if !loaded? && owner.persisted? && owner.jit_preloader
7
+ if !loaded? && owner.persisted? && owner.jit_preloader && (reflection.scope.nil? || reflection.scope.arity == 0)
8
8
  owner.jit_preloader.jit_preload(reflection.name)
9
9
  end
10
10
 
@@ -23,6 +23,15 @@ module JitPreloadExtension
23
23
  return jit_preload_scoped_relations[name] if jit_preload_scoped_relations&.key?(name)
24
24
 
25
25
  records = jit_preloader&.records || [self]
26
+ previous_association_values = {}
27
+
28
+ records.each do |record|
29
+ association = record.association(base_association)
30
+ if association.loaded?
31
+ previous_association_values[record] = association.target
32
+ association.reset
33
+ end
34
+ end
26
35
 
27
36
  preloader_association = ActiveRecord::Associations::Preloader.new.preload(
28
37
  records,
@@ -33,7 +42,11 @@ module JitPreloadExtension
33
42
  records.each do |record|
34
43
  record.jit_preload_scoped_relations ||= {}
35
44
  association = record.association(base_association)
36
- record.jit_preload_scoped_relations[name] = preloader_association.records_by_owner[record]
45
+ record.jit_preload_scoped_relations[name] = preloader_association.records_by_owner[record] || []
46
+ association.reset
47
+ if previous_association_values.key?(record)
48
+ association.target = previous_association_values[record]
49
+ end
37
50
  end
38
51
 
39
52
  jit_preload_scoped_relations[name]
@@ -1,3 +1,3 @@
1
1
  module JitPreloader
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.4"
3
3
  end
@@ -74,6 +74,9 @@ RSpec.describe "ActiveRecord::Base Extensions" do
74
74
 
75
75
  it "doesn't load the value into the association" do
76
76
  contacts = Contact.jit_preload.limit(2).to_a
77
+ expect(contacts.first.association(:addresses)).to_not be_loaded
78
+ expect(contacts.last.association(:addresses)).to_not be_loaded
79
+
77
80
  call(contacts.first)
78
81
 
79
82
  expect(contacts.first.association(:addresses)).to_not be_loaded
@@ -90,5 +93,18 @@ RSpec.describe "ActiveRecord::Base Extensions" do
90
93
  expect(contacts.last.association(:addresses)).to be_loaded
91
94
  end
92
95
  end
96
+
97
+ context "when no records exist for the association" do
98
+ let!(:record) { Parent.create }
99
+
100
+ it "returns an empty array" do
101
+ value = record.preload_scoped_relation(
102
+ name: "Parent Children",
103
+ base_association: :parents_child
104
+ )
105
+
106
+ expect(value).to eq([])
107
+ end
108
+ end
93
109
  end
94
110
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  RSpec.describe JitPreloader::Preloader do
4
4
  let!(:contact1) do
@@ -154,6 +154,16 @@ RSpec.describe JitPreloader::Preloader do
154
154
  expect(c.contacts_count).to eql contact_owner_counts[i]
155
155
  end
156
156
  end
157
+
158
+ context "when a record has a polymorphic association type that's not an ActiveRecord" do
159
+ before do
160
+ contact1.update!(contact_owner_type: "NilClass", contact_owner_id: nil)
161
+ end
162
+
163
+ it "doesn't die while trying to load the association" do
164
+ expect(Contact.jit_preload.map(&:contact_owner)).to eq [nil, ContactOwner.first, Address.first]
165
+ end
166
+ end
157
167
  end
158
168
  end
159
169
 
@@ -192,6 +202,22 @@ RSpec.describe JitPreloader::Preloader do
192
202
  end
193
203
  end
194
204
 
205
+ context "when accessing an association with a scope that has a parameter" do
206
+ let!(:contact_book) { ContactBook.create(name: "The Yellow Pages") }
207
+ let!(:contact) { Contact.create(name: "Contact", contact_book: contact_book) }
208
+ let!(:company1) { Company.create(name: "Company1", contact_book: contact_book) }
209
+
210
+ it "is unable to be preloaded" do
211
+ ActiveSupport::Notifications.subscribed(callback, "n_plus_one_query") do
212
+ ContactBook.all.jit_preload.each do |contact_book|
213
+ expect(contact_book.contacts_with_scope.to_a).to eql [company1, contact]
214
+ end
215
+ end
216
+
217
+ expect(source_map).to eql(Hash[contact_book, [:contacts_with_scope]])
218
+ end
219
+ end
220
+
195
221
  context "when preloading an aggregate on a polymorphic has_many through relationship" do
196
222
  let(:contact_owner_addresses_counts) { [3] }
197
223
 
@@ -262,6 +288,22 @@ RSpec.describe JitPreloader::Preloader do
262
288
  end
263
289
  end
264
290
 
291
+ context "when a singular association id changes after preload" do
292
+ let!(:contact_book1) { ContactBook.create(name: "The Yellow Pages") }
293
+ let!(:contact_book2) { ContactBook.create(name: "The White Pages") }
294
+ let!(:company1) { Company.create(name: "Company1", contact_book: contact_book1) }
295
+ let!(:company2) { Company.create(name: "Company2", contact_book: contact_book1) }
296
+
297
+ it "allows the association to be reloaded" do
298
+ companies = Company.where(id: [company1.id, company2.id]).jit_preload.all.to_a
299
+ expect(companies.map(&:contact_book)).to match_array [contact_book1, contact_book1]
300
+
301
+ company = companies.each {|c| c.contact_book_id = contact_book2.id }
302
+
303
+ expect(companies.map(&:contact_book)).to match_array [contact_book2, contact_book2]
304
+ end
305
+ end
306
+
265
307
  context "when preloading an aggregate" do
266
308
  let(:addresses_counts) { [3, 0, 2] }
267
309
  let(:phone_number_counts) { [2, 0, 1] }
@@ -1,5 +1,6 @@
1
1
  class ContactBook < ActiveRecord::Base
2
2
  has_many :contacts
3
+ has_many :contacts_with_scope, ->(_contact_book) { desc }, class_name: "Contact", foreign_key: :contact_book_id
3
4
  has_many :employees, through: :contacts
4
5
 
5
6
  has_many :companies
@@ -29,6 +30,8 @@ class Contact < ActiveRecord::Base
29
30
  has_many_aggregate :addresses, :max_street_length, :maximum, "LENGTH(street)"
30
31
  has_many_aggregate :phone_numbers, :count, :count, "id"
31
32
  has_many_aggregate :addresses, :count, :count, "*"
33
+
34
+ scope :desc, ->{ order(id: :desc) }
32
35
  end
33
36
 
34
37
  class Company < Contact
metadata CHANGED
@@ -1,22 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jit_preloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.4
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: 2020-11-04 00:00:00.000000000 Z
11
+ date: 2021-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '5.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '7'
@@ -24,9 +24,9 @@ dependencies:
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ">"
27
+ - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '5.0'
29
+ version: '5.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '7'
@@ -151,9 +151,17 @@ executables: []
151
151
  extensions: []
152
152
  extra_rdoc_files: []
153
153
  files:
154
+ - ".github/workflows/CODEOWNERS"
155
+ - ".github/workflows/gem-push.yml"
154
156
  - ".gitignore"
155
157
  - ".rspec"
156
158
  - Gemfile
159
+ - Gemfile.5.2
160
+ - Gemfile.5.2.lock
161
+ - Gemfile.6.0
162
+ - Gemfile.6.0.lock
163
+ - Gemfile.6.1
164
+ - Gemfile.6.1.lock
157
165
  - LICENSE
158
166
  - README.md
159
167
  - Rakefile
@@ -193,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
201
  - !ruby/object:Gem::Version
194
202
  version: '0'
195
203
  requirements: []
196
- rubygems_version: 3.1.4
204
+ rubygems_version: 3.1.6
197
205
  signing_key:
198
206
  specification_version: 4
199
207
  summary: Tool to understand N+1 queries and to remove them