goldiloader 0.0.4 → 0.0.5

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
  SHA1:
3
- metadata.gz: 856cc3b8807810dbf9e6b357bbc460d4c0bec3ac
4
- data.tar.gz: b8cd9a6f87ffa7a9427dd089ad7fcab7580361da
3
+ metadata.gz: 3912274cc2f34e5dafb2a3b1d8ee8e3f6521885e
4
+ data.tar.gz: 72e93497e463d924c5c06c49df9d10cb7427170a
5
5
  SHA512:
6
- metadata.gz: 020f3bb39360cf4253f738592bb06fc222d83c71d6a6b343ca7e4df36741bf937f6e5bd62d7faee8c83638a6b020e2f950176184b757ac0b11a0a88687057c17
7
- data.tar.gz: 4d3f56fd3c361608314c2c057cf0cbb9620513db6ecbc0595ac9e8f95a72a115c04339f678877c83142ed278d52f03a503a66ff651958deb39bfe038d1fe44cf
6
+ metadata.gz: 80396bbcaddfe170d4fd0db9a199839ef07c65d4fbfe8947de40dd31ab0500c86db372c11b105f3efaa980506402582af656da202088d7cd559c1f2d0dc10521
7
+ data.tar.gz: 060d399492cbbcd68cdb053fbc08c91781826404678e694287768d770c42736b39e5ad8e06cbad4600c0b2262fee4af4df469232fd973b6ccadb562c969d34ec
data/CHANGELOG.md CHANGED
@@ -1,11 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ### 0.0.5
4
+
5
+ * Fix ActiveRecord and ActiveSupport dependencies to work with versions greater than 4.1.0. Thanks for the pull
6
+ requests [Alexey Volodkin](https://github.com/miraks) and [Philip Claren](https://github.com/DerKobe).
7
+ * Workaround [issue 13](https://github.com/salsify/goldiloader/issues/13) by not auto-eager loading associations
8
+ that use `unscope`. This workaround will be removed when the underlying
9
+ [bug 11036](https://github.com/rails/rails/issues/11036) in the Rails eager loader is fixed.
10
+ * Workaround [issue 11](https://github.com/salsify/goldiloader/issues/11) by not auto-eager loading associations
11
+ that use `joins`. This workaround will be removed when the underlying
12
+ [bug 11518](https://github.com/rails/rails/pull/11518) in the Rails eager loader is fixed.
13
+ * Fix [issue 15](https://github.com/salsify/goldiloader/issues/15) - Don't auto eager load associations
14
+ with finder_sql in Rails 4.0. Previously this was only done for Rails 3.2.
15
+
3
16
  ### 0.0.4
4
17
 
5
- * Fix [issue 3](https://github.com/salsify/goldiloader/issues/3) - `exists?` method should take an argument
6
- thanks for reporting [Bert Goethals](https://github.com/Bertg)
18
+ * Fix [issue 3](https://github.com/salsify/goldiloader/issues/3) - `exists?` method should take an argument.
19
+ Thanks for reporting [Bert Goethals](https://github.com/Bertg)
7
20
  * Fix [issue 4](https://github.com/salsify/goldiloader/issues/4) - Associations couldn't be loaded in after
8
- destroy callbacks thanks for reporting [Bert Goethals](https://github.com/Bertg)
21
+ destroy callbacks. Thanks for reporting [Bert Goethals](https://github.com/Bertg)
9
22
  * Fix [issue 6](https://github.com/salsify/goldiloader/issues/6) - Models in read only associations weren't
10
23
  being marked as read only
11
24
  * Fix [issue 7](https://github.com/salsify/goldiloader/issues/7) - Don't attempt to eager load associations that
data/goldiloader.gemspec CHANGED
@@ -17,8 +17,8 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = Dir.glob('spec/**/*')
18
18
  spec.require_paths = ['lib']
19
19
 
20
- spec.add_dependency 'activerecord', ENV.fetch('RAILS_VERSION', ['>= 3.2', '<= 4.1'])
21
- spec.add_dependency 'activesupport', ENV.fetch('RAILS_VERSION', ['>= 3.2', '<= 4.1'])
20
+ spec.add_dependency 'activerecord', ENV.fetch('RAILS_VERSION', ['>= 3.2', '< 4.2'])
21
+ spec.add_dependency 'activesupport', ENV.fetch('RAILS_VERSION', ['>= 3.2', '< 4.2'])
22
22
 
23
23
  spec.add_development_dependency 'coveralls'
24
24
  spec.add_development_dependency 'database_cleaner', '>= 1.2'
data/lib/goldiloader.rb CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'active_support/all'
4
4
  require 'active_record'
5
+ require 'goldiloader/compatibility'
5
6
  require 'goldiloader/auto_include_context'
6
7
  require 'goldiloader/association_info'
7
8
  require 'goldiloader/association_options'
8
9
  require 'goldiloader/association_loader'
9
- require 'goldiloader/model_registry'
10
10
  require 'goldiloader/active_record_patches'
@@ -14,7 +14,12 @@ module Goldiloader
14
14
  end
15
15
 
16
16
  def auto_include_context
17
- @auto_include_context ||= Goldiloader::AutoIncludeContext.create_empty.register_model(self)
17
+ @auto_include_context ||= Goldiloader::AutoIncludeContext.new.register_model(self)
18
+ end
19
+
20
+ def reload(*)
21
+ @auto_include_context = nil
22
+ super
18
23
  end
19
24
  end
20
25
  end
@@ -28,7 +33,7 @@ ActiveRecord::Relation.class_eval do
28
33
 
29
34
  models = exec_queries_without_auto_include
30
35
  # Add all loaded models to the same AutoIncludeContext
31
- auto_include_context = Goldiloader::AutoIncludeContext.create_empty
36
+ auto_include_context = Goldiloader::AutoIncludeContext.new
32
37
  auto_include_context.register_models(models)
33
38
  models
34
39
  end
@@ -52,27 +57,26 @@ ActiveRecord::Associations::Association.class_eval do
52
57
  !loaded? && options.fetch(:fully_load) { self.class.default_fully_load }
53
58
  end
54
59
 
55
- def auto_include_context
56
- @auto_include_context ||= Goldiloader::AutoIncludeContext.new(owner.auto_include_context.model_registry,
57
- owner.auto_include_context.association_path + [reflection.name])
58
- end
60
+ private
59
61
 
60
62
  def eager_loadable?
61
63
  association_info = Goldiloader::AssociationInfo.new(self)
62
64
  !association_info.limit? &&
63
65
  !association_info.offset? &&
64
66
  !association_info.group? &&
65
- !association_info.from?
67
+ !association_info.from? &&
68
+ !association_info.finder_sql? &&
69
+ # Joins not properly eager loaded - See https://github.com/salsify/goldiloader/issues/11
70
+ !association_info.joins? &&
71
+ # Unscope not properly eager loaded - https://github.com/salsify/goldiloader/issues/13
72
+ !association_info.unscope?
66
73
  end
67
74
 
68
- private
69
-
70
75
  def load_with_auto_include(load_method, *args)
71
76
  if loaded? && !stale_target?
72
77
  target
73
78
  elsif auto_include?
74
- Goldiloader::AssociationLoader.load(auto_include_context.model_registry, owner,
75
- auto_include_context.association_path)
79
+ Goldiloader::AssociationLoader.load(owner, reflection.name)
76
80
  target
77
81
  else
78
82
  send("#{load_method}_without_auto_include", *args)
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  module Goldiloader
2
4
  class AssociationInfo
3
5
 
@@ -5,6 +7,16 @@ module Goldiloader
5
7
  @association = association
6
8
  end
7
9
 
10
+ def unscope?
11
+ Goldiloader::Compatibility.unscope_query_method_enabled? &&
12
+ @association.association_scope.unscope_values.present?
13
+ end
14
+
15
+ def finder_sql?
16
+ Goldiloader::Compatibility.association_finder_sql_enabled? &&
17
+ @association.options[:finder_sql].present?
18
+ end
19
+
8
20
  if ActiveRecord::VERSION::MAJOR >= 4
9
21
  def read_only?
10
22
  @association.association_scope.readonly_value.present?
@@ -25,6 +37,23 @@ module Goldiloader
25
37
  def group?
26
38
  @association.association_scope.group_values.present?
27
39
  end
40
+
41
+ def joins?
42
+ # Yuck - Through associations will always have a join for *each* 'through' table
43
+ (@association.association_scope.joins_values.size - num_through_joins) > 0
44
+ end
45
+
46
+ private
47
+
48
+ def num_through_joins
49
+ association = @association
50
+ count = 0
51
+ while association.is_a?(ActiveRecord::Associations::ThroughAssociation)
52
+ count += 1
53
+ association = association.owner.association(association.through_reflection.name)
54
+ end
55
+ count
56
+ end
28
57
  else
29
58
  def read_only?
30
59
  @association.options[:readonly].present?
@@ -39,12 +68,17 @@ module Goldiloader
39
68
  end
40
69
 
41
70
  def from?
42
- @association.options[:finder_sql].present?
71
+ false
43
72
  end
44
73
 
45
74
  def group?
46
75
  @association.options[:group].present?
47
76
  end
77
+
78
+ def joins?
79
+ # Rails 3 didn't support joins for associations
80
+ false
81
+ end
48
82
  end
49
83
 
50
84
  end
@@ -4,19 +4,18 @@ module Goldiloader
4
4
  module AssociationLoader
5
5
  extend self
6
6
 
7
- def load(model_registry, model, association_path)
8
- *model_path, association_name = *association_path
9
- models = model_registry.peers(model, model_path).select do |peer|
7
+ def load(model, association_name)
8
+ models = model.auto_include_context.models.select do |peer|
10
9
  load?(peer, association_name)
11
10
  end
12
11
 
13
12
  eager_load(models, association_name)
14
13
 
15
- associated_models = associated_models(models, association_name)
16
14
  # Workaround Rails #15853 by setting models read only
17
- mark_read_only(associated_models) if read_only?(models, association_name)
18
- auto_include_context = Goldiloader::AutoIncludeContext.new(model_registry, association_path)
19
- auto_include_context.register_models(associated_models)
15
+ if read_only?(models, association_name)
16
+ associated_models = associated_models(models, association_name)
17
+ mark_read_only(associated_models)
18
+ end
20
19
  end
21
20
 
22
21
  private
@@ -1,15 +1,17 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Goldiloader
4
- class AutoIncludeContext < Struct.new(:model_registry, :association_path)
5
- def self.create_empty
6
- Goldiloader::AutoIncludeContext.new(Goldiloader::ModelRegistry.new, [])
4
+ class AutoIncludeContext
5
+ attr_reader :models
6
+
7
+ def initialize
8
+ @models = []
7
9
  end
8
10
 
9
11
  def register_models(models)
10
12
  Array.wrap(models).each do |model|
11
13
  model.auto_include_context = self
12
- model_registry.register(model, association_path)
14
+ self.models << model
13
15
  end
14
16
  self
15
17
  end
@@ -0,0 +1,18 @@
1
+ # encoding: UTF-8
2
+
3
+ module Goldiloader
4
+ module Compatibility
5
+
6
+ def self.mass_assignment_security_enabled?
7
+ ::ActiveRecord::VERSION::MAJOR < 4 || defined?(::ActiveRecord::MassAssignmentSecurity)
8
+ end
9
+
10
+ def self.association_finder_sql_enabled?
11
+ ::Gem::Version.new(::ActiveRecord::VERSION::STRING) < ::Gem::Version.new('4.1')
12
+ end
13
+
14
+ def self.unscope_query_method_enabled?
15
+ ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new('4.1')
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Goldiloader
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
data/spec/db/schema.rb CHANGED
@@ -67,17 +67,27 @@ class Blog < ActiveRecord::Base
67
67
  has_many :grouped_posts, -> { group(:blog_id) }, class_name: 'Post'
68
68
  has_many :offset_posts, -> { offset(2) }, class_name: 'Post'
69
69
  has_many :from_posts, -> { from('(select distinct blog_id from posts) as posts') }, class_name: 'Post'
70
+
71
+ has_many :posts_ordered_by_author, -> { joins(:author).order('users.name') }, class_name: 'Post'
72
+
73
+ has_many :authors_with_join, -> { joins(:address).order('addresses.city') }, through: :posts, source: :author
70
74
  else
71
75
  has_many :read_only_posts, readonly: true, class_name: 'Post'
72
76
  has_many :limited_posts, limit: 2, class_name: 'Post'
73
77
  has_many :grouped_posts, group: :blog_id, class_name: 'Post'
74
78
  has_many :offset_posts, offset: 2, class_name: 'Post'
75
- has_many :from_posts, finder_sql: Proc.new { "select distinct blog_id from posts where blog_id = #{self.id}" },
79
+
80
+ has_many :posts_ordered_by_author, include: :author, order: 'users.name', class_name: 'Post'
81
+ end
82
+
83
+ if Goldiloader::Compatibility.association_finder_sql_enabled?
84
+ has_many :finder_sql_posts, finder_sql: Proc.new { "select distinct blog_id from posts where blog_id = #{self.id}" },
76
85
  class_name: 'Post'
77
86
  end
78
87
 
79
88
  has_many :posts_overridden, class_name: 'Post'
80
89
  has_many :authors, through: :posts
90
+ has_many :addresses, through: :authors
81
91
 
82
92
  if Goldiloader::Compatibility.mass_assignment_security_enabled?
83
93
  attr_accessible :name
@@ -118,6 +128,10 @@ class User < ActiveRecord::Base
118
128
  has_one :address
119
129
  has_one :address_without_auto_include, auto_include: false, class_name: 'Address'
120
130
 
131
+ if Goldiloader::Compatibility.unscope_query_method_enabled?
132
+ has_one :scoped_address_with_default_scope_remove, -> { unscope(where: :city) }, class_name: 'ScopedAddress'
133
+ end
134
+
121
135
  if Goldiloader::Compatibility.mass_assignment_security_enabled?
122
136
  attr_accessible :name
123
137
  end
@@ -131,6 +145,16 @@ class Address < ActiveRecord::Base
131
145
  end
132
146
  end
133
147
 
148
+ class ScopedAddress < ActiveRecord::Base
149
+ self.table_name = 'addresses'
150
+ default_scope { where(city: ['Philadelphia'])}
151
+ belongs_to :user
152
+
153
+ if Goldiloader::Compatibility.mass_assignment_security_enabled?
154
+ attr_accessible :city
155
+ end
156
+ end
157
+
134
158
  class Group < ActiveRecord::Base
135
159
  has_many :tags, as: :owner
136
160
 
@@ -150,6 +150,19 @@ describe Goldiloader do
150
150
  expect(User).to have_received(:find_by_sql).once
151
151
  end
152
152
 
153
+ it "auto eager loads nested has_many through associations" do
154
+ blogs = Blog.order(:name).to_a
155
+ blogs.first.addresses.to_a
156
+
157
+ blogs.each do |blog|
158
+ expect(blog.association(:addresses)).to be_loaded
159
+ end
160
+
161
+ expect(blogs.first.addresses).to match_array([author1, author2].map(&:address))
162
+ expect(blogs.second.addresses).to match_array([author3, author1].map(&:address))
163
+ expect(Address).to have_received(:find_by_sql).once
164
+ end
165
+
153
166
  it "auto eager loads associations when the model is loaded via find" do
154
167
  blog = Blog.find(blog1.id)
155
168
  blog.posts.to_a.first.author
@@ -235,8 +248,8 @@ describe Goldiloader do
235
248
  let(:blogs) { Blog.order(:name).to_a }
236
249
 
237
250
  before do
238
- blog1.posts.create!(title: 'blog1-post3')
239
- blog2.posts.create!(title: 'blog2-post3')
251
+ blog1.posts.create!(title: 'blog1-post3', author: author1)
252
+ blog2.posts.create!(title: 'blog2-post3', author: author1)
240
253
  end
241
254
 
242
255
  shared_examples "it doesn't auto eager the association" do |association_name|
@@ -283,16 +296,81 @@ describe Goldiloader do
283
296
  it_behaves_like "it doesn't auto eager the association", :offset_posts
284
297
  end
285
298
 
286
- context "associations with an overridden from" do
287
- before do
288
- blogs.first.from_posts.to_a
299
+ if ActiveRecord::VERSION::MAJOR >= 4
300
+ context "associations with an overridden from" do
301
+ before do
302
+ blogs.first.from_posts.to_a
303
+ end
304
+
305
+ it "applies the from correctly" do
306
+ expect(blogs.first.from_posts.to_a.size).to eq 1
307
+ end
308
+
309
+ it_behaves_like "it doesn't auto eager the association", :from_posts
289
310
  end
311
+ end
312
+
313
+ if Goldiloader::Compatibility.association_finder_sql_enabled?
314
+ context "associations with finder_sql" do
315
+ before do
316
+ blogs.first.finder_sql_posts.to_a
317
+ end
318
+
319
+ it "applies the finder_sql correctly" do
320
+ expect(blogs.first.finder_sql_posts.to_a.size).to eq 1
321
+ end
290
322
 
291
- it "applies the from correctly" do
292
- expect(blogs.first.from_posts.to_a.size).to eq 1
323
+ it_behaves_like "it doesn't auto eager the association", :finder_sql_posts
293
324
  end
325
+ end
294
326
 
295
- it_behaves_like "it doesn't auto eager the association", :from_posts
327
+ if ActiveRecord::VERSION::MAJOR >= 4
328
+ context "associations with a join" do
329
+ before do
330
+ blogs.first.posts_ordered_by_author.to_a
331
+ end
332
+
333
+ it "applies the join correctly" do
334
+ expect(blogs.first.posts_ordered_by_author.to_a.size).to eq 3
335
+ end
336
+
337
+ it_behaves_like "it doesn't auto eager the association", :posts_ordered_by_author
338
+ end
339
+
340
+ context "associations with a join in a has_many_through" do
341
+ before do
342
+ blogs.first.authors_with_join.to_a
343
+ end
344
+
345
+ it "applies the join correctly" do
346
+ expect(blogs.first.authors_with_join.to_a.size).to eq 3
347
+ end
348
+
349
+ it_behaves_like "it doesn't auto eager the association", :authors_with_join
350
+ end
351
+ end
352
+
353
+ if Goldiloader::Compatibility.unscope_query_method_enabled?
354
+ context "associations with an unscoped" do
355
+ let(:authors) { User.order(:id).to_a }
356
+
357
+ before do
358
+ author1.address.update_attributes!(city: 'Boston')
359
+ author2.address.update_attributes!(city: 'Philadelphia')
360
+ author3.address.update_attributes!(city: 'Philadelphia')
361
+ authors.first.scoped_address_with_default_scope_remove
362
+ end
363
+
364
+ it "applies the unscope correctly" do
365
+ expect(authors.first.scoped_address_with_default_scope_remove).to be_present
366
+ end
367
+
368
+ it "doesn't auto eager the association" do
369
+ authors.drop(1).each do |author|
370
+ expect(author.association(:scoped_address_with_default_scope_remove)).to_not be_loaded
371
+ end
372
+ end
373
+ end
296
374
  end
297
375
  end
298
376
 
data/spec/spec_helper.rb CHANGED
@@ -16,9 +16,6 @@ require 'database_cleaner'
16
16
  require 'goldiloader'
17
17
  require 'yaml'
18
18
 
19
- spec_dir = File.dirname(__FILE__)
20
- Dir["#{spec_dir}/support/**/*.rb"].sort.each { |f| require f }
21
-
22
19
  FileUtils.makedirs('log')
23
20
 
24
21
  ActiveRecord::Base.logger = Logger.new('log/test.log')
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: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Turkel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-23 00:00:00.000000000 Z
11
+ date: 2014-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -17,9 +17,9 @@ dependencies:
17
17
  - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.2'
20
- - - <=
20
+ - - <
21
21
  - !ruby/object:Gem::Version
22
- version: '4.1'
22
+ version: '4.2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +27,9 @@ dependencies:
27
27
  - - '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '3.2'
30
- - - <=
30
+ - - <
31
31
  - !ruby/object:Gem::Version
32
- version: '4.1'
32
+ version: '4.2'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: activesupport
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -37,9 +37,9 @@ dependencies:
37
37
  - - '>='
38
38
  - !ruby/object:Gem::Version
39
39
  version: '3.2'
40
- - - <=
40
+ - - <
41
41
  - !ruby/object:Gem::Version
42
- version: '4.1'
42
+ version: '4.2'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -47,9 +47,9 @@ dependencies:
47
47
  - - '>='
48
48
  - !ruby/object:Gem::Version
49
49
  version: '3.2'
50
- - - <=
50
+ - - <
51
51
  - !ruby/object:Gem::Version
52
- version: '4.1'
52
+ version: '4.2'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: coveralls
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -156,13 +156,12 @@ files:
156
156
  - lib/goldiloader/association_loader.rb
157
157
  - lib/goldiloader/association_options.rb
158
158
  - lib/goldiloader/auto_include_context.rb
159
- - lib/goldiloader/model_registry.rb
159
+ - lib/goldiloader/compatibility.rb
160
160
  - lib/goldiloader/version.rb
161
161
  - spec/db/database.yml
162
162
  - spec/db/schema.rb
163
163
  - spec/goldiloader/goldiloader_spec.rb
164
164
  - spec/spec_helper.rb
165
- - spec/support/compatibility.rb
166
165
  homepage: https://github.com/salsify/goldiloader
167
166
  licenses:
168
167
  - MIT
@@ -192,4 +191,3 @@ test_files:
192
191
  - spec/db/schema.rb
193
192
  - spec/goldiloader/goldiloader_spec.rb
194
193
  - spec/spec_helper.rb
195
- - spec/support/compatibility.rb
@@ -1,26 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- module Goldiloader
4
- class ModelRegistry
5
- def initialize
6
- @registry = {}
7
- end
8
-
9
- def register(record, association_path)
10
- key = registry_key(record, association_path)
11
- @registry[key] ||= []
12
- @registry[key] << record
13
- end
14
-
15
- # Returns all models with the same base class loaded from the same association path
16
- def peers(record, association_path)
17
- @registry.fetch(registry_key(record, association_path), [])
18
- end
19
-
20
- private
21
-
22
- def registry_key(record, association_path)
23
- [record.class.base_class, association_path]
24
- end
25
- end
26
- end
@@ -1,14 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require 'active_support/version'
4
- require 'active_record/version'
5
-
6
- module Goldiloader
7
- module Compatibility
8
-
9
- def self.mass_assignment_security_enabled?
10
- ::ActiveRecord::VERSION::MAJOR < 4 || defined?(::ActiveRecord::MassAssignmentSecurity)
11
- end
12
-
13
- end
14
- end