From f68cb236a01209ccbe91430341ae5846b933cdeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chary=C5=82o?= Date: Tue, 12 Mar 2024 12:08:12 +0100 Subject: [PATCH] fix: restore has_one with scope (#551) * test: simplify HasOneWithScope relation config * test: add has_one restore by accident test case * test: imporove has_one_with_scope_missed --- lib/paranoia.rb | 19 ++++++++++++------- test/paranoia_test.rb | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/paranoia.rb b/lib/paranoia.rb index 05f15b08..7024c83c 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -210,6 +210,16 @@ def timestamp_attributes_with_current_time timestamp_attributes_for_update_in_model.each_with_object({}) { |attr,hash| hash[attr] = current_time_from_proper_timezone } end + def paranoia_find_has_one_target(association) + association_foreign_key = association.options[:through].present? ? association.klass.primary_key : association.foreign_key + association_find_conditions = { association_foreign_key => self.id } + association_find_conditions[association.type] = self.class.name if association.type + + scope = association.klass.only_deleted.where(association_find_conditions) + scope = scope.merge(association.scope) if association.scope + scope.first + end + # restore associated records that have been soft deleted when # we called #destroy def restore_associated_records(recovery_window_range = nil) @@ -233,13 +243,8 @@ def restore_associated_records(recovery_window_range = nil) end if association_data.nil? && association.macro.to_s == "has_one" - association_class = association.klass - if association_class.paranoid? - association_foreign_key = association.options[:through].present? ? association.klass.primary_key : association.foreign_key - association_find_conditions = { association_foreign_key => self.id } - association_find_conditions[association.type] = self.class.name if association.type - - association_class.only_deleted.where(association_find_conditions).first + if association.klass.paranoid? + paranoia_find_has_one_target(association) .try!(:restore, recursive: true, :recovery_window_range => recovery_window_range) end end diff --git a/test/paranoia_test.rb b/test/paranoia_test.rb index 86cc8748..14be4d0e 100644 --- a/test/paranoia_test.rb +++ b/test/paranoia_test.rb @@ -54,6 +54,7 @@ def setup! 'empty_paranoid_models' => 'deleted_at DATETIME', 'paranoid_has_one_throughs' => 'paranoid_has_through_restore_parent_id INTEGER NOT NULL, empty_paranoid_model_id INTEGER NOT NULL, deleted_at DATETIME', 'paranoid_has_many_throughs' => 'paranoid_has_through_restore_parent_id INTEGER NOT NULL, empty_paranoid_model_id INTEGER NOT NULL, deleted_at DATETIME', + 'paranoid_has_one_with_scopes' => 'deleted_at DATETIME, kind STRING, paranoid_has_one_with_scope_id INTEGER', }.each do |table_name, columns_as_sql_string| ActiveRecord::Base.connection.execute "CREATE TABLE #{table_name} (id INTEGER NOT NULL PRIMARY KEY, #{columns_as_sql_string})" end @@ -1223,6 +1224,37 @@ def test_counter_cache_column_on_restore end end + def test_has_one_with_scope_missed + parent = ParanoidHasOneWithScope.create + gamma = ParanoidHasOneWithScope.create(kind: :gamma, paranoid_has_one_with_scope: parent) # this has to be first + alpha = ParanoidHasOneWithScope.create(kind: :alpha, paranoid_has_one_with_scope: parent) + beta = ParanoidHasOneWithScope.create(kind: :beta, paranoid_has_one_with_scope: parent) + + parent.destroy + assert !gamma.reload.destroyed? + gamma.destroy + assert_equal 0, ParanoidHasOneWithScope.count # all destroyed + parent.reload # we unload associations + parent.restore(recursive: true) + + assert_equal "alpha", parent.alpha&.kind, "record was not restored" + assert_equal "beta", parent.beta&.kind, "record was not restored" + assert_nil parent.gamma, "record was incorrectly restored" + end + + def test_has_one_with_scope_not_restored + parent = ParanoidHasOneWithScope.create + gamma = ParanoidHasOneWithScope.create(kind: :gamma, paranoid_has_one_with_scope: parent) + parent.destroy + assert_equal 1, ParanoidHasOneWithScope.count # gamma not deleted + gamma.destroy + parent.reload # we unload associations + parent.restore(recursive: true) + + assert gamma.reload.deleted?, "the record was incorrectly restored" + assert_equal 1, ParanoidHasOneWithScope.count # gamma deleted + end + private def get_featureful_model FeaturefulModel.new(:name => "not empty") @@ -1627,3 +1659,11 @@ class ParanoidHasManyThrough < ActiveRecord::Base belongs_to :paranoid_has_through_restore_parent belongs_to :empty_paranoid_model, dependent: :destroy end + +class ParanoidHasOneWithScope < ActiveRecord::Base + acts_as_paranoid + has_one :alpha, -> () { where(kind: :alpha) }, class_name: "ParanoidHasOneWithScope", dependent: :destroy + has_one :beta, -> () { where(kind: :beta) }, class_name: "ParanoidHasOneWithScope", dependent: :destroy + has_one :gamma, -> () { where(kind: :gamma) }, class_name: "ParanoidHasOneWithScope" + belongs_to :paranoid_has_one_with_scope +end