diff options
-rw-r--r-- | activerecord/lib/active_record/associations/through_association.rb | 35 | ||||
-rw-r--r-- | activerecord/lib/active_record/reflection.rb | 3 | ||||
-rw-r--r-- | activerecord/test/cases/associations/nested_through_associations_test.rb | 9 | ||||
-rw-r--r-- | activerecord/test/fixtures/taggings.yml | 10 | ||||
-rw-r--r-- | activerecord/test/models/post.rb | 1 | ||||
-rw-r--r-- | activerecord/test/models/rating.rb | 1 |
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 |