aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2010-10-19 00:27:40 +0100
committerJon Leighton <j@jonathanleighton.com>2010-10-19 00:27:40 +0100
commit9ec07348749675110843c44f680da79223218db2 (patch)
treee4a356d2b6ee95232097fac21df5a04118dcc014
parent78b8c51cb3b0c629152f3bbaf6d8bcf988cc936e (diff)
downloadrails-9ec07348749675110843c44f680da79223218db2.tar.gz
rails-9ec07348749675110843c44f680da79223218db2.tar.bz2
rails-9ec07348749675110843c44f680da79223218db2.zip
Properly support conditions on any of the reflections involved in a nested through association
-rw-r--r--activerecord/lib/active_record/associations.rb15
-rw-r--r--activerecord/lib/active_record/associations/through_association_scope.rb127
-rw-r--r--activerecord/lib/active_record/reflection.rb53
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb6
-rw-r--r--activerecord/test/cases/associations/eager_test.rb4
-rw-r--r--activerecord/test/cases/associations/nested_has_many_through_associations_test.rb40
-rw-r--r--activerecord/test/cases/batches_test.rb2
-rw-r--r--activerecord/test/cases/finder_test.rb6
-rw-r--r--activerecord/test/cases/relations_test.rb18
-rw-r--r--activerecord/test/fixtures/posts.yml14
-rw-r--r--activerecord/test/fixtures/taggings.yml28
-rw-r--r--activerecord/test/fixtures/tags.yml4
-rw-r--r--activerecord/test/models/author.rb7
-rw-r--r--activerecord/test/models/post.rb5
-rw-r--r--activerecord/test/models/tagging.rb3
-rw-r--r--activerecord/test/schema/schema.rb1
16 files changed, 235 insertions, 98 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 1111033435..75e5eb8ee4 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -2164,8 +2164,10 @@ module ActiveRecord
chain = through_reflection_chain.reverse
foreign_table = parent_table
+ index = 0
- chain.zip(@tables).each do |reflection, table|
+ chain.each do |reflection|
+ table = @tables[index]
conditions = []
if reflection.source_reflection.nil?
@@ -2234,13 +2236,14 @@ module ActiveRecord
conditions << table[key].eq(foreign_table[foreign_key])
- conditions << reflection_conditions(reflection, table)
+ conditions << reflection_conditions(index, table)
conditions << sti_conditions(reflection, table)
- relation = relation.join(table, join_type).on(*conditions.compact)
+ relation = relation.join(table, join_type).on(*conditions.flatten.compact)
# The current table in this iteration becomes the foreign table in the next
foreign_table = table
+ index += 1
end
relation
@@ -2325,10 +2328,10 @@ module ActiveRecord
@tables
end
- def reflection_conditions(reflection, table)
- if reflection.options[:conditions]
+ def reflection_conditions(index, table)
+ @reflection.through_conditions.reverse[index].map do |condition|
Arel.sql(interpolate_sql(sanitize_sql(
- reflection.options[:conditions],
+ condition,
table.table_alias || table.name
)))
end
diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb
index 261b4037a3..feb0a93360 100644
--- a/activerecord/lib/active_record/associations/through_association_scope.rb
+++ b/activerecord/lib/active_record/associations/through_association_scope.rb
@@ -24,7 +24,6 @@ module ActiveRecord
end
# Build SQL conditions from attributes, qualified by table name.
- # TODO: Conditions on joins
def construct_conditions
reflection = @reflection.through_reflection_chain.last
@@ -34,11 +33,12 @@ module ActiveRecord
table_alias = table_aliases[reflection]
end
- conditions = construct_quoted_owner_attributes(reflection).map do |attr, value|
+ parts = construct_quoted_owner_attributes(reflection).map do |attr, value|
"#{table_alias}.#{attr} = #{value}"
end
- conditions << sql_conditions if sql_conditions
- "(" + conditions.join(') AND (') + ")"
+ parts += reflection_conditions(0)
+
+ "(" + parts.join(') AND (') + ")"
end
# Associate attributes pointing to owner, quoted.
@@ -55,23 +55,21 @@ module ActiveRecord
end
end
- def construct_from
- @reflection.table_name
- end
-
def construct_select(custom_select = nil)
distinct = "DISTINCT " if @reflection.options[:uniq]
selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*"
end
def construct_joins(custom_joins = nil)
- # p @reflection.through_reflection_chain
+ # TODO: Remove this at the end
+ #p @reflection.through_reflection_chain
+ #p @reflection.through_conditions
"#{construct_through_joins} #{@reflection.options[:joins]} #{custom_joins}"
end
def construct_through_joins
- joins = []
+ joins, right_index = [], 1
# Iterate over each pair in the through reflection chain, joining them together
@reflection.through_reflection_chain.each_cons(2) do |left, right|
@@ -86,20 +84,23 @@ module ActiveRecord
joins << inner_join_sql(
right_table_and_alias,
table_aliases[left], left.klass.primary_key,
- table_aliases[right], left.primary_key_name
+ table_aliases[right], left.primary_key_name,
+ reflection_conditions(right_index)
)
when :has_many, :has_one
joins << inner_join_sql(
right_table_and_alias,
table_aliases[left], left.primary_key_name,
table_aliases[right], right.klass.primary_key,
- polymorphic_conditions(left, left)
+ polymorphic_conditions(left, left),
+ reflection_conditions(right_index)
)
when :has_and_belongs_to_many
joins << inner_join_sql(
right_table_and_alias,
table_aliases[left].first, left.primary_key_name,
- table_aliases[right], right.klass.primary_key
+ table_aliases[right], right.klass.primary_key,
+ reflection_conditions(right_index)
)
end
else
@@ -109,7 +110,8 @@ module ActiveRecord
right_table_and_alias,
table_aliases[left], left.klass.primary_key,
table_aliases[right], left.source_reflection.primary_key_name,
- source_type_conditions(left)
+ source_type_conditions(left),
+ reflection_conditions(right_index)
)
when :has_many, :has_one
if right.macro == :has_and_belongs_to_many
@@ -123,7 +125,8 @@ module ActiveRecord
right_table_and_alias,
table_aliases[left], left.source_reflection.primary_key_name,
right_table, right.klass.primary_key,
- polymorphic_conditions(left, left.source_reflection)
+ polymorphic_conditions(left, left.source_reflection),
+ reflection_conditions(right_index)
)
if right.macro == :has_and_belongs_to_many
@@ -151,10 +154,13 @@ module ActiveRecord
joins << inner_join_sql(
right_table_and_alias,
join_table, left.source_reflection.primary_key_name,
- table_aliases[right], right.klass.primary_key
+ table_aliases[right], right.klass.primary_key,
+ reflection_conditions(right_index)
)
end
end
+
+ right_index += 1
end
joins.join(" ")
@@ -206,18 +212,45 @@ module ActiveRecord
"#{table_name} #{table_alias if table_alias != table_name}".strip
end
- def inner_join_sql(table, on_left_table, on_left_key, on_right_table, on_right_key, conds = nil)
- "INNER JOIN %s ON %s.%s = %s.%s %s" % [
- table,
- on_left_table, on_left_key,
- on_right_table, on_right_key,
- conds
- ]
+ def inner_join_sql(table, on_left_table, on_left_key, on_right_table, on_right_key, *conditions)
+ conditions << "#{on_left_table}.#{on_left_key} = #{on_right_table}.#{on_right_key}"
+ conditions = conditions.flatten.compact
+ conditions = conditions.map { |sql| "(#{sql})" } * ' AND '
+
+ "INNER JOIN #{table} ON #{conditions}"
+ end
+
+ def reflection_conditions(index)
+ reflection = @reflection.through_reflection_chain[index]
+ reflection_conditions = @reflection.through_conditions[index]
+
+ conditions = []
+
+ if reflection.options[:as].nil? && # reflection.klass is a Module if :as is used
+ reflection.klass.finder_needs_type_condition?
+ conditions << reflection.klass.send(:type_condition).to_sql
+ end
+
+ reflection_conditions.each do |condition|
+ sanitized_condition = reflection.klass.send(:sanitize_sql, condition)
+ interpolated_condition = interpolate_sql(sanitized_condition)
+
+ if condition.is_a?(Hash)
+ interpolated_condition.gsub!(
+ @reflection.quoted_table_name,
+ reflection.quoted_table_name
+ )
+ end
+
+ conditions << interpolated_condition
+ end
+
+ conditions
end
def polymorphic_conditions(reflection, polymorphic_reflection)
if polymorphic_reflection.options[:as]
- "AND %s.%s = %s" % [
+ "%s.%s = %s" % [
table_aliases[reflection], "#{polymorphic_reflection.options[:as]}_type",
@owner.class.quote_value(polymorphic_reflection.active_record.base_class.name)
]
@@ -226,7 +259,7 @@ module ActiveRecord
def source_type_conditions(reflection)
if reflection.options[:source_type]
- "AND %s.%s = %s" % [
+ "%s.%s = %s" % [
table_aliases[reflection.through_reflection],
reflection.source_reflection.options[:foreign_type].to_s,
@owner.class.quote_value(reflection.options[:source_type])
@@ -245,6 +278,8 @@ module ActiveRecord
end
# Construct attributes for :through pointing to owner and associate.
+ # This method is used when adding records to the association. Since this only makes sense for
+ # non-nested through associations, that's the only case we have to worry about here.
def construct_join_attributes(associate)
# TODO: revisit this to allow it for deletion, supposing dependent option is supported
raise ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) if [:has_one, :has_many].include?(@reflection.source_reflection.macro)
@@ -261,48 +296,6 @@ module ActiveRecord
join_attributes
end
-
- def conditions
- @conditions = build_conditions unless defined?(@conditions)
- @conditions
- end
-
- def build_conditions
- association_conditions = @reflection.options[:conditions]
- through_conditions = build_through_conditions
- source_conditions = @reflection.source_reflection.options[:conditions]
- uses_sti = !@reflection.through_reflection.klass.descends_from_active_record?
-
- if association_conditions || through_conditions || source_conditions || uses_sti
- all = []
-
- [association_conditions, source_conditions].each do |conditions|
- all << interpolate_sql(sanitize_sql(conditions)) if conditions
- end
-
- all << through_conditions if through_conditions
- all << build_sti_condition if uses_sti
-
- all.map { |sql| "(#{sql})" } * ' AND '
- end
- end
-
- def build_through_conditions
- conditions = @reflection.through_reflection.options[:conditions]
- if conditions.is_a?(Hash)
- interpolate_sql(@reflection.through_reflection.klass.send(:sanitize_sql, conditions)).gsub(
- @reflection.quoted_table_name,
- @reflection.through_reflection.quoted_table_name)
- elsif conditions
- interpolate_sql(sanitize_sql(conditions))
- end
- end
-
- def build_sti_condition
- @reflection.through_reflection.klass.send(:type_condition).to_sql
- end
-
- alias_method :sql_conditions, :conditions
def ensure_not_nested
if @reflection.nested?
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index ee63fcfce2..d8bd6c9873 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -253,6 +253,10 @@ module ActiveRecord
def through_reflection_chain
[self]
end
+
+ def through_conditions
+ [Array.wrap(options[:conditions])]
+ end
def through_reflection_primary_key_name
end
@@ -378,9 +382,9 @@ module ActiveRecord
# TODO: Documentation
def through_reflection_chain
@through_reflection_chain ||= begin
- if source_reflection.through_reflection
- # If the source reflection goes through another reflection, then the chain must start
- # by getting us to the source reflection.
+ if source_reflection.source_reflection
+ # If the source reflection has its own source reflection, then the chain must start
+ # by getting us to that source reflection.
chain = source_reflection.through_reflection_chain
else
# If the source reflection does not go through another reflection, then we can get
@@ -396,6 +400,49 @@ module ActiveRecord
end
end
+ # Consider the following example:
+ #
+ # class Person
+ # has_many :articles
+ # has_many :comment_tags, :through => :articles
+ # end
+ #
+ # class Article
+ # has_many :comments
+ # has_many :comment_tags, :through => :comments, :source => :tags
+ # end
+ #
+ # class Comment
+ # has_many :tags
+ # end
+ #
+ # There may be conditions on Person.comment_tags, Article.comment_tags and/or Comment.tags,
+ # but only Comment.tags will be represented in the through_reflection_chain. So this method
+ # creates an array of conditions corresponding to the through_reflection_chain. Each item in
+ # the through_conditions array corresponds to an item in the through_reflection_chain, and is
+ # itself an array of conditions from an arbitrary number of relevant reflections.
+ def through_conditions
+ @through_conditions ||= begin
+ # Initialize the first item - which corresponds to this reflection - either by recursing
+ # into the souce reflection (if it is itself a through reflection), or by grabbing the
+ # source reflection conditions.
+ if source_reflection.source_reflection
+ conditions = source_reflection.through_conditions
+ else
+ conditions = [Array.wrap(source_reflection.options[:conditions])]
+ end
+
+ # Add to it the conditions from this reflection if necessary.
+ conditions.first << options[:conditions] if options[:conditions]
+
+ # Recursively fill out the rest of the array from the through reflection
+ conditions += through_reflection.through_conditions
+
+ # And return
+ conditions
+ end
+ end
+
def nested?
through_reflection_chain.length > 2
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 0e9c8a2639..5b24d49a7d 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -15,7 +15,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
- assert_equal 2, authors[1].posts.size
+ assert_equal 3, authors[1].posts.size
assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
end
@@ -23,7 +23,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
authors = Author.find(:all, :include=>[{:posts=>:comments}, :categorizations], :order=>"authors.id")
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
- assert_equal 2, authors[1].posts.size
+ assert_equal 3, authors[1].posts.size
assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
assert_equal 1, authors[0].categorizations.size
assert_equal 2, authors[1].categorizations.size
@@ -56,7 +56,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id")
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
- assert_equal 2, authors[1].posts.size
+ assert_equal 3, authors[1].posts.size
assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 2ff0714e9f..6b910ae2a0 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -53,8 +53,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_with_ordering
list = Post.find(:all, :include => :comments, :order => "posts.id DESC")
- [:misc_by_mary, :misc_by_bob, :eager_other, :sti_habtm, :sti_post_and_comments,
- :sti_comments, :authorless, :thinking, :welcome
+ [:other_by_mary, :other_by_bob, :misc_by_mary, :misc_by_bob, :eager_other,
+ :sti_habtm, :sti_post_and_comments, :sti_comments, :authorless, :thinking, :welcome
].each_with_index do |post, index|
assert_equal posts(post), list[index]
end
diff --git a/activerecord/test/cases/associations/nested_has_many_through_associations_test.rb b/activerecord/test/cases/associations/nested_has_many_through_associations_test.rb
index 03ec4281d8..c39ec5d139 100644
--- a/activerecord/test/cases/associations/nested_has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_has_many_through_associations_test.rb
@@ -92,8 +92,6 @@ class NestedHasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_no_queries do
assert_equal [luke, david, david], authors.first.subscribers.sort_by(&:nick)
end
-
- # TODO: Add eager loading test using LEFT OUTER JOIN
end
# has_many through
@@ -325,7 +323,7 @@ class NestedHasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_nested_has_many_through_with_a_table_referenced_multiple_times
author = authors(:bob)
- assert_equal [posts(:misc_by_bob), posts(:misc_by_mary)], author.similar_posts.sort_by(&:id)
+ assert_equal [posts(:misc_by_bob), posts(:misc_by_mary), posts(:other_by_bob), posts(:other_by_mary)], author.similar_posts.sort_by(&:id)
# Mary and Bob both have posts in misc, but they are the only ones.
authors = Author.joins(:similar_posts).where('posts.id' => posts(:misc_by_bob).id)
@@ -406,6 +404,42 @@ class NestedHasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_nested_has_many_through_with_conditions_on_through_associations
+ blue, bob = tags(:blue), authors(:bob)
+
+ assert_equal [blue], bob.misc_post_first_blue_tags
+
+ # Pointless condition to force single-query loading
+ assert_includes_and_joins_equal(
+ Author.where('tags.id = tags.id'),
+ [bob], :misc_post_first_blue_tags
+ )
+
+ assert Author.where('tags.id' => 100).joins(:misc_post_first_blue_tags).empty?
+
+ authors = assert_queries(3) { Author.includes(:misc_post_first_blue_tags).to_a }
+ assert_no_queries do
+ assert_equal [blue], authors[2].misc_post_first_blue_tags
+ end
+ end
+
+ def test_nested_has_many_through_with_conditions_on_source_associations
+ blue, bob = tags(:blue), authors(:bob)
+
+ assert_equal [blue], bob.misc_post_first_blue_tags_2
+
+ # Pointless condition to force single-query loading
+ assert_includes_and_joins_equal(
+ Author.where('tags.id = tags.id'),
+ [bob], :misc_post_first_blue_tags_2
+ )
+
+ authors = assert_queries(4) { Author.includes(:misc_post_first_blue_tags_2).to_a }
+ assert_no_queries do
+ assert_equal [blue], authors[2].misc_post_first_blue_tags_2
+ end
+ end
+
private
def assert_includes_and_joins_equal(query, expected, association)
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 70883ad30f..9e72ac4250 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -24,7 +24,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_each_should_execute_if_id_is_in_select
- assert_queries(5) do
+ assert_queries(6) do
Post.find_each(:select => "id, title, type", :batch_size => 2) do |post|
assert_kind_of Post, post
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 0476fc94df..4c9475f1cd 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -123,11 +123,13 @@ class FinderTest < ActiveRecord::TestCase
def test_find_all_with_limit_and_offset_and_multiple_order_clauses
first_three_posts = Post.find :all, :order => 'author_id, id', :limit => 3, :offset => 0
second_three_posts = Post.find :all, :order => ' author_id,id ', :limit => 3, :offset => 3
- last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6
+ third_three_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6
+ last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 9
assert_equal [[0,3],[1,1],[1,2]], first_three_posts.map { |p| [p.author_id, p.id] }
assert_equal [[1,4],[1,5],[1,6]], second_three_posts.map { |p| [p.author_id, p.id] }
- assert_equal [[2,7],[2,9],[3,8]], last_posts.map { |p| [p.author_id, p.id] }
+ assert_equal [[2,7],[2,9],[2,11]], third_three_posts.map { |p| [p.author_id, p.id] }
+ assert_equal [[3,8],[3,10]], last_posts.map { |p| [p.author_id, p.id] }
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index df4e84ca29..0d88c8eded 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -501,22 +501,22 @@ class RelationTest < ActiveRecord::TestCase
def test_count
posts = Post.scoped
- assert_equal 9, posts.count
- assert_equal 9, posts.count(:all)
- assert_equal 9, posts.count(:id)
+ assert_equal 11, posts.count
+ assert_equal 11, posts.count(:all)
+ assert_equal 11, posts.count(:id)
assert_equal 1, posts.where('comments_count > 1').count
- assert_equal 7, posts.where(:comments_count => 0).count
+ assert_equal 9, posts.where(:comments_count => 0).count
end
def test_count_with_distinct
posts = Post.scoped
assert_equal 3, posts.count(:comments_count, :distinct => true)
- assert_equal 9, posts.count(:comments_count, :distinct => false)
+ assert_equal 11, posts.count(:comments_count, :distinct => false)
assert_equal 3, posts.select(:comments_count).count(:distinct => true)
- assert_equal 9, posts.select(:comments_count).count(:distinct => false)
+ assert_equal 11, posts.select(:comments_count).count(:distinct => false)
end
def test_count_explicit_columns
@@ -526,7 +526,7 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [0], posts.select('comments_count').where('id is not null').group('id').order('id').count.values.uniq
assert_equal 0, posts.where('id is not null').select('comments_count').count
- assert_equal 9, posts.select('comments_count').count('id')
+ assert_equal 11, posts.select('comments_count').count('id')
assert_equal 0, posts.select('comments_count').count
assert_equal 0, posts.count(:comments_count)
assert_equal 0, posts.count('comments_count')
@@ -541,12 +541,12 @@ class RelationTest < ActiveRecord::TestCase
def test_size
posts = Post.scoped
- assert_queries(1) { assert_equal 9, posts.size }
+ assert_queries(1) { assert_equal 11, posts.size }
assert ! posts.loaded?
best_posts = posts.where(:comments_count => 0)
best_posts.to_a # force load
- assert_no_queries { assert_equal 7, best_posts.size }
+ assert_no_queries { assert_equal 9, best_posts.size }
end
def test_count_complex_chained_relations
diff --git a/activerecord/test/fixtures/posts.yml b/activerecord/test/fixtures/posts.yml
index ca6d4c2fe1..264ca164f0 100644
--- a/activerecord/test/fixtures/posts.yml
+++ b/activerecord/test/fixtures/posts.yml
@@ -64,3 +64,17 @@ misc_by_mary:
title: misc post by mary
body: hello
type: Post
+
+other_by_bob:
+ id: 10
+ author_id: 3
+ title: other post by bob
+ body: hello
+ type: Post
+
+other_by_mary:
+ id: 11
+ author_id: 2
+ title: other post by mary
+ body: hello
+ type: Post
diff --git a/activerecord/test/fixtures/taggings.yml b/activerecord/test/fixtures/taggings.yml
index 7cc7198ded..a337cce019 100644
--- a/activerecord/test/fixtures/taggings.yml
+++ b/activerecord/test/fixtures/taggings.yml
@@ -38,3 +38,31 @@ misc_post_by_mary:
tag_id: 2
taggable_id: 9
taggable_type: Post
+
+misc_by_bob_blue_first:
+ id: 8
+ tag_id: 3
+ taggable_id: 8
+ taggable_type: Post
+ comment: first
+
+misc_by_bob_blue_second:
+ id: 9
+ tag_id: 3
+ taggable_id: 8
+ taggable_type: Post
+ comment: second
+
+other_by_bob_blue:
+ id: 10
+ tag_id: 3
+ taggable_id: 10
+ taggable_type: Post
+ comment: first
+
+other_by_mary_blue:
+ id: 11
+ tag_id: 3
+ taggable_id: 11
+ taggable_type: Post
+ comment: first
diff --git a/activerecord/test/fixtures/tags.yml b/activerecord/test/fixtures/tags.yml
index 6cb886dc46..d4b7c9a4d5 100644
--- a/activerecord/test/fixtures/tags.yml
+++ b/activerecord/test/fixtures/tags.yml
@@ -5,3 +5,7 @@ general:
misc:
id: 2
name: Misc
+
+blue:
+ id: 3
+ name: Blue
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index b5f702018a..c0e082836d 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -86,7 +86,7 @@ class Author < ActiveRecord::Base
has_many :tagging, :through => :posts
has_many :taggings, :through => :posts
has_many :tags, :through => :posts
- has_many :similar_posts, :through => :tags, :source => :tagged_posts
+ has_many :similar_posts, :through => :tags, :source => :tagged_posts, :uniq => true
has_many :distinct_tags, :through => :posts, :source => :tags, :select => "DISTINCT tags.*", :order => "tags.name"
has_many :post_categories, :through => :posts, :source => :categories
has_many :tagging_tags, :through => :taggings, :source => :tag
@@ -103,6 +103,11 @@ class Author < ActiveRecord::Base
has_many :post_categories, :through => :posts, :source => :categories
has_many :category_post_comments, :through => :categories, :source => :post_comments
+
+ has_many :misc_posts, :class_name => 'Post', :conditions => "posts.title LIKE 'misc post%'"
+ has_many :misc_post_first_blue_tags, :through => :misc_posts, :source => :first_blue_tags
+
+ has_many :misc_post_first_blue_tags_2, :through => :posts, :source => :first_blue_tags_2, :conditions => "posts.title LIKE 'misc post%'"
scope :relation_include_posts, includes(:posts)
scope :relation_include_tags, includes(:tags)
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index f3b78c3647..281586b438 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -64,6 +64,11 @@ class Post < ActiveRecord::Base
has_many :funky_tags, :through => :taggings, :source => :tag
has_many :super_tags, :through => :taggings
has_one :tagging, :as => :taggable
+
+ has_many :first_taggings, :as => :taggable, :class_name => 'Tagging', :conditions => "taggings.comment = 'first'"
+ has_many :first_blue_tags, :through => :first_taggings, :source => :tag, :conditions => "tags.name = 'Blue'"
+
+ has_many :first_blue_tags_2, :through => :taggings, :source => :blue_tag, :conditions => "taggings.comment = 'first'"
has_many :invalid_taggings, :as => :taggable, :class_name => "Tagging", :conditions => 'taggings.id < 0'
has_many :invalid_tags, :through => :invalid_taggings, :source => :tag
diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb
index a1fa1a9750..c92df88e71 100644
--- a/activerecord/test/models/tagging.rb
+++ b/activerecord/test/models/tagging.rb
@@ -6,5 +6,6 @@ class Tagging < ActiveRecord::Base
belongs_to :tag, :include => :tagging
belongs_to :super_tag, :class_name => 'Tag', :foreign_key => 'super_tag_id'
belongs_to :invalid_tag, :class_name => 'Tag', :foreign_key => 'tag_id'
+ belongs_to :blue_tag, :class_name => 'Tag', :foreign_key => :tag_id, :conditions => "tags.name = 'Blue'"
belongs_to :taggable, :polymorphic => true, :counter_cache => true
-end \ No newline at end of file
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 8b9c56b895..ee129162a6 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -537,6 +537,7 @@ ActiveRecord::Schema.define do
t.column :super_tag_id, :integer
t.column :taggable_type, :string
t.column :taggable_id, :integer
+ t.string :comment
end
create_table :tags, :force => true do |t|