Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Set inverse associations when using scopes

Currently using scopes through a has_many association will not set
the inverse association, e.g:

  product = Product.first
  product.variants.enabled.first.product.equal? product
  => false

This commit fixes it by copying the association into the scope and
then attempting to set the inverse association when the records are
loaded.
  • Loading branch information...
commit 9f607fb4ddb3789b0859e05bf7810354a867bfc2 1 parent ea84b0c
Andrew White pixeltrix authored
1  activerecord/lib/active_record/associations/association_scope.rb
@@ -15,6 +15,7 @@ def initialize(association)
15 15
16 16 def scope
17 17 scope = klass.unscoped
  18 + scope.association = @association
18 19 scope.merge! eval_scope(klass, reflection.scope) if reflection.scope
19 20 add_constraints(scope)
20 21 end
4 activerecord/lib/active_record/associations/collection_proxy.rb
@@ -35,8 +35,8 @@ module Associations
35 35 # instantiation of the actual post records.
36 36 class CollectionProxy < Relation
37 37 def initialize(association) #:nodoc:
38   - @association = association
39 38 super association.klass, association.klass.arel_table
  39 + @association = association
40 40 merge! association.scope
41 41 end
42 42
@@ -896,7 +896,7 @@ def ==(other)
896 896 end
897 897
898 898 # Returns a new array of objects from the collection. If the collection
899   - # hasn't been loaded, it fetches the records from the database.
  899 + # hasn't been loaded, it fetches the records from the database.
900 900 #
901 901 # class Person < ActiveRecord::Base
902 902 # has_many :pets
7 activerecord/lib/active_record/relation.rb
@@ -17,7 +17,7 @@ class Relation
17 17 include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
18 18
19 19 attr_reader :table, :klass, :loaded
20   - attr_accessor :default_scoped
  20 + attr_accessor :default_scoped, :association
21 21 alias :model :klass
22 22 alias :loaded? :loaded
23 23 alias :default_scoped? :default_scoped
@@ -29,6 +29,7 @@ def initialize(klass, table, values = {})
29 29 @implicit_readonly = nil
30 30 @loaded = false
31 31 @default_scoped = false
  32 + @association = nil
32 33 end
33 34
34 35 def insert(values)
@@ -572,6 +573,10 @@ def exec_queries
572 573 @records = default_scoped.to_a
573 574 end
574 575
  576 + if association
  577 + @records.each { |record| association.set_inverse_instance(record) }
  578 + end
  579 +
575 580 @loaded = true
576 581 @records
577 582 end
4 activerecord/lib/active_record/relation/merger.rb
@@ -35,6 +35,10 @@ def initialize(relation, other)
35 35 other = other.with_default_scope
36 36 end
37 37
  38 + if other.association
  39 + relation.association = other.association
  40 + end
  41 +
38 42 @relation = relation
39 43 @values = other.values
40 44 end
20 activerecord/test/cases/associations/inverse_associations_test.rb
@@ -261,8 +261,24 @@ def test_parent_instance_should_be_shared_with_replaced_via_accessor_children
261 261
262 262 def test_parent_instance_should_be_shared_with_first_and_last_child
263 263 man = Man.first
264   - assert man.interests.first.man.equal? man
265   - assert man.interests.last.man.equal? man
  264 + assert man.interests.first.man.equal?(man), "Man should be the same instance when using association.first"
  265 + assert man.interests.last.man.equal?(man), "Man should be the same instance when using association.last"
  266 + end
  267 +
  268 + def test_parent_instance_should_be_shared_with_all_children
  269 + man = Man.first
  270 + assert man.interests.to_a.all?{ |interest| interest.man.equal?(man) }, "Man should be the same instance when using association.to_a"
  271 + end
  272 +
  273 + def test_parent_instance_should_be_shared_with_first_and_last_child_when_using_scope
  274 + man = Man.first
  275 + assert man.interests.by_topic.first.man.equal?(man), "Man should be the same instance when using association.scope.first"
  276 + assert man.interests.by_topic.last.man.equal?(man), "Man should be the same instance when using association.scope.last"
  277 + end
  278 +
  279 + def test_parent_instance_should_be_shared_with_all_children_when_using_scope
  280 + man = Man.first
  281 + assert man.interests.by_topic.to_a.all?{ |interest| interest.man.equal?(man) }, "Man should be the same instance when using association.scope.to_a"
266 282 end
267 283
268 284 def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
4 activerecord/test/models/interest.rb
@@ -2,4 +2,8 @@ class Interest < ActiveRecord::Base
2 2 belongs_to :man, :inverse_of => :interests
3 3 belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_interests
4 4 belongs_to :zine, :inverse_of => :interests
  5 +
  6 + def self.by_topic
  7 + order(arel_table[:topic])
  8 + end
5 9 end

0 comments on commit 9f607fb

Please sign in to comment.
Something went wrong with that request. Please try again.