Tony Beninate
Tony Beninate

Reputation: 1985

before_destroy callback in Rails 5 throw(:abort) but also modify record

In Rails 5, I believe the only way to prevent destroy is to call throw(:abort); and this is working as expected. The issue I am having, however, is if the record does not meet requirements, rather than destroying it I want to modify the record. However with throw(:abort) the entire transaction is reverted, undoing any changes the record has received.

Any suggestions on how to achieve this?

class Thing < ApplicationRecord

  before_destroy :can_destroy?

  private

  def can_destroy?
    if model.something?
      self.update(something: 'foo') # This part is not being retained.
      throw(:abort)
  end
end

Upvotes: 2

Views: 748

Answers (1)

thesecretmaster
thesecretmaster

Reputation: 1984

The problem that you're encountering is that ActiveRecord wraps all of your before/after callbacks in transactions to ensure that if any of them fail, no results will be committed to the database. Sadly, you want to do exactly what it's trying to protect you from. There isn't really a "good" way of doing this (that I'm aware of) but a bit of a hack to get around this restriction would be to do your update in an after_rollback callback, because your throw(:abort) will cause a transaction rollback. For example:

class Thing < ApplicationRecord

  before_destroy :can_destroy?
  after_rollback :can_destroy_callbacks
  private

  def can_destroy?
    if model.something?
      @update_later = true
      throw(:abort)
    end
  end

  def can_destroy_callbacks
    if @update_later
      update(something: 'foo')
    end
  end
end

Upvotes: 1

Related Questions