aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG6
-rwxr-xr-xactiverecord/lib/active_record/associations.rb13
-rw-r--r--activerecord/lib/active_record/reflection.rb7
-rw-r--r--activerecord/test/associations_join_model_test.rb74
-rw-r--r--activerecord/test/fixtures/post.rb2
-rw-r--r--activerecord/test/fixtures/taggings.yml6
6 files changed, 99 insertions, 9 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 1a2cd15458..0ef11609ee 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -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]
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index a098c0f326..5d9672a0d9 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -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
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 32e812a86a..e54e38e184 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -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
diff --git a/activerecord/test/associations_join_model_test.rb b/activerecord/test/associations_join_model_test.rb
index b14693d7a6..2d74895664 100644
--- a/activerecord/test/associations_join_model_test.rb
+++ b/activerecord/test/associations_join_model_test.rb
@@ -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
diff --git a/activerecord/test/fixtures/post.rb b/activerecord/test/fixtures/post.rb
index 7c1f8e1961..e5b95aff32 100644
--- a/activerecord/test/fixtures/post.rb
+++ b/activerecord/test/fixtures/post.rb
@@ -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
diff --git a/activerecord/test/fixtures/taggings.yml b/activerecord/test/fixtures/taggings.yml
index dced625580..38ec1ba5b1 100644
--- a/activerecord/test/fixtures/taggings.yml
+++ b/activerecord/test/fixtures/taggings.yml
@@ -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 \ No newline at end of file