require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' require 'models/ship' require 'models/pirate' require 'models/car' require 'models/bulb' class HasOneAssociationsTest < ActiveRecord::TestCase self.use_transactional_fixtures = false unless supports_savepoints? fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates def setup Account.destroyed_account_ids.clear end def test_has_one assert_equal companies(:first_firm).account, Account.find(1) assert_equal Account.find(1).credit_limit, companies(:first_firm).account.credit_limit end def test_has_one_cache_nils firm = companies(:another_firm) assert_queries(1) { assert_nil firm.account } assert_queries(0) { assert_nil firm.account } firms = Firm.find(:all, :include => :account) assert_queries(0) { firms.each(&:account) } end def test_with_select assert_equal Firm.find(1).account_with_select.attributes.size, 2 assert_equal Firm.find(1, :include => :account_with_select).account_with_select.attributes.size, 2 end def test_finding_using_primary_key firm = companies(:first_firm) assert_equal Account.find_by_firm_id(firm.id), firm.account firm.firm_id = companies(:rails_core).id assert_equal accounts(:rails_core_account), firm.account_using_primary_key end def test_update_with_foreign_and_primary_keys firm = companies(:first_firm) account = firm.account_using_foreign_and_primary_keys assert_equal Account.find_by_firm_name(firm.name), account firm.save firm.reload assert_equal account, firm.account_using_foreign_and_primary_keys end def test_can_marshal_has_one_association_with_nil_target firm = Firm.new assert_nothing_raised do assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes end firm.account assert_nothing_raised do assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes end end def test_proxy_assignment company = companies(:first_firm) assert_nothing_raised { company.account = company.account } end def test_type_mismatch assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = 1 } assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = Project.find(1) } end def test_natural_assignment apple = Firm.create("name" => "Apple") citibank = Account.create("credit_limit" => 10) apple.account = citibank assert_equal apple.id, citibank.firm_id end def test_natural_assignment_to_nil old_account_id = companies(:first_firm).account.id companies(:first_firm).account = nil companies(:first_firm).save assert_nil companies(:first_firm).account # account is dependent, therefore is destroyed when reference to owner is lost assert_raise(ActiveRecord::RecordNotFound) { Account.find(old_account_id) } end def test_nullification_on_association_change firm = companies(:rails_core) old_account_id = firm.account.id firm.account = Account.new(:credit_limit => 5) # account is dependent with nullify, therefore its firm_id should be nil assert_nil Account.find(old_account_id).firm_id end def test_natural_assignment_to_nil_after_destroy firm = companies(:rails_core) old_account_id = firm.account.id firm.account.destroy firm.account = nil assert_nil companies(:rails_core).account assert_raise(ActiveRecord::RecordNotFound) { Account.find(old_account_id) } end def test_association_change_calls_delete companies(:first_firm).deletable_account = Account.new(:credit_limit => 5) assert_equal [], Account.destroyed_account_ids[companies(:first_firm).id] end def test_association_change_calls_destroy companies(:first_firm).account = Account.new(:credit_limit => 5) assert_equal [companies(:first_firm).id], Account.destroyed_account_ids[companies(:first_firm).id] end def test_natural_assignment_to_already_associated_record company = companies(:first_firm) account = accounts(:signals37) assert_equal company.account, account company.account = account company.reload account.reload assert_equal company.account, account end def test_dependence num_accounts = Account.count firm = Firm.find(1) assert_not_nil firm.account account_id = firm.account.id assert_equal [], Account.destroyed_account_ids[firm.id] firm.destroy assert_equal num_accounts - 1, Account.count assert_equal [account_id], Account.destroyed_account_ids[firm.id] end def test_exclusive_dependence num_accounts = Account.count firm = ExclusivelyDependentFirm.find(9) assert_not_nil firm.account assert_equal [], Account.destroyed_account_ids[firm.id] firm.destroy assert_equal num_accounts - 1, Account.count assert_equal [], Account.destroyed_account_ids[firm.id] end def test_dependence_with_nil_associate firm = DependentFirm.new(:name => 'nullify') firm.save! assert_nothing_raised { firm.destroy } end def test_dependence_with_restrict option_before = ActiveRecord::Base.dependent_restrict_raises ActiveRecord::Base.dependent_restrict_raises = true firm = RestrictedFirm.create!(:name => 'restrict') firm.create_account(:credit_limit => 10) assert_not_nil firm.account assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy } assert RestrictedFirm.exists?(:name => 'restrict') assert firm.account.present? ensure ActiveRecord::Base.dependent_restrict_raises = option_before end def test_dependence_with_restrict_with_dependent_restrict_raises_config_set_to_false option_before = ActiveRecord::Base.dependent_restrict_raises ActiveRecord::Base.dependent_restrict_raises = false firm = RestrictedFirm.create!(:name => 'restrict') firm.create_account(:credit_limit => 10) assert_not_nil firm.account firm.destroy assert !firm.errors.empty? assert_equal "Cannot delete record because a dependent account exist", firm.errors[:base].first assert RestrictedFirm.exists?(:name => 'restrict') assert firm.account.present? ensure ActiveRecord::Base.dependent_restrict_raises = option_before end def test_dependence_with_restrict_with_dependent_restrict_raises_config_set_to_false_and_attribute_name old_backend = I18n.backend I18n.backend = I18n::Backend::Simple.new I18n.backend.store_translations 'en', :activerecord => {:attributes => {:restricted_firm => {:account => "account model"}}} option_before = ActiveRecord::Base.dependent_restrict_raises ActiveRecord::Base.dependent_restrict_raises = false firm = RestrictedFirm.create!(:name => 'restrict') firm.create_account(:credit_limit => 10) assert_not_nil firm.account firm.destroy assert !firm.errors.empty? assert_equal "Cannot delete record because a dependent account model exist", firm.errors[:base].first assert RestrictedFirm.exists?(:name => 'restrict') assert firm.account.present? ensure ActiveRecord::Base.dependent_restrict_raises = option_before I18n.backend = old_backend end def test_successful_build_association firm = Firm.new("name" => "GlobalMegaCorp") firm.save account = firm.build_account("credit_limit" => 1000) assert account.save assert_equal account, firm.account end def test_build_and_create_should_not_happen_within_scope pirate = pirates(:blackbeard) scoped_count = pirate.association(:foo_bulb).scoped.where_values.count bulb = pirate.build_foo_bulb assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count bulb = pirate.create_foo_bulb assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count bulb = pirate.create_foo_bulb! assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count end def test_create_association firm = Firm.create(:name => "GlobalMegaCorp") account = firm.create_account(:credit_limit => 1000) assert_equal account, firm.reload.account end def test_create_association_with_bang firm = Firm.create(:name => "GlobalMegaCorp") account = firm.create_account!(:credit_limit => 1000) assert_equal account, firm.reload.account end def test_create_association_with_bang_failing firm = Firm.create(:name => "GlobalMegaCorp") assert_raise ActiveRecord::RecordInvalid do firm.create_account! end account = firm.account assert_not_nil account account.credit_limit = 5 account.save assert_equal account, firm.reload.account end def test_build firm = Firm.new("name" => "GlobalMegaCorp") firm.save firm.account = account = Account.new("credit_limit" => 1000) assert_equal account, firm.account assert account.save assert_equal account, firm.account end def test_create firm = Firm.new("name" => "GlobalMegaCorp") firm.save firm.account = account = Account.create("credit_limit" => 1000) assert_equal account, firm.account end def test_create_before_save firm = Firm.new("name" => "GlobalMegaCorp") firm.account = account = Account.create("credit_limit" => 1000) assert_equal account, firm.account end def test_dependence_with_missing_association Account.destroy_all firm = Firm.find(1) assert_nil firm.account firm.destroy end def test_dependence_with_missing_association_and_nullify Account.destroy_all firm = DependentFirm.find(:first) assert_nil firm.account firm.destroy end def test_finding_with_interpolated_condition firm = Firm.find(:first) superior = firm.clients.create(:name => 'SuperiorCo') superior.rating = 10 superior.save assert_equal 10, firm.clients_with_interpolated_conditions.first.rating end def test_assignment_before_child_saved firm = Firm.find(1) firm.account = a = Account.new("credit_limit" => 1000) assert a.persisted? assert_equal a, firm.account assert_equal a, firm.account assert_equal a, firm.account(true) end def test_save_still_works_after_accessing_nil_has_one jp = Company.new :name => 'Jaded Pixel' jp.dummy_account.nil? assert_nothing_raised do jp.save! end end def test_cant_save_readonly_association assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_firm).readonly_account.save! } assert companies(:first_firm).readonly_account.readonly? end def test_has_one_proxy_should_not_respond_to_private_methods assert_raise(NoMethodError) { accounts(:signals37).private_method } assert_raise(NoMethodError) { companies(:first_firm).account.private_method } end def test_has_one_proxy_should_respond_to_private_methods_via_send accounts(:signals37).send(:private_method) companies(:first_firm).account.send(:private_method) end def test_save_of_record_with_loaded_has_one @firm = companies(:first_firm) assert_not_nil @firm.account assert_nothing_raised do Firm.find(@firm.id).save! Firm.find(@firm.id, :include => :account).save! end @firm.account.destroy assert_nothing_raised do Firm.find(@firm.id).save! Firm.find(@firm.id, :include => :account).save! end end def test_build_respects_hash_condition account = companies(:first_firm).build_account_limit_500_with_hash_conditions assert account.save assert_equal 500, account.credit_limit end def test_create_respects_hash_condition account = companies(:first_firm).create_account_limit_500_with_hash_conditions assert account.persisted? assert_equal 500, account.credit_limit end def test_attributes_are_being_set_when_initialized_from_has_one_association_with_where_clause new_account = companies(:first_firm).build_account(:firm_name => 'Account') assert_equal new_account.firm_name, "Account" end def test_creation_failure_without_dependent_option pirate = pirates(:blackbeard) orig_ship = pirate.ship assert_equal ships(:black_pearl), orig_ship new_ship = pirate.create_ship assert_not_equal ships(:black_pearl), new_ship assert_equal new_ship, pirate.ship assert new_ship.new_record? assert_nil orig_ship.pirate_id assert !orig_ship.changed? # check it was saved end def test_creation_failure_with_dependent_option pirate = pirates(:blackbeard).becomes(DestructivePirate) orig_ship = pirate.dependent_ship new_ship = pirate.create_dependent_ship assert new_ship.new_record? assert orig_ship.destroyed? end def test_creation_failure_due_to_new_record_should_raise_error pirate = pirates(:redbeard) new_ship = Ship.new assert_raise(ActiveRecord::RecordNotSaved) do pirate.ship = new_ship end assert_nil pirate.ship assert_nil new_ship.pirate_id end def test_replacement_failure_due_to_existing_record_should_raise_error pirate = pirates(:blackbeard) pirate.ship.name = nil assert !pirate.ship.valid? assert_raise(ActiveRecord::RecordNotSaved) do pirate.ship = ships(:interceptor) end assert_equal ships(:black_pearl), pirate.ship assert_equal pirate.id, pirate.ship.pirate_id end def test_replacement_failure_due_to_new_record_should_raise_error pirate = pirates(:blackbeard) new_ship = Ship.new assert_raise(ActiveRecord::RecordNotSaved) do pirate.ship = new_ship end assert_equal ships(:black_pearl), pirate.ship assert_equal pirate.id, pirate.ship.pirate_id assert_equal pirate.id, ships(:black_pearl).reload.pirate_id assert_nil new_ship.pirate_id end def test_association_keys_bypass_attribute_protection car = Car.create(:name => 'honda') bulb = car.build_bulb assert_equal car.id, bulb.car_id bulb = car.build_bulb :car_id => car.id + 1 assert_equal car.id, bulb.car_id bulb = car.create_bulb assert_equal car.id, bulb.car_id bulb = car.create_bulb :car_id => car.id + 1 assert_equal car.id, bulb.car_id end def test_association_conditions_bypass_attribute_protection car = Car.create(:name => 'honda') bulb = car.build_frickinawesome_bulb assert_equal true, bulb.frickinawesome? bulb = car.build_frickinawesome_bulb(:frickinawesome => false) assert_equal true, bulb.frickinawesome? bulb = car.create_frickinawesome_bulb assert_equal true, bulb.frickinawesome? bulb = car.create_frickinawesome_bulb(:frickinawesome => false) assert_equal true, bulb.frickinawesome? end def test_new_is_called_with_attributes_and_options car = Car.create(:name => 'honda') bulb = car.build_bulb assert_equal Bulb, bulb.class bulb = car.build_bulb assert_equal Bulb, bulb.class bulb = car.build_bulb(:bulb_type => :custom) assert_equal Bulb, bulb.class bulb = car.build_bulb({ :bulb_type => :custom }, :as => :admin) assert_equal CustomBulb, bulb.class end def test_build_with_block car = Car.create(:name => 'honda') bulb = car.build_bulb{ |b| b.color = 'Red' } assert_equal 'RED!', bulb.color end def test_create_with_block car = Car.create(:name => 'honda') bulb = car.create_bulb{ |b| b.color = 'Red' } assert_equal 'RED!', bulb.color end def test_create_bang_with_block car = Car.create(:name => 'honda') bulb = car.create_bulb!{ |b| b.color = 'Red' } assert_equal 'RED!', bulb.color end def test_association_attributes_are_available_to_after_initialize car = Car.create(:name => 'honda') bulb = car.create_bulb assert_equal car.id, bulb.attributes_after_initialize['car_id'] end def test_building_has_one_association_with_dependent_restrict option_before = ActiveRecord::Base.dependent_restrict_raises ActiveRecord::Base.dependent_restrict_raises = true klass = Class.new(ActiveRecord::Base) assert_deprecated { klass.has_one :account, :dependent => :restrict } assert_not_deprecated { klass.has_one :account } ensure ActiveRecord::Base.dependent_restrict_raises = option_before end end