Browse files

has_one supports the :dependent => :delete option which skips the typ…

…ical callback chain and deletes the associated object directly from the database. Closes #5927.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4848 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 92f1e26 commit 3704088ebde5ef074d186bff0d380858a9a01055 @jeremy jeremy committed Aug 29, 2006
View
2 activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* has_one supports the :dependent => :delete option which skips the typical callback chain and deletes the associated object directly from the database. #5927 [Chris Mear, Jonathan Viney]
+
* Nested subclasses are not prefixed with the parent class' table_name since they should always use the base class' table_name. #5911 [Jonathan Viney]
* SQLServer: work around bug where some unambiguous date formats are not correctly identified if the session language is set to german. #5894 [Tom Ward, kruth@bfpi]
View
9 activerecord/lib/active_record/associations.rb
@@ -570,8 +570,9 @@ def has_many(association_id, options = {}, &extension)
# sql fragment, such as "rank = 5".
# * <tt>:order</tt> - specify the order from which the associated object will be picked at the top. Specified as
# an "ORDER BY" sql fragment, such as "last_name, first_name DESC"
- # * <tt>:dependent</tt> - if set to :destroy (or true) all the associated objects are destroyed when this object is. Also,
- # association is assigned.
+ # * <tt>:dependent</tt> - if set to :destroy (or true) the associated object is destroyed when this object is. If set to
+ # :delete the associated object is deleted *without* calling its destroy method. If set to :nullify the associated
+ # object's foreign key is set to NULL. Also, association is assigned.
# * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
# of this class in lower-case and "_id" suffixed. So a +Person+ class that makes a has_one association will use "person_id"
# as the default foreign_key.
@@ -1020,12 +1021,14 @@ def configure_dependency_for_has_one(reflection)
case reflection.options[:dependent]
when :destroy, true
module_eval "before_destroy '#{reflection.name}.destroy unless #{reflection.name}.nil?'"
+ when :delete
+ module_eval "before_destroy '#{reflection.class_name}.delete(#{reflection.name}.id) unless #{reflection.name}.nil?'"
when :nullify
module_eval "before_destroy '#{reflection.name}.update_attribute(\"#{reflection.primary_key_name}\", nil)'"
when nil, false
# pass
else
- raise ArgumentError, "The :dependent option expects either :destroy or :nullify."
+ raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify."
end
end
View
18 activerecord/test/associations_test.rb
@@ -91,6 +91,10 @@ def test_proxy_accessors
class HasOneAssociationsTest < Test::Unit::TestCase
fixtures :accounts, :companies, :developers, :projects, :developers_projects
+ 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
@@ -168,8 +172,22 @@ def test_dependence
num_accounts = Account.count
firm = Firm.find(1)
assert !firm.account.nil?
+ 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 !firm.account.nil?
+ 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.destroyed_account_ids[firm.id]
end
def test_succesful_build_association
View
16 activerecord/test/calculations_test.rb
@@ -8,7 +8,7 @@ class CalculationsTest < Test::Unit::TestCase
fixtures :companies, :accounts, :topics
def test_should_sum_field
- assert_equal 265, Account.sum(:credit_limit)
+ assert_equal 318, Account.sum(:credit_limit)
end
def test_should_average_field
@@ -49,13 +49,13 @@ def test_should_group_by_summed_field
def test_should_order_by_grouped_field
c = Account.sum(:credit_limit, :group => :firm_id, :order => "firm_id")
- assert_equal [1, 2, 6], c.keys.compact
+ assert_equal [1, 2, 6, 9], c.keys.compact
end
def test_should_order_by_calculation
c = Account.sum(:credit_limit, :group => :firm_id, :order => "sum_credit_limit desc, firm_id")
- assert_equal [105, 60, 50, 50], c.keys.collect { |k| c[k] }
- assert_equal [6, 2, 1], c.keys.compact
+ assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] }
+ assert_equal [6, 2, 9, 1], c.keys.compact
end
def test_should_limit_calculation
@@ -114,8 +114,8 @@ def test_should_group_by_fields_with_table_alias
end
def test_should_calculate_with_invalid_field
- assert_equal 5, Account.calculate(:count, '*')
- assert_equal 5, Account.calculate(:count, :all)
+ assert_equal 6, Account.calculate(:count, '*')
+ assert_equal 6, Account.calculate(:count, :all)
end
def test_should_calculate_grouped_with_invalid_field
@@ -193,7 +193,7 @@ def test_should_reject_invalid_options
end
def test_should_count_selected_field_with_include
- assert_equal 5, Account.count(:distinct => true, :include => :firm)
- assert_equal 3, Account.count(:distinct => true, :include => :firm, :select => :credit_limit)
+ assert_equal 6, Account.count(:distinct => true, :include => :firm)
+ assert_equal 4, Account.count(:distinct => true, :include => :firm, :select => :credit_limit)
end
end
View
4 activerecord/test/finder_test.rb
@@ -454,8 +454,8 @@ def test_select_value
end
def test_select_values
- assert_equal ["1","2","3","4","5","6","7","8"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map! { |i| i.to_s }
- assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
+ assert_equal ["1","2","3","4","5","6","7","8","9"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map! { |i| i.to_s }
+ assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
end
protected
View
7 activerecord/test/fixtures/accounts.yml
@@ -20,4 +20,9 @@ last_account:
rails_core_account_2:
id: 5
firm_id: 6
- credit_limit: 55
+ credit_limit: 55
+
+odegy_account:
+ id: 6
+ firm_id: 9
+ credit_limit: 53
View
7 activerecord/test/fixtures/companies.yml
@@ -47,4 +47,9 @@ leetsoft:
jadedpixel:
id: 8
name: Jadedpixel
- client_of: 6
+ client_of: 6
+
+odegy:
+ id: 9
+ name: Odegy
+ type: ExclusivelyDependentFirm
View
15 activerecord/test/fixtures/company.rb
@@ -42,6 +42,9 @@ class DependentFirm < Company
has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :nullify
end
+class ExclusivelyDependentFirm < Company
+ has_one :account, :foreign_key => "firm_id", :dependent => :delete
+end
class Client < Company
belongs_to :firm, :foreign_key => "client_of"
@@ -83,6 +86,18 @@ class VerySpecialClient < SpecialClient
class Account < ActiveRecord::Base
belongs_to :firm
+ def self.destroyed_account_ids
+ @destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] }
+ end
+
+ before_destroy do |account|
+ if account.firm
+ Account.destroyed_account_ids[account.firm.id] << account.id
+ end
+ true
+ end
+
+
protected
def validate
errors.add_on_empty "credit_limit"
View
2 activerecord/test/inheritance_test.rb
@@ -58,7 +58,7 @@ def test_alt_inheritance_save
end
def test_inheritance_condition
- assert_equal 8, Company.count
+ assert_equal 9, Company.count
assert_equal 2, Firm.count
assert_equal 3, Client.count
end

0 comments on commit 3704088

Please sign in to comment.