Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Allow :dependent options to be used with polymorphic joins. #3820 [Ri…

…ck Olson]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3881 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 0859779d6f1cadfec12f1112c80382658466ab7d 1 parent bf39c39
risk danger olson technoweenie authored
6 activerecord/CHANGELOG
View
@@ -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]
13 activerecord/lib/active_record/associations.rb
View
@@ -485,7 +485,7 @@ def belongs_to(association_id, options = {})
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 @@ def configure_dependency_for_has_many(reflection)
# 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
7 activerecord/lib/active_record/reflection.rb
View
@@ -118,10 +118,11 @@ def table_name
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
74 activerecord/test/associations_join_model_test.rb
View
@@ -69,13 +69,72 @@ def test_set_polymorphic_has_one
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 @@ def test_belongs_to_polymorphic_with_counter_cache
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
2  activerecord/test/fixtures/post.rb
View
@@ -18,7 +18,7 @@ def find_most_recent
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
6 activerecord/test/fixtures/taggings.yml
View
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.