Skip to content

Commit

Permalink
fix: restore has_one with scope (#551)
Browse files Browse the repository at this point in the history
* test: simplify HasOneWithScope relation config
* test: add has_one restore by accident test case
* test: imporove has_one_with_scope_missed
  • Loading branch information
zygzagZ authored Mar 12, 2024
1 parent de74493 commit f68cb23
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 7 deletions.
19 changes: 12 additions & 7 deletions lib/paranoia.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
40 changes: 40 additions & 0 deletions test/paranoia_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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

0 comments on commit f68cb23

Please sign in to comment.