Rails 5 throw abort : how do I setup error messages?
Asked Answered
L

2

18

Rails has introduced this throw(:abort) syntax, but now how do I get meaningful destroy errors ?

For validation errors one would do

if not user.save
  # => user.errors has information

if not user.destroy
  # => user.errors is empty

Here is my model

class User

  before_destroy :destroy_validation,
    if: :some_reason

  private

  def destroy_validation
    throw(:abort) if some_condition
  end
Lamberto answered 28/7, 2016 at 0:28 Comment(0)
M
30

You can use errors.add for your class method.

User model:

def destroy_validation
  if some_condition
    errors.add(:base, "can't be destroyed cause x,y or z")
    throw(:abort)
  end
end

Users controller:

def destroy
  if @user.destroy
    respond_to do |format|
      format.html { redirect_to users_path, notice: ':)' }
      format.json { head :no_content }
    end
  else
    respond_to do |format|
      format.html { redirect_to users_path, alert: ":( #{@user.errors[:base]}"}
    end
  end
end
Monolayer answered 10/11, 2016 at 21:24 Comment(1)
Not totally true when destroying an element from a has_many collection, - there is no error message created: post.comments.destroy(comment) will raise no errors (at least in Rails console) but the comment element will not be destroyed in this case. It is supposed that you have a Post model to have has_many comments relation and you have before_destroy callback defined in Post model just before the has_many relation declared.Headgear
S
6

Gonzalo S answer is perfectly fine. However to clean up the coede you can consider a helper method. The following code works best in Rails 5.0 or higher since you can make use of the ApplicationRecord model.

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  private

  def halt(tag: :abort, attr: :base, msg: nil)
    errors.add(attr, msg) if msg
    throw(tag)
  end

end

Now you can do:

class User < ApplicationRecord

  before_destroy(if: :some_reason) { halt msg: 'Your message.' }

  # or if you have some longer condition:
  before_destroy if: -> { condition1 && condition2 && condition3 } do
    halt msg: 'Your message.'
  end

  # or more in lines with your example:
  before_destroy :destroy_validation, if: :some_reason
  
  private

  def destroy_validation
    halt msg: 'Your message.' if some_condition
  end

end
Salicylate answered 5/9, 2017 at 9:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.