aboutsummaryrefslogblamecommitdiffstats
path: root/activerecord/test/cases/associations/inverse_associations_test.rb
blob: 47f83db1120f3345d4cd778aedb92779a1af0eae (plain) (tree)































































































                                                                                                             








                                                                                                           







                                                                                                           

     









































                                                                                                                         









                                                                                                             










                                                                                                             

     

















































                                                                                                                                   









                                                                                                                                   








                                                                                                                                   































































                                                                                                                                                 
require "cases/helper"
require 'models/man'
require 'models/face'
require 'models/interest'
require 'models/zine'
require 'models/club'
require 'models/sponsor'

class InverseAssociationTests < ActiveRecord::TestCase
  def test_should_allow_for_inverse_of_options_in_associations
    assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_many') do
      Class.new(ActiveRecord::Base).has_many(:wheels, :inverse_of => :car)
    end

    assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_one') do
      Class.new(ActiveRecord::Base).has_one(:engine, :inverse_of => :car)
    end

    assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on belongs_to') do
      Class.new(ActiveRecord::Base).belongs_to(:car, :inverse_of => :driver)
    end
  end

  def test_should_be_able_to_ask_a_reflection_if_it_has_an_inverse
    has_one_with_inverse_ref = Man.reflect_on_association(:face)
    assert has_one_with_inverse_ref.respond_to?(:has_inverse?)
    assert has_one_with_inverse_ref.has_inverse?

    has_many_with_inverse_ref = Man.reflect_on_association(:interests)
    assert has_many_with_inverse_ref.respond_to?(:has_inverse?)
    assert has_many_with_inverse_ref.has_inverse?

    belongs_to_with_inverse_ref = Face.reflect_on_association(:man)
    assert belongs_to_with_inverse_ref.respond_to?(:has_inverse?)
    assert belongs_to_with_inverse_ref.has_inverse?

    has_one_without_inverse_ref = Club.reflect_on_association(:sponsor)
    assert has_one_without_inverse_ref.respond_to?(:has_inverse?)
    assert !has_one_without_inverse_ref.has_inverse?

    has_many_without_inverse_ref = Club.reflect_on_association(:memberships)
    assert has_many_without_inverse_ref.respond_to?(:has_inverse?)
    assert !has_many_without_inverse_ref.has_inverse?

    belongs_to_without_inverse_ref = Sponsor.reflect_on_association(:sponsor_club)
    assert belongs_to_without_inverse_ref.respond_to?(:has_inverse?)
    assert !belongs_to_without_inverse_ref.has_inverse?
  end

  def test_should_be_able_to_ask_a_reflection_what_it_is_the_inverse_of
    has_one_ref = Man.reflect_on_association(:face)
    assert has_one_ref.respond_to?(:inverse_of)

    has_many_ref = Man.reflect_on_association(:interests)
    assert has_many_ref.respond_to?(:inverse_of)

    belongs_to_ref = Face.reflect_on_association(:man)
    assert belongs_to_ref.respond_to?(:inverse_of)
  end

  def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of
    has_one_ref = Man.reflect_on_association(:face)
    assert_equal Face.reflect_on_association(:man), has_one_ref.inverse_of

    has_many_ref = Man.reflect_on_association(:interests)
    assert_equal Interest.reflect_on_association(:man), has_many_ref.inverse_of

    belongs_to_ref = Face.reflect_on_association(:man)
    assert_equal Man.reflect_on_association(:face), belongs_to_ref.inverse_of
  end

  def test_associations_with_no_inverse_of_should_return_nil
    has_one_ref = Club.reflect_on_association(:sponsor)
    assert_nil has_one_ref.inverse_of

    has_many_ref = Club.reflect_on_association(:memberships)
    assert_nil has_many_ref.inverse_of

    belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club)
    assert_nil belongs_to_ref.inverse_of
  end
end

class InverseHasOneTests < ActiveRecord::TestCase
  fixtures :men, :faces

  def test_parent_instance_should_be_shared_with_child_on_find
    m = Man.find(:first)
    f = m.face
    assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
    m.name = 'Bongo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
    f.man.name = 'Mungo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
  end


  def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
    m = Man.find(:first, :include => :face)
    f = m.face
    assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
    m.name = 'Bongo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
    f.man.name = 'Mungo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"

    m = Man.find(:first, :include => :face, :order => 'faces.id')
    f = m.face
    assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
    m.name = 'Bongo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
    f.man.name = 'Mungo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
  end

  def test_parent_instance_should_be_shared_with_newly_built_child
    m = Man.find(:first)
    f = m.build_face(:description => 'haunted')
    assert_not_nil f.man
    assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
    m.name = 'Bongo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
    f.man.name = 'Mungo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
  end

  def test_parent_instance_should_be_shared_with_newly_created_child
    m = Man.find(:first)
    f = m.create_face(:description => 'haunted')
    assert_not_nil f.man
    assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
    m.name = 'Bongo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
    f.man.name = 'Mungo'
    assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
  end

  def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
    assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).dirty_face }
  end
end

class InverseHasManyTests < ActiveRecord::TestCase
  fixtures :men, :interests

  def test_parent_instance_should_be_shared_with_every_child_on_find
    m = Man.find(:first)
    is = m.interests
    is.each do |i|
      assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
      m.name = 'Bongo'
      assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
      i.man.name = 'Mungo'
      assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
    end
  end

  def test_parent_instance_should_be_shared_with_eager_loaded_children
    m = Man.find(:first, :include => :interests)
    is = m.interests
    is.each do |i|
      assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
      m.name = 'Bongo'
      assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
      i.man.name = 'Mungo'
      assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
    end

    m = Man.find(:first, :include => :interests, :order => 'interests.id')
    is = m.interests
    is.each do |i|
      assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
      m.name = 'Bongo'
      assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
      i.man.name = 'Mungo'
      assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
    end

  end

  def test_parent_instance_should_be_shared_with_newly_built_child
    m = Man.find(:first)
    i = m.interests.build(:topic => 'Industrial Revolution Re-enactment')
    assert_not_nil i.man
    assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
    m.name = 'Bongo'
    assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
    i.man.name = 'Mungo'
    assert_equal m.name, i.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
  end

  def test_parent_instance_should_be_shared_with_newly_created_child
    m = Man.find(:first)
    i = m.interests.create(:topic => 'Industrial Revolution Re-enactment')
    assert_not_nil i.man
    assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
    m.name = 'Bongo'
    assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
    i.man.name = 'Mungo'
    assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
  end

  def test_parent_instance_should_be_shared_with_poked_in_child
    m = Man.find(:first)
    i = Interest.create(:topic => 'Industrial Revolution Re-enactment')
    m.interests << i
    assert_not_nil i.man
    assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
    m.name = 'Bongo'
    assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
    i.man.name = 'Mungo'
    assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
  end

  def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
    assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).secret_interests }
  end
end

class InverseBelongsToTests < ActiveRecord::TestCase
  fixtures :men, :faces, :interests

  def test_child_instance_should_be_shared_with_parent_on_find
    f = Face.find(:first)
    m = f.man
    assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
    f.description = 'gormless'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
    m.face.description = 'pleasing'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
  end

  def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
    f = Face.find(:first, :include => :man)
    m = f.man
    assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
    f.description = 'gormless'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
    m.face.description = 'pleasing'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"


    f = Face.find(:first, :include => :man, :order => 'men.id')
    m = f.man
    assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
    f.description = 'gormless'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
    m.face.description = 'pleasing'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
  end

  def test_child_instance_should_be_shared_with_newly_built_parent
    f = Face.find(:first)
    m = f.build_man(:name => 'Charles')
    assert_not_nil m.face
    assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
    f.description = 'gormless'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
    m.face.description = 'pleasing'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to just-built-parent-owned instance"
  end

  def test_child_instance_should_be_shared_with_newly_created_parent
    f = Face.find(:first)
    m = f.create_man(:name => 'Charles')
    assert_not_nil m.face
    assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
    f.description = 'gormless'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
    m.face.description = 'pleasing'
    assert_equal f.description, m.face.description, "Description of face should be the same after changes to newly-created-parent-owned instance"
  end

  def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
    i = Interest.find(:first)
    m = i.man
    assert_not_nil m.interests
    iz = m.interests.detect {|iz| iz.id == i.id}
    assert_not_nil iz
    assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
    i.topic = 'Eating cheese with a spoon'
    assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child"
    iz.topic = 'Cow tipping'
    assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
  end

  def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
    assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_man }
  end
end

# NOTE - these tests might not be meaningful, ripped as they were from the parental_control plugin
# which would guess the inverse rather than look for an explicit configuration option.
class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
  fixtures :men, :interests, :zines

  def test_that_we_can_load_associations_that_have_the_same_reciprocal_name_from_different_models
    assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
      i = Interest.find(:first)
      z = i.zine
      m = i.man
    end
  end

  def test_that_we_can_create_associations_that_have_the_same_reciprocal_name_from_different_models
    assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
      i = Interest.find(:first)
      i.build_zine(:title => 'Get Some in Winter! 2008')
      i.build_man(:name => 'Gordon')
      i.save!
    end
  end
end