predictive_load 0.6.0 → 0.8.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: f59d44d0c89dbf1ef633348451e1df43d6b059ddb50094549e2bc6a5f4843531
4
- data.tar.gz: 75cd6c8518d8f200c13a8c52ed887b7e1a0d1e72aa02205305f7bc65d1b4b9ca
3
+ metadata.gz: 738aa194d5fa872a614133eafb9bf2eefe2bc4c6958eaf02784cdee1e3b56ade
4
+ data.tar.gz: ff4d1953f00722fee627b1b73c41d1af2ee3b5168993886dfa9bd65529d616d5
5
5
  SHA512:
6
- metadata.gz: cfb8c2e28057981f773794139483805712c17faad421506c39d5309961b2be0c7d2b541c6afd4e0877f2e40703e3c036832651b6f569e57799d12ae0bd8ca20e
7
- data.tar.gz: 49482a1ffcd183092f0f71242ab78fecca8b583a69aa8f90816173edcee4226f389061a36bccb27d455679132cb8dc6a756f6d3dc025293198213e5e7962cba4
6
+ metadata.gz: 7d5c7f2b7bd5ce257238cc0a767f44dbb0889b5fbd5311dd7f56ce0cc237c6d424e9a78f0758fac73ca2e8ec55226f01db3f843455a7d2324b7d02f1aead937f
7
+ data.tar.gz: 3fd1edf5e995179c55e9bfba6a1801daad33f036f2f95ac2f56b928c143f65957d9be7221434592f5896ce7087f50f0eb1103b3f2022ffe710a16133a49a7ebd
data/README.md CHANGED
@@ -41,8 +41,29 @@ Some things cannot be preloaded, use `predictive_load: false`
41
41
  has_many :foos, predictive_load: false
42
42
  ```
43
43
 
44
+ ### Instrumentation
45
+
46
+ The library can be instrumented by providing a callback, to be invoked every time automatic preloading happens. The callback must be a callable that receives two arguments:
47
+ * The record (instance) on which the queries that triggered automatic preloading are being performed, in the form of some association call.
48
+ * The association object, which can be inspected to check the type and name of the association.
49
+
50
+ For example, the callback could be used to emit some metrics:
51
+
52
+ ```ruby
53
+ require "active_support/core_ext/string"
54
+
55
+ PredictiveLoad.callback = -> (record, association) do
56
+ METRICS_CLIENT.increment_counter(
57
+ "active_record.automatic_preloads",
58
+ tags: [
59
+ "model:#{record.class.name.underscore}",
60
+ "association:#{association.reflection.name}"
61
+ ]
62
+ )
63
+ end
64
+ ```
65
+
44
66
  #### Known limitations:
45
67
 
46
68
  * Calling association#size will trigger an N+1 on SELECT COUNT(*). Work around by calling #length, loading all records.
47
69
  * 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
@@ -1,5 +1,4 @@
1
1
  module PredictiveLoad::ActiveRecordCollectionObservation
2
-
3
2
  def self.included(base)
4
3
  ActiveRecord::Relation.class_attribute :collection_observer
5
4
  if ActiveRecord::VERSION::MAJOR >= 5
@@ -38,9 +37,7 @@ module PredictiveLoad::ActiveRecordCollectionObservation
38
37
  end
39
38
 
40
39
  module CollectionMember
41
-
42
40
  attr_accessor :collection_observer
43
-
44
41
  end
45
42
 
46
43
  # disable eager loading since includes + unscoped is broken on rails 4
@@ -73,9 +70,7 @@ module PredictiveLoad::ActiveRecordCollectionObservation
73
70
  protected
74
71
 
75
72
  def notify_collection_observer
76
- if @owner.collection_observer
77
- @owner.collection_observer.loading_association(@owner, self)
78
- end
73
+ @owner.collection_observer&.loading_association(@owner, self)
79
74
  end
80
75
  end
81
76
 
@@ -86,5 +81,4 @@ module PredictiveLoad::ActiveRecordCollectionObservation
86
81
  super
87
82
  end
88
83
  end
89
-
90
84
  end
@@ -23,6 +23,7 @@ module PredictiveLoad
23
23
  association_name = association.reflection.name
24
24
 
25
25
  if all_records_will_likely_load_association?(association_name) && supports_preload?(association)
26
+ PredictiveLoad.callback&.call(record, association)
26
27
  preload(association_name)
27
28
  end
28
29
  end
@@ -32,7 +33,7 @@ module PredictiveLoad
32
33
  attr_reader :records
33
34
 
34
35
  def all_records_will_likely_load_association?(association_name)
35
- if defined?(Mocha) && association_name.to_s.index('_stub_')
36
+ if defined?(Mocha) && association_name.to_s.index("_stub_")
36
37
  false
37
38
  else
38
39
  true
@@ -43,11 +44,11 @@ module PredictiveLoad
43
44
  return false if ActiveRecord::Base.predictive_load_disabled.include?(association.klass)
44
45
  return false if association.reflection.options[:predictive_load] == false
45
46
  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 scope = association.reflection.scope
47
+ if (scope = association.reflection.scope)
47
48
  if scope.is_a?(Proc)
48
49
  # rails 4+ conditions block, if it uses a passed in object, we assume it is not preloadable
49
50
  return false if scope.arity.to_i > 0
50
- elsif where = scope.options[:where]
51
+ elsif (where = scope.options[:where])
51
52
  # ActiveRecord::Associations::Builder::DeprecatedOptionsProc from rails 4.0 and deprecated finders
52
53
  # when conditions was a proc the where will be a proc too -> check arity
53
54
  return false if where.is_a?(Proc) && where.arity > 0
@@ -74,7 +75,7 @@ module PredictiveLoad
74
75
  else
75
76
  def preload(association_name)
76
77
  rs = records_with_association(association_name).reject { |r| r.association(association_name).loaded? }
77
- ActiveRecord::Associations::Preloader.new.preload(rs, [ association_name ])
78
+ ActiveRecord::Associations::Preloader.new.preload(rs, [association_name])
78
79
  end
79
80
  end
80
81
 
@@ -1,8 +1,7 @@
1
- require 'active_record/associations/preloader'
1
+ require "active_record/associations/preloader"
2
2
 
3
3
  module PredictiveLoad
4
4
  class PreloadLog < ActiveRecord::Associations::Preloader
5
-
6
5
  attr_accessor :logger
7
6
 
8
7
  def preload(association)
@@ -17,7 +16,7 @@ module PredictiveLoad
17
16
 
18
17
  preload_sql = preloader.scope.where(collection_arel(preloader)).to_sql
19
18
 
20
- log("would preload with: #{preload_sql.to_s}")
19
+ log("would preload with: #{preload_sql}")
21
20
  klass.connection.explain(preload_sql).each_line do |line|
22
21
  log(line)
23
22
  end
@@ -34,7 +33,5 @@ module PredictiveLoad
34
33
  def log(message)
35
34
  ActiveRecord::Base.logger.info("predictive_load: #{message}")
36
35
  end
37
-
38
36
  end
39
-
40
37
  end
@@ -4,6 +4,27 @@ module PredictiveLoad
4
4
  super + [:predictive_load]
5
5
  end
6
6
  end
7
+
8
+ class << self
9
+ attr_reader :callback
10
+
11
+ # Configure a callback to be invoked when the library preloads some association.
12
+ #
13
+ # It must be a callable with an arity of two.
14
+ # The callback receives two arguments:
15
+ # - The record (instance) on which the queries that triggered automatic preloading
16
+ # are being performed, in the form of some association call.
17
+ # - The association object, which can be inspected to check the type and name of
18
+ # the association.
19
+ #
20
+ def callback=(c)
21
+ if c.nil? || (c.respond_to?(:call) && c.respond_to?(:arity) && c.arity == 2)
22
+ @callback = c
23
+ else
24
+ raise ArgumentError, "wrong callback type, it must be a callable that supports 2 arguments"
25
+ end
26
+ end
27
+ end
7
28
  end
8
29
 
9
30
  if ActiveRecord::VERSION::MAJOR >= 5
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.6.0
4
+ version: 0.8.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: 2022-09-14 00:00:00.000000000 Z
11
+ date: 2023-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.2.0
19
+ version: '6.0'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '7.1'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 4.2.0
29
+ version: '6.0'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '7.1'
@@ -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,15 +55,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
55
55
  requirements:
56
56
  - - ">="
57
57
  - !ruby/object:Gem::Version
58
- version: '2.4'
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
- rubygems_version: 3.1.6
66
- signing_key:
65
+ rubygems_version: 3.0.3.1
66
+ signing_key:
67
67
  specification_version: 4
68
68
  summary: ''
69
69
  test_files: []