predictive_load 0.5.1 → 0.7.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/README.md +1 -2
- data/lib/predictive_load/active_record_collection_observation.rb +8 -10
- data/lib/predictive_load/loader.rb +19 -16
- data/lib/predictive_load/preload_log.rb +2 -4
- data/lib/predictive_load.rb +9 -14
- metadata +11 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93ddc441f98b6e7f9ec3b9a4277b5bf91cad8d360564e2c46dfae3a4963e647b
|
4
|
+
data.tar.gz: 05c1fc9459671d0cad41b68c9f6e5e5003e006ba582a5fedf3b63aca379a85fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6b4e23f4c7cbb1d4751e7aef27fabaa1e5ccb337276788489b08c031b0f3a9b9b3ac318328215579d78b2477ac95148af1dfa350a0635cb98959bb28c76b296
|
7
|
+
data.tar.gz: 8d836cbd60a8798408752818ec84b08165d27f9196645129683a1c15abd43288976575ad0e57c737c93e9169b9995a065c3d1bfea2a962da4b39f62d25f53a07
|
data/README.md
CHANGED
@@ -14,7 +14,7 @@ Observes Active Record collections and notifies when a member loads an associati
|
|
14
14
|
```ruby
|
15
15
|
require 'predictive_load'
|
16
16
|
require 'predictive_load/active_record_collection_observation'
|
17
|
-
ActiveRecord::Base.
|
17
|
+
ActiveRecord::Base.include(PredictiveLoad::ActiveRecordCollectionObservation)
|
18
18
|
|
19
19
|
require 'predictive_load/loader'
|
20
20
|
|
@@ -45,4 +45,3 @@ has_many :foos, predictive_load: false
|
|
45
45
|
|
46
46
|
* Calling association#size will trigger an N+1 on SELECT COUNT(*). Work around by calling #length, loading all records.
|
47
47
|
* Calling first / last will trigger an N+1.
|
48
|
-
* Rails 4: unscoped will disable eager loading to circument a rails bug ... hopefully fixed in rails 5 https://github.com/rails/rails/pull/16531
|
@@ -45,18 +45,16 @@ module PredictiveLoad::ActiveRecordCollectionObservation
|
|
45
45
|
|
46
46
|
# disable eager loading since includes + unscoped is broken on rails 4
|
47
47
|
module UnscopedTracker
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
predictive_load_disabled << self
|
53
|
-
super
|
54
|
-
ensure
|
55
|
-
predictive_load_disabled.pop
|
56
|
-
end
|
57
|
-
else
|
48
|
+
def unscoped
|
49
|
+
if block_given?
|
50
|
+
begin
|
51
|
+
predictive_load_disabled << self
|
58
52
|
super
|
53
|
+
ensure
|
54
|
+
predictive_load_disabled.pop
|
59
55
|
end
|
56
|
+
else
|
57
|
+
super
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
@@ -43,7 +43,7 @@ module PredictiveLoad
|
|
43
43
|
return false if ActiveRecord::Base.predictive_load_disabled.include?(association.klass)
|
44
44
|
return false if association.reflection.options[:predictive_load] == false
|
45
45
|
return false if association.reflection.options[:conditions].respond_to?(:to_proc) # rails 3 conditions proc (we do not know if it uses instance methods)
|
46
|
-
if
|
46
|
+
if scope = association.reflection.scope
|
47
47
|
if scope.is_a?(Proc)
|
48
48
|
# rails 4+ conditions block, if it uses a passed in object, we assume it is not preloadable
|
49
49
|
return false if scope.arity.to_i > 0
|
@@ -56,21 +56,24 @@ module PredictiveLoad
|
|
56
56
|
true
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
ActiveRecord::Associations::Preloader.new(rs, [
|
73
|
-
|
59
|
+
# https://github.com/rails/rails/blob/v4.2.10/activerecord/lib/active_record/associations/preloader.rb#L187 (similar to other Rails versions)
|
60
|
+
# If the first record association is loaded, Preloader aborts.
|
61
|
+
#
|
62
|
+
# In a code like `comments.each { |c| c.user }, if the first comment user_id is nil,
|
63
|
+
# when calling the method (`user`) ActiveRecord doesn't load the association, but marks it as loaded.
|
64
|
+
# So when the second comment calls `user` (and user_id is not nil), @records.first will be the first
|
65
|
+
# comment above (with thr association already loaded), which will be checked by Preloader and used to skip
|
66
|
+
# any preloading.
|
67
|
+
#
|
68
|
+
# Fix is pretty simple, ignore any record with association already loaded.
|
69
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
70
|
+
def preload(association_name)
|
71
|
+
rs = records_with_association(association_name).reject { |r| r.association(association_name).loaded? }
|
72
|
+
ActiveRecord::Associations::Preloader.new(records: rs, associations: [association_name]).call
|
73
|
+
end
|
74
|
+
else
|
75
|
+
def preload(association_name)
|
76
|
+
rs = records_with_association(association_name).reject { |r| r.association(association_name).loaded? }
|
74
77
|
ActiveRecord::Associations::Preloader.new.preload(rs, [ association_name ])
|
75
78
|
end
|
76
79
|
end
|
@@ -8,16 +8,14 @@ module PredictiveLoad
|
|
8
8
|
def preload(association)
|
9
9
|
grouped_records(association).each do |reflection, klasses|
|
10
10
|
klasses.each do |klass, records|
|
11
|
-
|
12
|
-
preloader = preloader_for(reflection).new(klass, records, reflection, preload_scope)
|
11
|
+
preloader = preloader_for(reflection).new(klass, records, reflection, preload_scope)
|
13
12
|
|
14
13
|
if preloader.respond_to?(:through_reflection)
|
15
14
|
log("encountered :through association for #{association}. Requires loading records to generate query, so skipping for now.")
|
16
15
|
next
|
17
16
|
end
|
18
17
|
|
19
|
-
|
20
|
-
preload_sql = scope.where(collection_arel(preloader)).to_sql
|
18
|
+
preload_sql = preloader.scope.where(collection_arel(preloader)).to_sql
|
21
19
|
|
22
20
|
log("would preload with: #{preload_sql.to_s}")
|
23
21
|
klass.connection.explain(preload_sql).each_line do |line|
|
data/lib/predictive_load.rb
CHANGED
@@ -1,18 +1,13 @@
|
|
1
1
|
module PredictiveLoad
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
if ActiveRecord::VERSION::MAJOR == 3
|
8
|
-
# when belongs_to etc is loaded before us it already made a copy of valid_options
|
9
|
-
klasses.concat ActiveRecord::Associations::Builder::Association.descendants
|
2
|
+
module Rails5AssociationOptions
|
3
|
+
def valid_options(options)
|
4
|
+
super + [:predictive_load]
|
5
|
+
end
|
6
|
+
end
|
10
7
|
end
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
klass::VALID_OPTIONS << :predictive_load
|
17
|
-
end
|
9
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
10
|
+
ActiveRecord::Associations::Builder::Association.singleton_class.prepend(PredictiveLoad::Rails5AssociationOptions)
|
11
|
+
else
|
12
|
+
ActiveRecord::Associations::Builder::Association.valid_options << :predictive_load
|
18
13
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: predictive_load
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Chapweske
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '6.0'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '7.1'
|
23
23
|
type: :runtime
|
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:
|
29
|
+
version: '6.0'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '7.1'
|
33
33
|
description: Predictive loader
|
34
34
|
email:
|
35
35
|
- eac@zendesk.com
|
@@ -47,7 +47,7 @@ homepage: https://github.com/zendesk/predictive_load
|
|
47
47
|
licenses:
|
48
48
|
- Apache License Version 2.0
|
49
49
|
metadata: {}
|
50
|
-
post_install_message:
|
50
|
+
post_install_message:
|
51
51
|
rdoc_options: []
|
52
52
|
require_paths:
|
53
53
|
- lib
|
@@ -55,16 +55,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
55
|
requirements:
|
56
56
|
- - ">="
|
57
57
|
- !ruby/object:Gem::Version
|
58
|
-
version: '2.
|
58
|
+
version: '2.7'
|
59
59
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
61
|
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: '0'
|
64
64
|
requirements: []
|
65
|
-
|
66
|
-
|
67
|
-
signing_key:
|
65
|
+
rubygems_version: 3.0.3.1
|
66
|
+
signing_key:
|
68
67
|
specification_version: 4
|
69
68
|
summary: ''
|
70
69
|
test_files: []
|