Skip to content

Commit

Permalink
Wrap discard methods in a transaction
Browse files Browse the repository at this point in the history
This seeks to solve issue: jhawthorn#77

Given the situation where a `before/after` callback fails, particularly
with related records, we shouldn't still discard records.

Note: This is leveraging a private API that exists in
[ActiveRecord::Transactions](https://github.com/rails/rails/blob/cd2949d2d936daa89b7e23f816f1c004aee85461/activerecord/lib/active_record/transactions.rb#L345)
  • Loading branch information
thewatts committed May 5, 2022
1 parent 4d04eee commit 99e4e45
Showing 1 changed file with 28 additions and 5 deletions.
33 changes: 28 additions & 5 deletions lib/discard/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,15 @@ def undiscarded?
# @return [Boolean] true if successful, otherwise false
def discard
return false if discarded?
run_callbacks(:discard) do
update_attribute(self.class.discard_column, Time.current)

_in_discard_transaction do
run_callbacks(:discard) do
update_attribute(self.class.discard_column, Time.current)
end
end

rescue Exception => e
handle_transaction_rollback(e)
end

# Discard the record in the database
Expand All @@ -131,16 +137,21 @@ def discard
# @return [Boolean] true if successful
# @raise {Discard::RecordNotDiscarded}
def discard!
discard || _raise_record_not_discarded
with_transaction_returning_status do
discard || _raise_record_not_discarded
end
end

# Undiscard the record in the database
#
# @return [Boolean] true if successful, otherwise false
def undiscard
return unless discarded?
run_callbacks(:undiscard) do
update_attribute(self.class.discard_column, nil)

_in_discard_transaction do
run_callbacks(:undiscard) do
update_attribute(self.class.discard_column, nil)
end
end
end

Expand All @@ -158,6 +169,18 @@ def undiscard!

private

def _in_discard_transaction
previous_discard_value = public_send(self.class.discard_column)

with_transaction_returning_status do
yield
end
rescue Exception => exception
assign_attributes(self.class.discard_column => previous_discard_value)

raise exception
end

def _raise_record_not_discarded
raise ::Discard::RecordNotDiscarded.new("Failed to discard the record", self)
end
Expand Down

0 comments on commit 99e4e45

Please sign in to comment.