goldiloader 3.0.2 → 4.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cea2be18413e1cc1e5d2dc0a325a808f64165a52
4
- data.tar.gz: b9ea0538d9894df028174a7e081acd5b96b4455b
2
+ SHA256:
3
+ metadata.gz: 2b1702016efdeb47b12f583ff299bc46125714acc053b7c8594a9cf9a4fcba07
4
+ data.tar.gz: 6485555c4d71199683907813d9563fdecce3c8bada819d7ae1180f48ad5afadb
5
5
  SHA512:
6
- metadata.gz: 28df2ae2eed0bc12cd4d29458aebbc16cf5f667d2950d8b113bf92859447b75a2380d83abf59ef1b36ec0d707fe3954ef25d12b642b69502850d142aa590607e
7
- data.tar.gz: 1ebb5abe80281c1a76fc3b3dd8eed50367ccde1dce353493d90d7da468750e436c4d273e39e1d1388800d31ba2676e4729fbc37ee271e063267a83b6612c96c5
6
+ metadata.gz: 949fc36b600662dde9597b87c26430949bbdc12dff2d2eb9845405dcb273c2c9621edbb8d850f95dd602c892687dffc95849243bbd94c4e3473c31932f48464a
7
+ data.tar.gz: 5d645a33f703ad458096af205535efcd5cebb2ab802ae0af8203cc6236f388a73d79ec0c08681c447dbbe4b057a29a0bd373c001ce277dd34285447614f138a8
data/lib/goldiloader.rb CHANGED
@@ -4,7 +4,7 @@ require 'active_support/all'
4
4
  require 'active_record'
5
5
  require 'goldiloader/compatibility'
6
6
  require 'goldiloader/auto_include_context'
7
- require 'goldiloader/association_info'
7
+ require 'goldiloader/scope_info'
8
8
  require 'goldiloader/association_options'
9
9
  require 'goldiloader/association_loader'
10
10
  require 'goldiloader/active_record_patches'
@@ -47,21 +47,12 @@ module Goldiloader
47
47
  end
48
48
 
49
49
  def auto_include_value
50
- # Note: Don't use get_value because that doesn't work properly with defaulting boolean values
51
50
  @values.fetch(:auto_include, true)
52
51
  end
53
52
 
54
53
  def auto_include_value=(value)
55
- if Goldiloader::Compatibility.rails_4?
56
- raise ::ActiveRecord::Relation::ImmutableRelation if @loaded
57
- check_cached_relation
58
- @values[:auto_include] = value
59
- elsif Goldiloader::Compatibility.rails_5_0?
60
- assert_mutability!
61
- @values[:auto_include] = value
62
- else
63
- set_value(:auto_include, value)
64
- end
54
+ assert_mutability!
55
+ @values[:auto_include] = value
65
56
  end
66
57
  end
67
58
  ::ActiveRecord::Relation.prepend(::Goldiloader::RelationPatch)
@@ -76,6 +67,29 @@ module Goldiloader
76
67
  end
77
68
  ActiveRecord::Relation::Merger.prepend(::Goldiloader::MergerPatch)
78
69
 
70
+ module AssociationReflectionPatch
71
+ def eager_loadable?
72
+ return @eager_loadable if instance_variable_defined?(:@eager_loadable)
73
+
74
+ @eager_loadable = if scope.nil?
75
+ # Associations without any scoping options are eager loadable
76
+ true
77
+ elsif scope.arity > 0
78
+ # The scope will be evaluated for every model instance so it can't
79
+ # be eager loaded
80
+ false
81
+ else
82
+ scope_info = Goldiloader::ScopeInfo.new(scope_for(klass.unscoped))
83
+ scope_info.auto_include? &&
84
+ !scope_info.limit? &&
85
+ !scope_info.offset? &&
86
+ (!has_one? || !scope_info.order?)
87
+ end
88
+ end
89
+ end
90
+ ActiveRecord::Reflection::AssociationReflection.include(::Goldiloader::AssociationReflectionPatch)
91
+ ActiveRecord::Reflection::ThroughReflection.include(::Goldiloader::AssociationReflectionPatch)
92
+
79
93
  module AssociationPatch
80
94
  extend ActiveSupport::Concern
81
95
 
@@ -97,25 +111,26 @@ module Goldiloader
97
111
  private
98
112
 
99
113
  def eager_loadable?
100
- association_info = Goldiloader::AssociationInfo.new(self)
101
- association_info.auto_include? &&
102
- !association_info.limit? &&
103
- !association_info.offset? &&
104
- (!association_info.has_one? || !association_info.order?) &&
105
- (::Goldiloader::Compatibility.group_eager_loadable? || !association_info.group?) &&
106
- (::Goldiloader::Compatibility.from_eager_loadable? || !association_info.from?) &&
107
- (::Goldiloader::Compatibility.destroyed_model_associations_eager_loadable? || !owner.destroyed?) &&
108
- !association_info.instance_dependent?
114
+ reflection.eager_loadable? &&
115
+ (::Goldiloader::Compatibility.destroyed_model_associations_eager_loadable? || !owner.destroyed?)
109
116
  end
110
117
 
111
118
  def load_with_auto_include
112
119
  if loaded? && !stale_target?
113
120
  target
114
- elsif auto_include?
121
+ elsif !auto_include?
122
+ yield
123
+ elsif owner.auto_include_context.size == 1
124
+ # Bypassing the preloader for a single model reduces object allocations by ~5% in benchmarks
125
+ result = yield
126
+ # As of https://github.com/rails/rails/commit/bd3b28f7f181dce53e872daa23dda101498b8fb4
127
+ # ActiveRecord does not use ActiveRecord::Relation#exec_queries to resolve association
128
+ # queries
129
+ Goldiloader::AutoIncludeContext.register_models(result)
130
+ result
131
+ else
115
132
  Goldiloader::AssociationLoader.load(owner, reflection.name)
116
133
  target
117
- else
118
- yield
119
134
  end
120
135
  end
121
136
  end
@@ -132,12 +147,7 @@ module Goldiloader
132
147
 
133
148
  module CollectionAssociationPatch
134
149
  # Force these methods to load the entire association for fully_load associations
135
- association_methods = [:size, :ids_reader, :empty?]
136
- if Goldiloader::Compatibility::ACTIVE_RECORD_VERSION < ::Gem::Version.new('5.1')
137
- association_methods.concat([:first, :second, :third, :fourth, :fifth, :last])
138
- end
139
-
140
- association_methods.each do |method|
150
+ [:size, :ids_reader, :empty?].each do |method|
141
151
  define_method(method) do |*args, &block|
142
152
  load_target if fully_load?
143
153
  super(*args, &block)
@@ -148,10 +158,8 @@ module Goldiloader
148
158
  load_with_auto_include { super }
149
159
  end
150
160
 
151
- if Goldiloader::Compatibility::ACTIVE_RECORD_VERSION >= ::Gem::Version.new('5.1')
152
- def find_from_target?
153
- fully_load? || super
154
- end
161
+ def find_from_target?
162
+ fully_load? || super
155
163
  end
156
164
  end
157
165
  ::ActiveRecord::Associations::CollectionAssociation.prepend(::Goldiloader::CollectionAssociationPatch)
@@ -15,17 +15,11 @@ module Goldiloader
15
15
  private
16
16
 
17
17
  def eager_load(models, association_name)
18
- ::ActiveRecord::Associations::Preloader.new.preload(models, [association_name])
19
- end
20
-
21
- def first_model_with_association(models, association_name)
22
- models.find { |model| has_association?(model, association_name) }
23
- end
24
-
25
- def associated_models(models, association_name)
26
- # We can't just do model.send(association_name) because the association method may have been
27
- # overridden
28
- models.map { |model| model.association(association_name).target }.flatten.compact.uniq
18
+ if Goldiloader::Compatibility.pre_rails_6_2?
19
+ ::ActiveRecord::Associations::Preloader.new.preload(models, [association_name])
20
+ else
21
+ ::ActiveRecord::Associations::Preloader.new(records: models, associations: [association_name]).call
22
+ end
29
23
  end
30
24
 
31
25
  def load?(model, association_name)
@@ -18,11 +18,7 @@ module Goldiloader
18
18
  end
19
19
 
20
20
  def register
21
- if ::ActiveRecord::VERSION::MAJOR >= 5
22
- ActiveRecord::Associations::Builder::Association.extensions << AssociationBuilderExtension
23
- else
24
- ActiveRecord::Associations::Builder::Association.valid_options.concat(OPTIONS)
25
- end
21
+ ActiveRecord::Associations::Builder::Association.extensions << AssociationBuilderExtension
26
22
  end
27
23
  end
28
24
  end
@@ -4,6 +4,8 @@ module Goldiloader
4
4
  class AutoIncludeContext
5
5
  attr_reader :models
6
6
 
7
+ delegate :size, to: :models
8
+
7
9
  def initialize
8
10
  @models = []
9
11
  end
@@ -2,34 +2,17 @@
2
2
 
3
3
  module Goldiloader
4
4
  module Compatibility
5
- ACTIVE_RECORD_VERSION = ::Gem::Version.new(::ActiveRecord::VERSION::STRING)
5
+ ACTIVE_RECORD_VERSION = ::Gem::Version.new(::ActiveRecord::VERSION::STRING).release
6
+ PRE_RAILS_6_2 = ACTIVE_RECORD_VERSION < ::Gem::Version.new('6.2.0')
6
7
  RAILS_5_2_0 = ACTIVE_RECORD_VERSION == ::Gem::Version.new('5.2.0')
7
- PRE_RAILS_5_2 = ACTIVE_RECORD_VERSION < ::Gem::Version.new('5.2.0')
8
- POST_RAILS_5_1_4 = ACTIVE_RECORD_VERSION > ::Gem::Version.new('5.1.5')
9
- PRE_RAILS_5_1_5 = ACTIVE_RECORD_VERSION < ::Gem::Version.new('5.1.5')
10
- FROM_EAGER_LOADABLE = ACTIVE_RECORD_VERSION >= ::Gem::Version.new('5.1.5') ||
11
- (ACTIVE_RECORD_VERSION >= ::Gem::Version.new('5.0.7') && ACTIVE_RECORD_VERSION < ::Gem::Version.new('5.1.0'))
12
- GROUP_EAGER_LOADABLE = FROM_EAGER_LOADABLE
13
8
 
14
- def self.rails_4?
15
- ::ActiveRecord::VERSION::MAJOR == 4
16
- end
17
-
18
- def self.rails_5_0?
19
- ::ActiveRecord::VERSION::MAJOR == 5 && ::ActiveRecord::VERSION::MINOR == 0
9
+ def self.pre_rails_6_2?
10
+ PRE_RAILS_6_2
20
11
  end
21
12
 
22
13
  # See https://github.com/rails/rails/pull/32375
23
14
  def self.destroyed_model_associations_eager_loadable?
24
15
  !RAILS_5_2_0
25
16
  end
26
-
27
- def self.from_eager_loadable?
28
- FROM_EAGER_LOADABLE
29
- end
30
-
31
- def self.group_eager_loadable?
32
- GROUP_EAGER_LOADABLE
33
- end
34
17
  end
35
18
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Goldiloader
4
+ class ScopeInfo
5
+ attr_reader :scope
6
+
7
+ def initialize(scope)
8
+ @scope = scope
9
+ end
10
+
11
+ def offset?
12
+ scope.offset_value.present?
13
+ end
14
+
15
+ def limit?
16
+ scope.limit_value.present?
17
+ end
18
+
19
+ def auto_include?
20
+ scope.auto_include_value
21
+ end
22
+
23
+ def from?
24
+ scope.from_clause.present?
25
+ end
26
+
27
+ def group?
28
+ scope.group_values.present?
29
+ end
30
+
31
+ def order?
32
+ scope.order_values.present?
33
+ end
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Goldiloader
4
- VERSION = '3.0.2'
4
+ VERSION = '4.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goldiloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Turkel
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-05 00:00:00.000000000 Z
11
+ date: 2021-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,40 +16,40 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.2'
19
+ version: '5.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '5.3'
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: '4.2'
29
+ version: '5.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '5.3'
32
+ version: '7.1'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: activesupport
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '4.2'
39
+ version: '5.2'
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '5.3'
42
+ version: '7.1'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: '4.2'
49
+ version: '5.2'
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '5.3'
52
+ version: '7.1'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: appraisal
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -65,7 +65,7 @@ dependencies:
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
67
  - !ruby/object:Gem::Dependency
68
- name: coveralls
68
+ name: benchmark-ips
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - ">="
@@ -78,6 +78,20 @@ dependencies:
78
78
  - - ">="
79
79
  - !ruby/object:Gem::Version
80
80
  version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: coveralls_reborn
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: 0.18.0
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 0.18.0
81
95
  - !ruby/object:Gem::Dependency
82
96
  name: database_cleaner
83
97
  requirement: !ruby/object:Gem::Requirement
@@ -134,20 +148,34 @@ dependencies:
134
148
  - - "~>"
135
149
  - !ruby/object:Gem::Version
136
150
  version: '3'
151
+ - !ruby/object:Gem::Dependency
152
+ name: rspec_junit_formatter
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
137
165
  - !ruby/object:Gem::Dependency
138
166
  name: salsify_rubocop
139
167
  requirement: !ruby/object:Gem::Requirement
140
168
  requirements:
141
- - - '='
169
+ - - "~>"
142
170
  - !ruby/object:Gem::Version
143
- version: 0.52.1.1
171
+ version: 1.0.1
144
172
  type: :development
145
173
  prerelease: false
146
174
  version_requirements: !ruby/object:Gem::Requirement
147
175
  requirements:
148
- - - '='
176
+ - - "~>"
149
177
  - !ruby/object:Gem::Version
150
- version: 0.52.1.1
178
+ version: 1.0.1
151
179
  - !ruby/object:Gem::Dependency
152
180
  name: simplecov
153
181
  requirement: !ruby/object:Gem::Requirement
@@ -166,16 +194,16 @@ dependencies:
166
194
  name: sqlite3
167
195
  requirement: !ruby/object:Gem::Requirement
168
196
  requirements:
169
- - - ">="
197
+ - - "~>"
170
198
  - !ruby/object:Gem::Version
171
- version: '0'
199
+ version: '1.3'
172
200
  type: :development
173
201
  prerelease: false
174
202
  version_requirements: !ruby/object:Gem::Requirement
175
203
  requirements:
176
- - - ">="
204
+ - - "~>"
177
205
  - !ruby/object:Gem::Version
178
- version: '0'
206
+ version: '1.3'
179
207
  description: Automatically eager loads Rails associations as associations are traversed
180
208
  email:
181
209
  - jturkel@salsify.com
@@ -186,11 +214,11 @@ files:
186
214
  - LICENSE.txt
187
215
  - lib/goldiloader.rb
188
216
  - lib/goldiloader/active_record_patches.rb
189
- - lib/goldiloader/association_info.rb
190
217
  - lib/goldiloader/association_loader.rb
191
218
  - lib/goldiloader/association_options.rb
192
219
  - lib/goldiloader/auto_include_context.rb
193
220
  - lib/goldiloader/compatibility.rb
221
+ - lib/goldiloader/scope_info.rb
194
222
  - lib/goldiloader/version.rb
195
223
  homepage: https://github.com/salsify/goldiloader
196
224
  licenses:
@@ -200,7 +228,7 @@ metadata:
200
228
  changelog_uri: https://github.com/salsify/goldiloader/blob/master/CHANGELOG.md
201
229
  source_code_uri: https://github.com/salsify/goldiloader/
202
230
  bug_tracker_uri: https://github.com/salsify/goldiloader/issues
203
- post_install_message:
231
+ post_install_message:
204
232
  rdoc_options: []
205
233
  require_paths:
206
234
  - lib
@@ -208,16 +236,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
208
236
  requirements:
209
237
  - - ">="
210
238
  - !ruby/object:Gem::Version
211
- version: '2.3'
239
+ version: '2.6'
212
240
  required_rubygems_version: !ruby/object:Gem::Requirement
213
241
  requirements:
214
242
  - - ">="
215
243
  - !ruby/object:Gem::Version
216
244
  version: '0'
217
245
  requirements: []
218
- rubyforge_project:
219
- rubygems_version: 2.6.14
220
- signing_key:
246
+ rubygems_version: 3.1.4
247
+ signing_key:
221
248
  specification_version: 4
222
249
  summary: Automatic Rails association eager loading
223
250
  test_files: []
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Goldiloader
4
- class AssociationInfo
5
-
6
- def initialize(association)
7
- @association = association
8
- end
9
-
10
- delegate :association_scope, :reflection, to: :@association
11
-
12
- def has_one? # rubocop:disable Naming/PredicateName
13
- reflection.has_one?
14
- end
15
-
16
- def offset?
17
- association_scope && association_scope.offset_value.present?
18
- end
19
-
20
- def limit?
21
- association_scope && association_scope.limit_value.present?
22
- end
23
-
24
- def auto_include?
25
- association_scope.nil? || association_scope.auto_include_value
26
- end
27
-
28
- def from?
29
- if ActiveRecord::VERSION::MAJOR >= 5
30
- association_scope && association_scope.from_clause.present?
31
- else
32
- association_scope && association_scope.from_value.present?
33
- end
34
- end
35
-
36
- def group?
37
- association_scope && association_scope.group_values.present?
38
- end
39
-
40
- def order?
41
- association_scope && association_scope.order_values.present?
42
- end
43
-
44
- def instance_dependent?
45
- reflection.scope.present? && reflection.scope.arity > 0
46
- end
47
- end
48
- end