Permalink
Browse files

Destroy associated has_and_belongs_to_many records after all before_d…

…estroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. Closes #2065.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2940 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent c54b51f commit 35b4bdcff0e697765b562a74bd881e78de97f4d1 @jeremy jeremy committed Nov 8, 2005
View
@@ -1,5 +1,7 @@
*SVN*
+* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. #2065 [larrywilliams1@gmail.com, sam.kirchmeier@gmail.com, elliot@townx.org, Jeremy Kemper]
+
* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all. [Jeremy Kemper]
* More compatible Oracle column reflection. #2771 [Ryan Davis <ryand-ruby@zenspider.com>, Michael Schoen <schoenm@earthlink.net>]
@@ -652,8 +652,17 @@ def has_and_belongs_to_many(association_id, options = {}, &extension)
collection_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasAndBelongsToManyAssociation)
- before_destroy_sql = "DELETE FROM #{options[:join_table]} WHERE #{association_class_primary_key_name} = \\\#{self.quoted_id}"
- module_eval(%{before_destroy "self.connection.delete(%{#{before_destroy_sql}})"}) # "
+ # Don't use a before_destroy callback since users' before_destroy
+ # callbacks will be executed after the association is wiped out.
+ old_method = "destroy_without_habtm_shim_for_#{association_name}"
+ class_eval <<-end_eval
+ alias_method :#{old_method}, :destroy_without_callbacks
+ def destroy_without_callbacks
+ #{association_name}.clear
+ #{old_method}
+ end
+ end_eval
+
add_association_callbacks(association_name, options)
# deprecated api
@@ -1246,10 +1246,13 @@ def test_deleting_all
end
def test_removing_associations_on_destroy
- Developer.find(1).destroy
- assert Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
+ david = DeveloperWithBeforeDestroyRaise.find(1)
+ assert !david.projects.empty?
+ assert_nothing_raised { david.destroy }
+ assert david.projects.empty?
+ assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
end
-
+
def test_additional_columns_from_join_table
# SQL Server doesn't have a separate column type just for dates,
# so the time is in the string and incorrectly formatted
@@ -28,3 +28,13 @@ class DeveloperWithAggregate < ActiveRecord::Base
self.table_name = 'developers'
composed_of :salary, :class_name => 'DeveloperSalary', :mapping => [%w(salary amount)]
end
+
+class DeveloperWithBeforeDestroyRaise < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, :join_table => 'developers_projects', :foreign_key => 'developer_id'
+ before_destroy :raise_if_projects_empty!
+
+ def raise_if_projects_empty!
+ raise if projects.empty?
+ end
+end

0 comments on commit 35b4bdc

Please sign in to comment.