Foton
Foton

Reputation: 1287

How to work with result of Active Record `destroy` in `around_destroy` callback

I have Product which has many product_variants. And ProductVariant has many document_items. It is forbidden to destroy product_variant if it's document_items are not empty. And when I destroy product, I want to delete all variants or nothing.

This setup works

class Product
  has_many :product_variant, dependent: :destroy, inverse_of: :product 
end

class ProductVariant
  has_many :document_items, dependent: :restrict_with_error, inverse_of: :product_variant 
end

If there is document_item binded to product_variant of product.

  pv = product.product_variants.first
  pv.destroy # => false, 
  pv.errors.messages #=> { base: ['Cannot delete variant, because there is dependent document_item'] })

  product.destroy # => false
  product.errors.messages # => []

BUT. I would like to add some clarifying errors to product. There are two ways:

1) overwrite destroy method:

  def destroy
    destroy_result = super
    copy_errors_from_associations if destroy_result == false
    destroy_result
  end

which works as expected, but Rubocop says

Rails/ActiveRecordOverride: Use before_destroy, around_destroy, or after_destroy callbacks instead of overriding the Active Record method destroy.

2) using around_destroy:

around_destroy :propagate_errors_from_associations_on_failed_destroy

def propagate_errors_from_associations_on_failed_destroy
  destroy_result = yield
  copy_errors_from_associations if destroy_result == false
end

Which copy no error. It seems that run of propagate_errors_from_associations_on_failed_destroy return immediately on first line, if yield returns false (actual deletion is aborted)

So I would like to know how I should setup the around_destroy callback to check result of destroy and if it is false, do something with errors.

Upvotes: 0

Views: 315

Answers (1)

Daniel Marchlík
Daniel Marchlík

Reputation: 21

yield does not returns false when deleting failed. It throws ActiveRecord::RecordNotDestroyed exception.

Rescuing from exception should work.

def propagate_errors_from_associations_on_failed_destroy
  yield
rescue ActiveRecord::RecordNotDestroyed => e
  copy_errors_from_associations
  raise e
end

Upvotes: 2

Related Questions