jit_preloader 1.0.0 → 1.0.4

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