From ce28b2ef3c7790ebbe28f44b29cc7ffd8d15713a Mon Sep 17 00:00:00 2001 From: Benjamin Alexander Date: Thu, 6 Feb 2025 08:51:21 +0000 Subject: [PATCH 1/2] Fix really_destroy used against a has_one relationship that is soft deleted --- lib/paranoia.rb | 3 +++ test/paranoia_test.rb | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/paranoia.rb b/lib/paranoia.rb index 93bf9730..e44e82ed 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -182,6 +182,9 @@ def really_destroy!(update_destroy_attributes: true) association_data = self.send(name) # has_one association can return nil # .paranoid? will work for both instances and classes + if association_data.nil? && reflection.has_one? && reflection.klass.paranoid? + association_data = reflection.klass.only_deleted.find_by(reflection.foreign_key => self.id) + end next unless association_data && association_data.paranoid? if reflection.collection? next association_data.with_deleted.find_each { |record| diff --git a/test/paranoia_test.rb b/test/paranoia_test.rb index be364e5b..faebc953 100644 --- a/test/paranoia_test.rb +++ b/test/paranoia_test.rb @@ -1419,6 +1419,16 @@ def test_update_has_many_through_relation_delete_associations assert_equal 2, employer.jobs.with_deleted.count end + def test_really_destroy_against_soft_deleted_object_with_has_one_association + model = ParanoidModelWithHasOne.create(paranoid_model_with_belong: ParanoidModelWithBelong.create) + assert_equal 1, ParanoidModelWithBelong.with_deleted.count + model.destroy + assert_equal 1, ParanoidModelWithBelong.with_deleted.count + model.reload + model.really_destroy! + assert_equal 0, ParanoidModelWithBelong.with_deleted.count # I think this should fail. + end + private def get_featureful_model FeaturefulModel.new(:name => "not empty") From 16ebd8220957752b2ddd57447dbdcf40eb737f10 Mon Sep 17 00:00:00 2001 From: Benjamin Alexander Date: Thu, 13 Mar 2025 17:04:54 +0000 Subject: [PATCH 2/2] fix: make previous fix work for polymorphic relationships --- lib/paranoia.rb | 4 +++- test/paranoia_test.rb | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/paranoia.rb b/lib/paranoia.rb index e44e82ed..8b214ff8 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -183,7 +183,9 @@ def really_destroy!(update_destroy_attributes: true) # has_one association can return nil # .paranoid? will work for both instances and classes if association_data.nil? && reflection.has_one? && reflection.klass.paranoid? - association_data = reflection.klass.only_deleted.find_by(reflection.foreign_key => self.id) + query = { reflection.foreign_key => self.id } + query[reflection.type] = self.class.name if reflection.options[:as] # Handle polymorphic associations + association_data = reflection.klass.only_deleted.find_by(query) end next unless association_data && association_data.paranoid? if reflection.collection? diff --git a/test/paranoia_test.rb b/test/paranoia_test.rb index faebc953..67a92c27 100644 --- a/test/paranoia_test.rb +++ b/test/paranoia_test.rb @@ -1426,7 +1426,21 @@ def test_really_destroy_against_soft_deleted_object_with_has_one_association assert_equal 1, ParanoidModelWithBelong.with_deleted.count model.reload model.really_destroy! - assert_equal 0, ParanoidModelWithBelong.with_deleted.count # I think this should fail. + assert_equal 0, ParanoidModelWithBelong.with_deleted.count + end + + def test_really_destroy_against_soft_deleted_object_with_polymorphic_has_one_association + unrelated_model = PolymorphicModel.create(parent_id: 1, parent_type: 'AModel') + model = ParentModel.create(id: 1, polymorphic_model: PolymorphicModel.create) + unrelated_model.destroy # means that later on in the test, there will be 2 deleted polymorphic models with parent_id of 1 + + assert_equal 2, PolymorphicModel.with_deleted.count + model.destroy + assert_equal 2, PolymorphicModel.with_deleted.count + model.reload + model.really_destroy! + assert_equal 1, PolymorphicModel.with_deleted.count + assert_equal 'AModel', PolymorphicModel.with_deleted.first.parent_type end private