Skip to content

Commit

Permalink
Support has_one through assocs as the source association
Browse files Browse the repository at this point in the history
  • Loading branch information
jonleighton committed Oct 12, 2010
1 parent 7aea695 commit 1777600
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 20 deletions.
Expand Up @@ -53,7 +53,7 @@ def construct_select(custom_select = nil)
end

def construct_joins(custom_joins = nil)
# puts @reflection.through_reflection_chain.map(&:inspect)
# p @reflection.through_reflection_chain

"#{construct_through_joins} #{@reflection.options[:joins]} #{custom_joins}"
end
Expand All @@ -67,16 +67,27 @@ def construct_through_joins

case
when left.source_reflection.nil?
left_primary_key = left.primary_key_name
right_primary_key = right.klass.primary_key
# TODO: Perhaps need to pay attention to left.options[:primary_key] and
# left.options[:foreign_key] in places here

if left.options[:as]
polymorphic_join = "AND %s.%s = %s" % [
table_aliases[left], "#{left.options[:as]}_type",
# TODO: Why right.klass.name? Rather than left.active_record.name?
# TODO: Also should maybe use the base_class (see related code in JoinAssociation)
@owner.class.quote_value(right.klass.name)
]
case left.macro
when :belongs_to
left_primary_key = left.klass.primary_key
right_primary_key = right.primary_key_name
when :has_many, :has_one
left_primary_key = left.primary_key_name
right_primary_key = right.klass.primary_key

if left.options[:as]
polymorphic_join = "AND %s.%s = %s" % [
table_aliases[left], "#{left.options[:as]}_type",
# TODO: Why right.klass.name? Rather than left.active_record.name?
# TODO: Also should maybe use the base_class (see related code in JoinAssociation)
@owner.class.quote_value(right.klass.name)
]
end
when :has_and_belongs_to_many
raise NotImplementedError
end
when left.source_reflection.macro == :belongs_to
left_primary_key = left.klass.primary_key
Expand Down
Expand Up @@ -18,6 +18,9 @@
require 'models/book'
require 'models/subscription'
require 'models/rating'
require 'models/member'
require 'models/member_detail'
require 'models/member_type'

# NOTE: Some of these tests might not really test "nested" HMT associations, as opposed to ones which
# are just one level deep. But it's all the same thing really, as the "nested" code is being
Expand All @@ -26,7 +29,8 @@

class NestedHasManyThroughAssociationsTest < ActiveRecord::TestCase
fixtures :authors, :books, :posts, :subscriptions, :subscribers, :tags, :taggings,
:people, :readers, :references, :jobs, :ratings, :comments
:people, :readers, :references, :jobs, :ratings, :comments, :members, :member_details,
:member_types

# Through associations can either use the has_many or has_one macros.
#
Expand Down Expand Up @@ -56,6 +60,9 @@ def test_has_many_through_a_has_many_through_association_on_source_reflection
authors = Author.joins(:tags).where('tags.id' => tags(:general).id)
assert_equal [authors(:david)], authors.uniq

authors = Author.includes(:tags)
assert_equal [tags(:general), tags(:general)], authors.first.tags

# This ensures that the polymorphism of taggings is being observed correctly
authors = Author.joins(:tags).where('taggings.taggable_type' => 'FakeModel')
assert authors.empty?
Expand All @@ -71,11 +78,24 @@ def test_has_many_through_a_has_many_through_association_on_through_reflection
# All authors with subscribers where one of the subscribers' nick is 'alterself'
authors = Author.joins(:subscribers).where('subscribers.nick' => 'alterself')
assert_equal [authors(:david)], authors

# TODO: Make this work
# authors = Author.includes(:subscribers)
# assert_equal [subscribers(:first), subscribers(:second), subscribers(:second)], authors.first.subscribers
end

# TODO: has_many through
# has_many through
# Source: has_one through
# Through: has_one
def test_has_many_through_has_one_with_has_one_through_source_reflection
assert_equal [member_types(:founding)], members(:groucho).nested_member_types

members = Member.joins(:nested_member_types).where('member_types.id' => member_types(:founding).id)
assert_equal [members(:groucho)], members

members = Member.includes(:nested_member_types)
assert_equal [member_types(:founding)], members.first.nested_member_types
end

# TODO: has_many through
# Source: has_one
Expand Down Expand Up @@ -105,9 +125,18 @@ def test_has_many_through_a_has_many_through_association_on_through_reflection
# Source: has_many through
# Through: belongs_to

# TODO: has_one through
# has_one through
# Source: has_one through
# Through: has_one
def test_has_one_through_has_one_with_has_one_through_source_reflection
assert_equal member_types(:founding), members(:groucho).nested_member_type

members = Member.joins(:nested_member_type).where('member_types.id' => member_types(:founding).id)
assert_equal [members(:groucho)], members

members = Member.includes(:nested_member_type)
assert_equal member_types(:founding), members.first.nested_member_type
end

# TODO: has_one through
# Source: belongs_to
Expand Down
3 changes: 3 additions & 0 deletions activerecord/test/fixtures/member_details.yml
@@ -0,0 +1,3 @@
groucho:
id: 1
member_id: 1
2 changes: 2 additions & 0 deletions activerecord/test/fixtures/members.yml
@@ -1,6 +1,8 @@
groucho:
id: 1
name: Groucho Marx
member_type_id: 1
some_other_guy:
id: 2
name: Englebert Humperdink
member_type_id: 2
6 changes: 3 additions & 3 deletions activerecord/test/fixtures/memberships.yml
@@ -1,20 +1,20 @@
membership_of_boring_club:
joined_on: <%= 3.weeks.ago.to_s(:db) %>
club: boring_club
member: groucho
member_id: 1
favourite: false
type: CurrentMembership

membership_of_favourite_club:
joined_on: <%= 3.weeks.ago.to_s(:db) %>
club: moustache_club
member: groucho
member_id: 1
favourite: true
type: Membership

other_guys_membership:
joined_on: <%= 4.weeks.ago.to_s(:db) %>
club: boring_club
member: some_other_guy
member_id: 2
favourite: false
type: CurrentMembership
9 changes: 6 additions & 3 deletions activerecord/test/fixtures/sponsors.yml
@@ -1,9 +1,12 @@
moustache_club_sponsor_for_groucho:
sponsor_club: moustache_club
sponsorable: groucho (Member)
sponsorable_id: 1
sponsorable_type: Member
boring_club_sponsor_for_groucho:
sponsor_club: boring_club
sponsorable: some_other_guy (Member)
sponsorable_id: 2
sponsorable_type: Member
crazy_club_sponsor_for_groucho:
sponsor_club: crazy_club
sponsorable: some_other_guy (Member)
sponsorable_id: 2
sponsorable_type: Member
5 changes: 4 additions & 1 deletion activerecord/test/models/member.rb
Expand Up @@ -9,4 +9,7 @@ class Member < ActiveRecord::Base
has_one :member_detail
has_one :organization, :through => :member_detail
belongs_to :member_type
end

has_many :nested_member_types, :through => :member_detail, :source => :member_type
has_one :nested_member_type, :through => :member_detail, :source => :member_type
end

0 comments on commit 1777600

Please sign in to comment.