aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb35
-rw-r--r--activerecord/lib/active_record/reflection.rb3
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb9
-rw-r--r--activerecord/test/fixtures/taggings.yml10
-rw-r--r--activerecord/test/models/post.rb1
-rw-r--r--activerecord/test/models/rating.rb1
6 files changed, 46 insertions, 13 deletions
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index ed24373cba..8e9259a28d 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -10,8 +10,20 @@ module ActiveRecord
protected
+ # We merge in these scopes for two reasons:
+ #
+ # 1. To get the scope_for_create on through reflection when building associated objects
+ # 2. To get the type conditions for any STI classes in the chain
+ #
+ # TODO: Don't actually do this. Getting the creation attributes for a non-nested through
+ # is a special case. The rest (STI conditions) should be handled by the reflection
+ # itself.
def target_scope
- super.merge(through_reflection.klass.scoped)
+ scope = super
+ through_reflection_chain[1..-1].each do |reflection|
+ scope = scope.merge(reflection.klass.scoped)
+ end
+ scope
end
def association_scope
@@ -227,21 +239,18 @@ module ActiveRecord
def reflection_conditions(index)
reflection = through_reflection_chain[index]
- conditions = through_conditions[index].dup
-
- # TODO: maybe this should go in Reflection#through_conditions directly?
- unless reflection.klass.descends_from_active_record?
- conditions << reflection.klass.send(:type_condition)
- end
+ conditions = through_conditions[index]
unless conditions.empty?
- conditions.map! do |condition|
- condition = reflection.klass.send(:sanitize_sql, interpolate(condition), reflection.table_name)
- condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
- condition
- end
+ Arel::Nodes::And.new(process_conditions(conditions, reflection))
+ end
+ end
- Arel::Nodes::And.new(conditions)
+ def process_conditions(conditions, reflection)
+ conditions.map do |condition|
+ condition = reflection.klass.send(:sanitize_sql, interpolate(condition), reflection.table_name)
+ condition = Arel.sql(condition) unless condition.is_a?(Arel::Node)
+ condition
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index e3e2cac042..7ae9bfc928 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -416,6 +416,9 @@ module ActiveRecord
else
# If the source reflection does not go through another reflection, then we can get
# to this reflection directly, and so start the chain here
+ #
+ # It is important to use self, rather than the source_reflection, because self
+ # may has a :source_type option which needs to be used.
chain = [self]
end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index a4ac69782a..0dd407f342 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -425,6 +425,15 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert !scope.where("comments.type" => "SubSpecialComment").empty?
end
+ def test_has_many_through_with_sti_on_nested_through_reflection
+ taggings = posts(:sti_comments).special_comments_ratings_taggings
+ assert_equal [taggings(:special_comment_rating)], taggings
+
+ scope = Post.joins(:special_comments_ratings_taggings).where(:id => posts(:sti_comments).id)
+ assert scope.where("comments.type" => "Comment").empty?
+ assert !scope.where("comments.type" => "SpecialComment").empty?
+ end
+
def test_nested_has_many_through_writers_should_raise_error
david = authors(:david)
subscriber = subscribers(:first)
diff --git a/activerecord/test/fixtures/taggings.yml b/activerecord/test/fixtures/taggings.yml
index a337cce019..d339c12b25 100644
--- a/activerecord/test/fixtures/taggings.yml
+++ b/activerecord/test/fixtures/taggings.yml
@@ -66,3 +66,13 @@ other_by_mary_blue:
taggable_id: 11
taggable_type: Post
comment: first
+
+special_comment_rating:
+ id: 12
+ taggable_id: 2
+ taggable_type: Rating
+
+normal_comment_rating:
+ id: 13
+ taggable_id: 1
+ taggable_type: Rating
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index b39325f949..c3843fd264 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -50,6 +50,7 @@ class Post < ActiveRecord::Base
has_many :nonexistant_comments, :class_name => 'Comment', :conditions => 'comments.id < 0'
has_many :special_comments_ratings, :through => :special_comments, :source => :ratings
+ has_many :special_comments_ratings_taggings, :through => :special_comments_ratings, :source => :taggings
has_and_belongs_to_many :categories
has_and_belongs_to_many :special_categories, :join_table => "categories_posts", :association_foreign_key => 'category_id'
diff --git a/activerecord/test/models/rating.rb b/activerecord/test/models/rating.rb
index 12c4b5affa..25a52c4ad7 100644
--- a/activerecord/test/models/rating.rb
+++ b/activerecord/test/models/rating.rb
@@ -1,3 +1,4 @@
class Rating < ActiveRecord::Base
belongs_to :comment
+ has_many :taggings, :as => :taggable
end