aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Kemper <jeremy@bitsweat.net>2013-03-19 14:00:04 -0700
committerJeremy Kemper <jeremy@bitsweat.net>2013-03-19 14:00:04 -0700
commit0fe4ae93b4f902ac5757304e015c415bf986a0d0 (patch)
tree31b0d6db4f658e1bf92d944544c7e8ec189ce46e
parent67a2c731c322c290d7ed44726a33d50489d1ed95 (diff)
parent840ca09a1151fafe1cb3299f12437378d25f2ce5 (diff)
downloadrails-0fe4ae93b4f902ac5757304e015c415bf986a0d0.tar.gz
rails-0fe4ae93b4f902ac5757304e015c415bf986a0d0.tar.bz2
rails-0fe4ae93b4f902ac5757304e015c415bf986a0d0.zip
Merge pull request #9529 from wangjohn/find_on_inverse_of_bugfix_9470
Find() on an association with an inverse will now scan existing in-memory records
-rw-r--r--activerecord/CHANGELOG.md10
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb5
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb25
3 files changed, 38 insertions, 2 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index e5ab6bac58..79247e3028 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,15 @@
## Rails 4.0.0 (unreleased) ##
+* If inverse_of is true on an association, then when one calls +find()+ on
+ the association, ActiveRecord will first look through the in-memory objects
+ in the association for a particular id. Then, it will go to the DB if it
+ is not found. This is accomplished by calling +find_by_scan+ in
+ collection associations whenever +options[:inverse_of]+ is not nil.
+
+ Fixes #9470.
+
+ *John Wang*
+
* `rake db:create` does not change permissions of the MySQL root user.
Fixes #8079.
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 906560bd44..73b4a187c8 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -79,7 +79,7 @@ module ActiveRecord
if block_given?
load_target.find(*args) { |*block_args| yield(*block_args) }
else
- if options[:finder_sql]
+ if options[:finder_sql] || options[:inverse_of]
find_by_scan(*args)
else
scope.find(*args)
@@ -567,7 +567,8 @@ module ActiveRecord
end
end
- # If using a custom finder_sql, #find scans the entire collection.
+ # If using a custom finder_sql or if the :inverse_of option has been
+ # specified, then #find scans the entire collection.
def find_by_scan(*args)
expects_array = args.first.kind_of?(Array)
ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 8c9b4fb921..c8cad84013 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -278,6 +278,31 @@ class InverseHasManyTests < ActiveRecord::TestCase
assert interests[1].man.equal? man
end
+ def test_parent_instance_should_find_child_instance_using_child_instance_id
+ man = Man.create!
+ interest = Interest.create!
+ man.interests = [interest]
+
+ assert interest.equal?(man.interests.first), "The inverse association should use the interest already created and held in memory"
+ assert interest.equal?(man.interests.find(interest.id)), "The inverse association should use the interest already created and held in memory"
+ assert man.equal?(man.interests.first.man), "Two inversion should lead back to the same object that was originally held"
+ assert man.equal?(man.interests.find(interest.id).man), "Two inversions should lead back to the same object that was originally held"
+ end
+
+ def test_parent_instance_should_find_child_instance_using_child_instance_id_when_created
+ man = Man.create!
+ interest = Interest.create!(man: man)
+
+ assert man.equal?(man.interests.first.man), "Two inverses should lead back to the same object that was originally held"
+ assert man.equal?(man.interests.find(interest.id).man), "Two inversions should lead back to the same object that was originally held"
+
+ assert_equal man.name, man.interests.find(interest.id).man.name, "The name of the man should match before the name is changed"
+ man.name = "Ben Bitdiddle"
+ assert_equal man.name, man.interests.find(interest.id).man.name, "The name of the man should match after the parent name is changed"
+ man.interests.find(interest.id).man.name = "Alyssa P. Hacker"
+ assert_equal man.name, man.interests.find(interest.id).man.name, "The name of the man should match after the child name is changed"
+ end
+
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.first.secret_interests }
end