From 823554eafef9e8ee8fe2788f6231a3e665c2cbbf Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 15 Jan 2005 17:45:16 +0000 Subject: Added support for associating unsaved objects #402 [Tim Bates] Added replace to associations, so you can do project.manager.replace(new_manager) or project.milestones.replace(new_milestones) #402 [Tim Bates] Added build and create methods to has_one and belongs_to associations, so you can now do project.manager.build(attributes) #402 [Tim Bates] Fixed that Base#== wouldn't work for multiple references to the same unsaved object #402 [Tim Bates] Added that if a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. #402 [Tim Bates] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@417 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activerecord/test/associations_test.rb | 232 ++++++++++++++++++++-- activerecord/test/callbacks_test.rb | 16 ++ activerecord/test/deprecated_associations_test.rb | 38 +++- activerecord/test/fixtures/company.rb | 2 + 4 files changed, 265 insertions(+), 23 deletions(-) (limited to 'activerecord/test') diff --git a/activerecord/test/associations_test.rb b/activerecord/test/associations_test.rb index 26640d0048..0001eebe9f 100755 --- a/activerecord/test/associations_test.rb +++ b/activerecord/test/associations_test.rb @@ -24,14 +24,14 @@ class AssociationsTest < Test::Unit::TestCase end def test_force_reload - firm = Firm.new + firm = Firm.new("name" => "A New Firm, Inc") firm.save firm.clients.each {|c|} # forcing to load all clients assert firm.clients.empty?, "New firm shouldn't have client objects" assert !firm.has_clients?, "New firm shouldn't have clients" assert_equal 0, firm.clients.size, "New firm should have 0 clients" - client = Client.new("firm_id" => firm.id) + client = Client.new("name" => "TheClient.com", "firm_id" => firm.id) client.save assert firm.clients.empty?, "New firm should have cached no client objects" @@ -72,12 +72,6 @@ class HasOneAssociationsTest < Test::Unit::TestCase def test_has_one assert_equal @signals37.account, Account.find(1) assert_equal Account.find(1).credit_limit, @signals37.account.credit_limit - assert @signals37.has_account?, "37signals should have an account" - assert Account.find(1).firm?(@signals37), "37signals account should be able to backtrack" - assert Account.find(1).has_firm?, "37signals account should be able to backtrack" - - assert !Account.find(2).has_firm?, "Unknown isn't linked" - assert !Account.find(2).firm?(@signals37), "Unknown isn't linked" end def test_type_mismatch @@ -100,43 +94,105 @@ class HasOneAssociationsTest < Test::Unit::TestCase assert_nil Account.find(old_account_id).firm_id end + def test_dependence + firm = Firm.find(1) + assert !firm.account.nil? + firm.destroy + assert_equal 1, Account.find_all.length + end + def test_build firm = Firm.new("name" => "GlobalMegaCorp") firm.save - - account = firm.build_account("credit_limit" => 1000) + + account = firm.account.build("credit_limit" => 1000) + assert_equal account, firm.account assert account.save assert_equal account, firm.account end + def test_build_before_child_saved + firm = Firm.find(1) + + account = firm.account.build("credit_limit" => 1000) + assert_equal account, firm.account + assert account.new_record? + assert firm.save + assert_equal account, firm.account + assert !account.new_record? + end + + def test_build_before_either_saved + firm = Firm.new("name" => "GlobalMegaCorp") + + account = firm.account.build("credit_limit" => 1000) + assert_equal account, firm.account + assert account.new_record? + assert firm.save + assert_equal account, firm.account + assert !account.new_record? + end + def test_failing_build_association firm = Firm.new("name" => "GlobalMegaCorp") firm.save - account = firm.build_account + account = firm.account.build + assert_equal account, firm.account assert !account.save + assert_equal account, firm.account assert_equal "can't be empty", account.errors.on("credit_limit") end def test_create firm = Firm.new("name" => "GlobalMegaCorp") firm.save - assert_equal firm.create_account("credit_limit" => 1000), firm.account + assert_equal firm.account.create("credit_limit" => 1000), firm.account end - - def test_dependence - firm = Firm.find(1) - assert !firm.account.nil? - firm.destroy - assert_equal 1, Account.find_all.length + + def test_create_before_save + firm = Firm.new("name" => "GlobalMegaCorp") + assert_equal firm.account.create("credit_limit" => 1000), firm.account end def test_dependence_with_missing_association Account.destroy_all - firm = Firm.find(1) - assert !firm.has_account? + firm = Firm.find(1) + assert firm.account.nil? firm.destroy end + + def test_assignment_before_parent_saved + firm = Firm.new("name" => "GlobalMegaCorp") + firm.account = a = Account.find(1) + assert firm.new_record? + assert_equal a, firm.account + assert firm.save + assert_equal a, firm.account + assert_equal a, firm.account(true) + end + + def test_assignment_before_child_saved + firm = Firm.find(1) + firm.account = a = Account.new("credit_limit" => 1000) + assert !a.new_record? + assert_equal a, firm.account + assert_equal a, firm.account + assert_equal a, firm.account(true) + end + + def test_assignment_before_either_saved + firm = Firm.new("name" => "GlobalMegaCorp") + firm.account = a = Account.new("credit_limit" => 1000) + assert firm.new_record? + assert a.new_record? + assert_equal a, firm.account + assert firm.save + assert !firm.new_record? + assert !a.new_record? + assert_equal a, firm.account + assert_equal a, firm.account(true) + end end @@ -257,16 +313,72 @@ class HasManyAssociationsTest < Test::Unit::TestCase assert_equal 3, @signals37.clients_of_firm(true).size end + def test_adding_before_save + no_of_firms = Firm.count + no_of_clients = Client.count + new_firm = Firm.new("name" => "A New Firm, Inc") + new_firm.clients_of_firm.push Client.new("name" => "Natural Company") + new_firm.clients_of_firm << (c = Client.new("name" => "Apple")) + assert new_firm.new_record? + assert c.new_record? + assert_equal 2, new_firm.clients_of_firm.size + assert_equal no_of_firms, Firm.count # Firm was not saved to database. + assert_equal no_of_clients, Client.count # Clients were not saved to database. + assert new_firm.save + assert !new_firm.new_record? + assert !c.new_record? + assert_equal new_firm, c.firm + assert_equal no_of_firms+1, Firm.count # Firm was saved to database. + assert_equal no_of_clients+2, Client.count # Clients were saved to database. + assert_equal 2, new_firm.clients_of_firm.size + assert_equal 2, new_firm.clients_of_firm(true).size + end + + def test_invalid_adding + firm = Firm.find(1) + assert !(firm.clients_of_firm << c = Client.new) + assert c.new_record? + assert !firm.save + assert c.new_record? + end + + def test_invalid_adding_before_save + no_of_firms = Firm.count + no_of_clients = Client.count + new_firm = Firm.new("name" => "A New Firm, Inc") + new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")]) + assert c.new_record? + assert !c.valid? + assert new_firm.valid? + assert !new_firm.save + assert c.new_record? + assert new_firm.new_record? + end + def test_build new_client = @signals37.clients_of_firm.build("name" => "Another Client") assert_equal "Another Client", new_client.name - assert new_client.save + assert new_client.new_record? + assert_equal new_client, @signals37.clients_of_firm.last + assert @signals37.save + assert !new_client.new_record? assert_equal 2, @signals37.clients_of_firm(true).size end + + def test_invalid_build + new_client = @signals37.clients_of_firm.build + assert new_client.new_record? + assert !new_client.valid? + assert_equal new_client, @signals37.clients_of_firm.last + assert !@signals37.save + assert new_client.new_record? + assert_equal 1, @signals37.clients_of_firm(true).size + end def test_create force_signal37_to_load_all_clients_of_firm new_client = @signals37.clients_of_firm.create("name" => "Another Client") + assert !new_client.new_record? assert_equal new_client, @signals37.clients_of_firm.last assert_equal new_client, @signals37.clients_of_firm(true).last end @@ -278,6 +390,14 @@ class HasManyAssociationsTest < Test::Unit::TestCase assert_equal 0, @signals37.clients_of_firm(true).size end + def test_deleting_before_save + new_firm = Firm.new("name" => "A New Firm, Inc.") + new_client = new_firm.clients_of_firm.build("name" => "Another Client") + assert_equal 1, new_firm.clients_of_firm.size + new_firm.clients_of_firm.delete(new_client) + assert_equal 0, new_firm.clients_of_firm.size + end + def test_deleting_a_collection force_signal37_to_load_all_clients_of_firm @signals37.clients_of_firm.create("name" => "Another Client") @@ -419,6 +539,44 @@ class BelongsToAssociationsTest < Test::Unit::TestCase assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted" end + def test_assignment_before_parent_saved + client = Client.find_first + apple = Firm.new("name" => "Apple") + client.firm = apple + assert_equal apple, client.firm + assert apple.new_record? + assert client.save + assert apple.save + assert !apple.new_record? + assert_equal apple, client.firm + assert_equal apple, client.firm(true) + end + + def test_assignment_before_child_saved + final_cut = Client.new("name" => "Final Cut") + firm = Firm.find(1) + final_cut.firm = firm + assert final_cut.new_record? + assert final_cut.save + assert !final_cut.new_record? + assert !firm.new_record? + assert_equal firm, final_cut.firm + assert_equal firm, final_cut.firm(true) + end + + def test_assignment_before_either_saved + final_cut = Client.new("name" => "Final Cut") + apple = Firm.new("name" => "Apple") + final_cut.firm = apple + assert final_cut.new_record? + assert apple.new_record? + assert final_cut.save + assert !final_cut.new_record? + assert !apple.new_record? + assert_equal apple, final_cut.firm + assert_equal apple, final_cut.firm(true) + end + def test_field_name_same_as_foreign_key computer = Computer.find 1 assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" @@ -514,6 +672,38 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase assert_equal 2, aridridel.projects.size assert_equal 2, aridridel.projects(true).size end + + def test_habtm_adding_before_save + no_of_devels = Developer.count + no_of_projects = Project.count + aridridel = Developer.new("name" => "Aridridel") + aridridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")]) + assert aridridel.new_record? + assert p.new_record? + assert aridridel.save + assert !aridridel.new_record? + assert_equal no_of_devels+1, Developer.count + assert_equal no_of_projects+1, Project.count + assert_equal 2, aridridel.projects.size + assert_equal 2, aridridel.projects(true).size + end + + def test_build + devel = Developer.find(1) + proj = devel.projects.build("name" => "Projekt") + assert_equal devel.projects.last, proj + assert proj.new_record? + devel.save + assert !proj.new_record? + assert_equal devel.projects.last, proj + end + + def test_create + devel = Developer.find(1) + proj = devel.projects.create("name" => "Projekt") + assert_equal devel.projects.last, proj + assert !proj.new_record? + end def test_uniq_after_the_fact @developers["jamis"].find.projects << @projects["active_record"].find diff --git a/activerecord/test/callbacks_test.rb b/activerecord/test/callbacks_test.rb index 3992c0a6a0..b92cbe7798 100644 --- a/activerecord/test/callbacks_test.rb +++ b/activerecord/test/callbacks_test.rb @@ -227,4 +227,20 @@ class CallbacksTest < Test::Unit::TestCase [ :after_initialize, :method ] ], david.history end + + def test_zzz_callback_returning_false # must be run last since we modify CallbackDeveloper + david = CallbackDeveloper.find(1) + CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :returning_false]; return false } + CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] } + david.save + assert_equal [ + [ :after_find, :method ], + [ :after_initialize, :method ], + [ :before_validation, :string ], + [ :before_validation, :proc ], + [ :before_validation, :object ], + [ :before_validation, :block ], + [ :before_validation, :returning_false ] + ], david.history + end end diff --git a/activerecord/test/deprecated_associations_test.rb b/activerecord/test/deprecated_associations_test.rb index ed1d3c4055..f746275215 100755 --- a/activerecord/test/deprecated_associations_test.rb +++ b/activerecord/test/deprecated_associations_test.rb @@ -138,14 +138,14 @@ class DeprecatedAssociationsTest < Test::Unit::TestCase end def test_force_reload - firm = Firm.new + firm = Firm.new("name" => "A New Firm, Inc") firm.save firm.clients.each {|c|} # forcing to load all clients assert firm.clients.empty?, "New firm shouldn't have client objects" assert !firm.has_clients?, "New firm shouldn't have clients" assert_equal 0, firm.clients_count, "New firm should have 0 clients" - client = Client.new("firm_id" => firm.id) + client = Client.new("name" => "TheClient.com", "firm_id" => firm.id) client.save assert firm.clients.empty?, "New firm should have cached no client objects" @@ -340,4 +340,38 @@ class DeprecatedAssociationsTest < Test::Unit::TestCase assert_equal 2, Firm.find_first.find_all_in_clients("type = 'Client'").length assert_equal 1, Firm.find_first.find_all_in_clients("name = 'Summit'").length end + + def test_has_one + assert @signals37.account?(Account.find(1)) + assert @signals37.has_account?, "37signals should have an account" + assert Account.find(1).firm?(@signals37), "37signals account should be able to backtrack" + assert Account.find(1).has_firm?, "37signals account should be able to backtrack" + + assert !Account.find(2).has_firm?, "Unknown isn't linked" + assert !Account.find(2).firm?(@signals37), "Unknown isn't linked" + end + + def test_has_one_build + firm = Firm.new("name" => "GlobalMegaCorp") + assert firm.save + + account = firm.build_account("credit_limit" => 1000) + assert account.save + assert_equal account, firm.account + end + + def test_has_one_failing_build_association + firm = Firm.new("name" => "GlobalMegaCorp") + firm.save + + account = firm.build_account + assert !account.save + assert_equal "can't be empty", account.errors.on("credit_limit") + end + + def test_has_one_create + firm = Firm.new("name" => "GlobalMegaCorp") + firm.save + assert_equal firm.create_account("credit_limit" => 1000), firm.account + end end diff --git a/activerecord/test/fixtures/company.rb b/activerecord/test/fixtures/company.rb index 7d9a6053d4..579590e909 100755 --- a/activerecord/test/fixtures/company.rb +++ b/activerecord/test/fixtures/company.rb @@ -1,5 +1,7 @@ class Company < ActiveRecord::Base attr_protected :rating + + validates_presence_of :name end -- cgit v1.2.3