From 16b129a68ca1770815107a3edb54090282349ba7 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 19 Jan 2008 05:30:42 +0000 Subject: belongs_to supports :dependent => :destroy and :delete. Closes #10592. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8675 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activerecord/CHANGELOG | 2 ++ activerecord/lib/active_record/associations.rb | 19 +++++++++++++++++++ activerecord/test/cases/associations_test.rb | 19 ++++++++++++++++++- activerecord/test/cases/json_serialization_test.rb | 2 +- activerecord/test/fixtures/authors.yml | 2 ++ activerecord/test/models/author.rb | 14 +++++++++++++- activerecord/test/schema/schema.rb | 2 +- 7 files changed, 56 insertions(+), 4 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index c6c2f00309..ef21f5d1d3 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* belongs_to supports :dependent => :destroy and :delete. #10592 [Jonathan Viney] + * Introduce preload query strategy for eager :includes. #9640 [Frederick Cheung, Aleksey Kondratenko] * Support aggregations in finder conditions. #10572 [Ryan Kinderman] diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 4743d3be8f..3692be3aeb 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -792,6 +792,10 @@ module ActiveRecord # * :foreign_key - specify the foreign key used for the association. By default this is guessed to be the name # of the association with an +_id+ suffix. So a class that defines a +belongs_to :person+ association will use +person_id+ as the default +foreign_key+. # Similarly, +belongs_to :favorite_person, :class_name => "Person"+ will use a foreign key of +favorite_person_id+. + # * :dependent - if set to :destroy, the associated object is destroyed when this object is. If set to + # :delete, the associated object is deleted *without* calling its destroy method. This option should not be specified when + # belongs_to is used in conjunction with a has_many relationship on another class because of the potential to leave + # orphaned records behind. # * :counter_cache - caches the number of belonging objects on the associate class through the use of +increment_counter+ # and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's # destroyed. This requires that a column named #{table_name}_count (such as +comments_count+ for a belonging +Comment+ class) @@ -876,6 +880,8 @@ module ActiveRecord "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)" ) end + + configure_dependency_for_belongs_to(reflection) end # Associates two classes via an intermediate join table. Unless the join table is explicitly specified as @@ -1202,6 +1208,19 @@ module ActiveRecord end end + def configure_dependency_for_belongs_to(reflection) + if reflection.options.include?(:dependent) + case reflection.options[:dependent] + when :destroy + 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?'" + else + raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})" + end + end + end + def create_has_many_reflection(association_id, options, &extension) options.assert_valid_keys( :class_name, :table_name, :foreign_key, diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index b21f0cc9e2..301f8a6e2c 100755 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -470,7 +470,7 @@ end class HasManyAssociationsTest < ActiveSupport::TestCase fixtures :accounts, :companies, :developers, :projects, - :developers_projects, :topics, :authors, :comments + :developers_projects, :topics, :authors, :comments, :author_addresses def setup Client.destroyed_client_ids.clear @@ -995,6 +995,23 @@ class HasManyAssociationsTest < ActiveSupport::TestCase assert_equal 1, Client.find_all_by_client_of(firm.id).size end + def test_dependent_delete_and_destroy_with_belongs_to + author_address = author_addresses(:david_address) + assert_equal [], AuthorAddress.destroyed_author_address_ids[authors(:david).id] + + assert_difference "AuthorAddress.count", -2 do + authors(:david).destroy + end + + assert_equal [author_address.id], AuthorAddress.destroyed_author_address_ids[authors(:david).id] + end + + def test_invalid_belongs_to_dependent_option_raises_exception + assert_raises ArgumentError do + Author.belongs_to :special_author_address, :dependent => :nullify + end + end + def test_clearing_without_initial_access firm = companies(:first_firm) diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index 465c2454fc..779b8e02ad 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -151,7 +151,7 @@ class DatabaseConnectedJsonEncodingTest < ActiveSupport::TestCase def test_should_allow_except_option_for_list_of_authors authors = [@david, @mary] - assert_equal %([{"id": 1}, {"id": 2}]), authors.to_json(:except => [:name, :author_address_id]) + assert_equal %([{"id": 1}, {"id": 2}]), authors.to_json(:except => [:name, :author_address_id, :author_address_extra_id]) end def test_should_allow_includes_for_list_of_authors diff --git a/activerecord/test/fixtures/authors.yml b/activerecord/test/fixtures/authors.yml index f59b84fa45..de2ec7d38b 100644 --- a/activerecord/test/fixtures/authors.yml +++ b/activerecord/test/fixtures/authors.yml @@ -1,6 +1,8 @@ david: id: 1 name: David + author_address_id: 1 + author_address_extra_id: 2 mary: id: 2 diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index bf0c993c50..593d77342e 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -65,7 +65,8 @@ class Author < ActiveRecord::Base has_many :tags, :through => :posts # through has_many :through has_many :post_categories, :through => :posts, :source => :categories - belongs_to :author_address + belongs_to :author_address, :dependent => :destroy + belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress" attr_accessor :post_log @@ -101,6 +102,17 @@ end class AuthorAddress < ActiveRecord::Base has_one :author + + def self.destroyed_author_address_ids + @destroyed_author_address_ids ||= Hash.new { |h,k| h[k] = [] } + end + + before_destroy do |author_address| + if author_address.author + AuthorAddress.destroyed_author_address_ids[author_address.author.id] << author_address.id + end + true + end end class AuthorFavorite < ActiveRecord::Base diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 2d187a601a..307ad8b935 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -239,9 +239,9 @@ ActiveRecord::Schema.define do add_column :posts, :taggings_count, :integer, :default => 0 add_column :authors, :author_address_id, :integer + add_column :authors, :author_address_extra_id, :integer create_table :author_addresses, :force => true do |t| - t.column :author_address_id, :integer end create_table :author_favorites, :force => true do |t| -- cgit v1.2.3