Skip to content

Commit

Permalink
Fix upsert all that restore soft-deleted record accidentally
Browse files Browse the repository at this point in the history
  • Loading branch information
mahdiar-naufal-shyftplan committed Nov 3, 2024
1 parent 7b96793 commit bc760a5
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 0 deletions.
26 changes: 26 additions & 0 deletions lib/paranoia.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ def paranoia_destroy_attributes
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 upsert_all(attributes, returning: nil, unique_by: nil)
return super unless ActiveRecord::VERSION::STRING >= "7.0"

insert_all = ActiveRecord::InsertAll.new(
self,
attributes,
on_duplicate: :update,
returning: returning,
unique_by: unique_by
)
insert_all.keys_including_timestamps.delete paranoia_column
insert_all.updatable_columns.delete paranoia_column
insert_all.execute
end
end

def paranoia_destroy
Expand Down Expand Up @@ -388,6 +403,17 @@ def deletion_time
paranoia_column_value.acts_like?(:time) ? paranoia_column_value : deleted_at
end
end

class ActiveRecord::InsertAll
private
if ActiveRecord::VERSION::STRING >= "7.0"
def verify_attributes(attributes)
if keys_including_timestamps != attributes.keys.excluding(model.paranoia_column).to_set
raise ArgumentError, "All objects being inserted must have the same keys"
end
end
end
end
end

require 'paranoia/rspec' if defined? RSpec
Expand Down
18 changes: 18 additions & 0 deletions test/paranoia_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,24 @@ def test_update_has_many_through_relation_delete_associations
assert_equal 2, employer.jobs.with_deleted.count
end

def test_upsert_all_on_soft_deleted_record
e1 = Employer.create(name: "e1")
e2 = Employer.create(name: "e2", deleted_at: Time.current)
assert_nil e1.deleted_at
assert e2.deleted_at != nil

Employer.upsert_all([
{ id: e1.id, name: "new_e1" },
{ id: e2.id, name: "new_e2" }
])

assert e1.reload.name == "new_e1"
assert e2.reload.name == "new_e2"

assert_nil e1.reload.deleted_at
assert e2.reload.deleted_at != nil
end

private
def get_featureful_model
FeaturefulModel.new(:name => "not empty")
Expand Down

0 comments on commit bc760a5

Please sign in to comment.