aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authoryui-knk <spiketeika@gmail.com>2016-02-02 20:57:55 +0900
committeryui-knk <spiketeika@gmail.com>2017-08-22 18:51:43 +0900
commit30ef715d40cd49d017524824bfbb92e4bd4ab6a4 (patch)
tree56963d44c52c5624b35b936b0aff1421a9d7157d /activerecord
parent1fe41329c6ffcbb52c9762d28d780e838584e63d (diff)
downloadrails-30ef715d40cd49d017524824bfbb92e4bd4ab6a4.tar.gz
rails-30ef715d40cd49d017524824bfbb92e4bd4ab6a4.tar.bz2
rails-30ef715d40cd49d017524824bfbb92e4bd4ab6a4.zip
Automatically guess the inverse associations for STI
ActiveRecord associations automatically guess the inverse associations. But this feature does not work correctly on assoctions for STI. For example, before this commit ``` class Post < ActiveRecord::Base belongs_to :author end class SpecialPost < Post; end class Author < ActiveRecord::Base has_many :posts has_many :special_posts end ``` `author.posts.first.author` works correctly, but `author.special_posts.first.author` does not work correctly.
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/reflection.rb2
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb39
3 files changed, 43 insertions, 2 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index b39d28ca31..4a840a57b5 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Automatically guess the inverse associations for STI.
+
+ *Yuichiro Kaneko*
+
* Ensure `sum` honors `distinct` on `has_many :through` associations
Fixes #16791.
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index b847933b2e..e35049bb41 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -657,7 +657,7 @@ module ActiveRecord
# from calling +klass+, +reflection+ will already be set to false.
def valid_inverse_reflection?(reflection)
reflection &&
- klass.name == reflection.active_record.name &&
+ klass <= reflection.active_record &&
can_find_inverse_of_automatically?(reflection)
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 536077fb92..86a034b429 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -18,6 +18,8 @@ require "models/admin/user"
require "models/developer"
require "models/company"
require "models/project"
+require "models/author"
+require "models/post"
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars
@@ -60,6 +62,20 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
assert_equal rating_reflection, comment_reflection.inverse_of, "The Comment reflection's inverse should be the Rating reflection"
end
+ def test_has_many_and_belongs_to_should_find_inverse_automatically_for_sti
+ author_reflection = Author.reflect_on_association(:posts)
+ author_child_reflection = Author.reflect_on_association(:special_posts)
+ post_reflection = Post.reflect_on_association(:author)
+
+ assert_respond_to author_reflection, :has_inverse?
+ assert author_reflection.has_inverse?, "The Author reflection should have an inverse"
+ assert_equal post_reflection, author_reflection.inverse_of, "The Author reflection's inverse should be the Post reflection"
+
+ assert_respond_to author_child_reflection, :has_inverse?
+ assert author_child_reflection.has_inverse?, "The Author reflection should have an inverse"
+ assert_equal post_reflection, author_child_reflection.inverse_of, "The Author reflection's inverse should be the Post reflection"
+ end
+
def test_has_one_and_belongs_to_automatic_inverse_shares_objects
car = Car.first
bulb = Bulb.create!(car: car)
@@ -267,7 +283,7 @@ class InverseHasOneTests < ActiveRecord::TestCase
end
class InverseHasManyTests < ActiveRecord::TestCase
- fixtures :men, :interests
+ fixtures :men, :interests, :posts, :authors
def test_parent_instance_should_be_shared_with_every_child_on_find
m = men(:gordon)
@@ -281,6 +297,27 @@ class InverseHasManyTests < ActiveRecord::TestCase
end
end
+ def test_parent_instance_should_be_shared_with_every_child_on_find_for_sti
+ a = authors(:david)
+ ps = a.posts
+ ps.each do |p|
+ assert_equal a.name, p.author.name, "Name of man should be the same before changes to parent instance"
+ a.name = "Bongo"
+ assert_equal a.name, p.author.name, "Name of man should be the same after changes to parent instance"
+ p.author.name = "Mungo"
+ assert_equal a.name, p.author.name, "Name of man should be the same after changes to child-owned instance"
+ end
+
+ sps = a.special_posts
+ sps.each do |sp|
+ assert_equal a.name, sp.author.name, "Name of man should be the same before changes to parent instance"
+ a.name = "Bongo"
+ assert_equal a.name, sp.author.name, "Name of man should be the same after changes to parent instance"
+ sp.author.name = "Mungo"
+ assert_equal a.name, sp.author.name, "Name of man should be the same after changes to child-owned instance"
+ end
+ end
+
def test_parent_instance_should_be_shared_with_eager_loaded_children
m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests).first
is = m.interests