Allow :dependent options to be used with polymorphic joins. #3820 [Rick Olson]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3881 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
Rick Olson 2006-03-16 02:10:11 +00:00
parent bf39c39d88
commit 0859779d6f
6 changed files with 99 additions and 9 deletions

View File

@ -1,5 +1,11 @@
*SVN*
* Allow :dependent options to be used with polymorphic joins. #3820 [Rick Olson]
class Foo < ActiveRecord::Base
has_many :attachments, :as => :attachable, :dependent => :delete_all
end
* Upgrade to Transaction::Simple 1.3 [Jamis Buck]
* Catch FixtureClassNotFound when using instantiated fixtures on a fixture that has no ActiveRecord model [Rick Olson]

View File

@ -485,7 +485,7 @@ module ActiveRecord
if association.updated?
self["#{reflection.primary_key_name}"] = association.id
self["#{reflection.options[:foreign_type]}"] = ActiveRecord::Base.send(:class_name_of_active_record_descendant, association.class).to_s
self["#{reflection.options[:foreign_type]}"] = association.class.base_class.name.to_s
end
end
EOF
@ -811,13 +811,20 @@ module ActiveRecord
# See HasManyAssociation#delete_records. Dependent associations
# delete children, otherwise foreign key is set to NULL.
# Add polymorphic type if the :as option is present
dependent_conditions = %(#{reflection.primary_key_name} = \#{record.quoted_id})
if reflection.options[:as]
dependent_conditions += " AND #{reflection.options[:as]}_type = '#{base_class.name}'"
end
case reflection.options[:dependent]
when :destroy, true
module_eval "before_destroy '#{reflection.name}.each { |o| o.destroy }'"
when :delete_all
module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{reflection.primary_key_name} = \#{record.quoted_id})) }"
module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{dependent_conditions})) }"
when :nullify
module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL), %(#{reflection.primary_key_name} = \#{record.quoted_id})) }"
module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL), %(#{dependent_conditions})) }"
when nil, false
# pass
else

View File

@ -118,10 +118,11 @@ module ActiveRecord
def primary_key_name
return @primary_key_name if @primary_key_name
case macro
when :belongs_to
case
when macro == :belongs_to
@primary_key_name = options[:foreign_key] || class_name.foreign_key
when options[:as]
@primary_key_name = options[:foreign_key] || "#{options[:as]}_id"
else
@primary_key_name = options[:foreign_key] || active_record.name.foreign_key
end

View File

@ -69,13 +69,72 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
end
def test_create_polymorphic_has_many_with_scope
tagging = posts(:welcome).taggings.create(:tag => tags(:general))
old_count = posts(:welcome).taggings.count
tagging = posts(:welcome).taggings.create(:tag => tags(:misc))
assert_equal "Post", tagging.taggable_type
assert_equal old_count+1, posts(:welcome).taggings.count
end
def test_create_polymorphic_has_one_with_scope
tagging = posts(:welcome).tagging.create(:tag => tags(:general))
old_count = Tagging.count
tagging = posts(:welcome).tagging.create(:tag => tags(:misc))
assert_equal "Post", tagging.taggable_type
assert_equal old_count+1, Tagging.count
end
def test_delete_polymorphic_has_many_with_delete_all
assert_equal 1, posts(:welcome).taggings.count
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDeleteAll'
post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
old_count = Tagging.count
post.destroy
assert_equal old_count-1, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
def test_delete_polymorphic_has_many_with_destroy
assert_equal 1, posts(:welcome).taggings.count
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDestroy'
post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
old_count = Tagging.count
post.destroy
assert_equal old_count-1, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
def test_delete_polymorphic_has_many_with_nullify
assert_equal 1, posts(:welcome).taggings.count
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyNullify'
post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
old_count = Tagging.count
post.destroy
assert_equal old_count, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
def test_delete_polymorphic_has_one_with_destroy
assert posts(:welcome).tagging
posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneDestroy'
post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
old_count = Tagging.count
post.destroy
assert_equal old_count-1, Tagging.count
assert_nil posts(:welcome).tagging(true)
end
def test_delete_polymorphic_has_one_with_nullify
assert posts(:welcome).tagging
posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneNullify'
post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
old_count = Tagging.count
post.destroy
assert_equal old_count, Tagging.count
assert_nil posts(:welcome).tagging(true)
end
def test_has_many_with_piggyback
@ -139,4 +198,15 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
tagging.destroy
assert posts(:welcome, :reload)[:taggings_count].zero?
end
private
# create dynamic Post models to allow different dependency options
def find_post_with_dependency(post_id, association, association_name, dependency)
class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
Post.find(post_id).update_attribute :type, class_name
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
klass.set_table_name 'posts'
klass.send(association, association_name, :as => :taggable, :dependent => dependency)
klass.find(post_id)
end
end

View File

@ -18,7 +18,7 @@ class Post < ActiveRecord::Base
has_many :special_comments
has_and_belongs_to_many :categories
has_and_belongs_to_many :special_categories, :join_table => "categories_posts"
has_and_belongs_to_many :special_categories, :join_table => "categories_posts", :association_foreign_key => 'category_id'
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings

View File

@ -9,3 +9,9 @@ thinking_general:
tag_id: 1
taggable_id: 2
taggable_type: Post
fake:
id: 3
tag_id: 1
taggable_id: 1
taggable_type: FakeModel