Permalink
Browse files

Add a test for STI on the through where the through is nested, and ch…

…ange the code which support this
  • Loading branch information...
1 parent 4206eff commit ddf83d14f1c7ddae07a285a8ad7c45f652edc843 @jonleighton jonleighton committed Mar 5, 2011
@@ -10,8 +10,20 @@ module ThroughAssociation #:nodoc:
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 @@ def inner_join(table, left_column, right_column, *conditions)
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
@@ -416,6 +416,9 @@ def through_reflection_chain
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
@@ -425,6 +425,15 @@ def test_has_many_through_with_sti_on_through_reflection
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)
@@ -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
@@ -50,6 +50,7 @@ def find_most_recent
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'
@@ -1,3 +1,4 @@
class Rating < ActiveRecord::Base
belongs_to :comment
+ has_many :taggings, :as => :taggable
end

0 comments on commit ddf83d1

Please sign in to comment.