Permalink
Browse files

First bit of support for habtm in through assocs - test_has_many_thro…

…ugh_has_many_with_has_and_belongs_to_many_source_reflection now passes
  • Loading branch information...
jonleighton committed Oct 13, 2010
1 parent 199db8c commit 781ad0f8fee209bcf10c5e52daae246477d49ea7
@@ -2180,6 +2180,7 @@ def join_to(relation)
# to represent the join table)
table, join_table = table
+ # TODO: Can join_key just be reflection.primary_key_name ?
join_key = reflection.options[:foreign_key] ||
reflection.active_record.to_s.foreign_key
join_foreign_key = reflection.active_record.primary_key
@@ -2192,18 +2193,37 @@ def join_to(relation)
# We've done the first join now, so update the foreign_table for the second
foreign_table = join_table
+ # TODO: Can foreign_key be reflection.association_foreign_key?
key = reflection.klass.primary_key
foreign_key = reflection.options[:association_foreign_key] ||
reflection.klass.to_s.foreign_key
end
- elsif reflection.source_reflection.macro == :belongs_to
- key = reflection.klass.primary_key
- foreign_key = reflection.source_reflection.primary_key_name
-
- conditions << source_type_conditions(reflection, foreign_table)
else
- key = reflection.source_reflection.primary_key_name
- foreign_key = reflection.source_reflection.klass.primary_key
+ case reflection.source_reflection.macro
+ when :belongs_to
+ key = reflection.klass.primary_key
+ foreign_key = reflection.source_reflection.primary_key_name
+
+ conditions << source_type_conditions(reflection, foreign_table)
+ when :has_many, :has_one
+ key = reflection.source_reflection.primary_key_name
+ foreign_key = reflection.source_reflection.klass.primary_key
+ when :has_and_belongs_to_many
+ table, join_table = table
+
+ join_key = reflection.source_reflection.primary_key_name
+ join_foreign_key = reflection.source_reflection.klass.primary_key
+
+ relation = relation.join(join_table, join_type).on(
+ join_table[join_key].
+ eq(foreign_table[join_foreign_key])
+ )
+
+ foreign_table = join_table
+
+ key = reflection.klass.primary_key
+ foreign_key = reflection.source_reflection.association_foreign_key
+ end
end
conditions << table[key].eq(foreign_table[foreign_key])
@@ -2269,14 +2289,19 @@ def setup_tables
# For habtm, we have two Arel::Table instances related to a single reflection, so
# we just store them as a pair in the array.
- if reflection.macro == :has_and_belongs_to_many
+ if reflection.macro == :has_and_belongs_to_many ||
+ (reflection.source_reflection &&
+ reflection.source_reflection.macro == :has_and_belongs_to_many)
+
+ join_table_name = (reflection.source_reflection || reflection).options[:join_table]
+
aliased_join_table_name = alias_tracker.aliased_name_for(
- reflection.options[:join_table],
+ join_table_name,
table_alias_for(reflection, true)
)
join_table = Arel::Table.new(
- reflection.options[:join_table], :engine => arel_engine,
+ join_table_name, :engine => arel_engine,
:as => aliased_join_table_name
)
@@ -65,6 +65,7 @@ def construct_through_joins
# Iterate over each pair in the through reflection chain, joining them together
@reflection.through_reflection_chain.each_cons(2) do |left, right|
polymorphic_join = nil
+ left_table, right_table = table_aliases[left], table_aliases[right]
if left.source_reflection.nil?
# TODO: Perhaps need to pay attention to left.options[:primary_key] and
@@ -114,20 +115,31 @@ def construct_through_joins
]
end
when :has_and_belongs_to_many
- raise NotImplementedError
+ join_table, left_table = left_table
+
+ left_primary_key = left.klass.primary_key
+ join_primary_key = left.source_reflection.association_foreign_key
+
+ joins << "INNER JOIN %s ON %s.%s = %s.%s" % [
+ table_name_and_alias(
+ quote_table_name(left.source_reflection.options[:join_table]),
+ join_table
+ ),
+ left_table, left_primary_key,
+ join_table, join_primary_key
+ ]
+
+ left_table = join_table
+
+ left_primary_key = left.source_reflection.primary_key_name
+ right_primary_key = right.klass.primary_key
end
end
- if right.quoted_table_name == table_aliases[right]
- table = right.quoted_table_name
- else
- table = "#{right.quoted_table_name} #{table_aliases[right]}"
- end
-
joins << "INNER JOIN %s ON %s.%s = %s.%s %s" % [
- table,
- table_aliases[left], left_primary_key,
- table_aliases[right], right_primary_key,
+ table_name_and_alias(right.quoted_table_name, right_table),
+ left_table, left_primary_key,
+ right_table, right_primary_key,
polymorphic_join
]
end
@@ -147,13 +159,16 @@ def table_aliases
table_alias_for(reflection, reflection != @reflection)
))
- if reflection.macro == :has_and_belongs_to_many
+ if reflection.macro == :has_and_belongs_to_many ||
+ (reflection.source_reflection &&
+ reflection.source_reflection.macro == :has_and_belongs_to_many)
+
join_table_alias = quote_table_name(alias_tracker.aliased_name_for(
- reflection.options[:join_table],
+ (reflection.source_reflection || reflection).options[:join_table],
table_alias_for(reflection, true)
))
- aliases[reflection] = [table_alias, join_table_alias]
+ aliases[reflection] = [join_table_alias, table_alias]
else
aliases[reflection] = table_alias
end
@@ -173,6 +188,10 @@ def table_alias_for(reflection, join = false)
def quote_table_name(table_name)
@reflection.klass.connection.quote_table_name(table_name)
end
+
+ def table_name_and_alias(table_name, table_alias)
+ "#{table_name} #{table_alias if table_alias != table_name}".strip
+ end
# Construct attributes for associate pointing to owner.
def construct_owner_attributes(reflection)
@@ -719,7 +719,7 @@ def test_find_grouped
def test_find_scoped_grouped
assert_equal 5, categories(:general).posts_grouped_by_title.size
- assert_equal 2, categories(:technology).posts_grouped_by_title.size
+ assert_equal 1, categories(:technology).posts_grouped_by_title.size
end
def test_find_scoped_grouped_having
@@ -159,10 +159,15 @@ def test_has_many_through_has_one_through_with_has_many_source_reflection
# has_many through
# Source: has_and_belongs_to_many
# Through: has_many
- # TODO: Enable and implement this, and finish off the test
- # def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection
- # assert_equal [categories(:general), categories(:technology)], authors(:bob).post_categories
- # end
+ def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection
+ assert_equal [categories(:general), categories(:cooking)], authors(:bob).post_categories
+
+ authors = Author.joins(:post_categories).where('categories.id' => categories(:cooking).id)
+ assert_equal [authors(:bob)], authors
+
+ authors = Author.includes(:post_categories)
+ assert_equal [categories(:general), categories(:cooking)], authors[2].post_categories
+ end
# TODO: has_many through
# Source: has_many
@@ -12,3 +12,8 @@ sti_test:
id: 3
name: Special category
type: SpecialCategory
+
+cooking:
+ id: 4
+ name: Cooking
+ type: Category
@@ -26,6 +26,6 @@ general_misc_by_bob:
category_id: 1
post_id: 8
-technology_misc_by_bob:
- category_id: 2
+cooking_misc_by_bob:
+ category_id: 4
post_id: 8

0 comments on commit 781ad0f

Please sign in to comment.