Прерывание сброса Rails 5: как настроить сообщения об ошибках?

В Rails появился этот синтаксис throw(:abort), но как теперь получить осмысленные ошибки уничтожения?

Для ошибок проверки можно было бы сделать

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

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

Вот моя модель

class User

  before_destroy :destroy_validation,
    if: :some_reason

  private

  def destroy_validation
    throw(:abort) if some_condition
  end

person Cyril Duchon-Doris    schedule 28.07.2016    source источник


Ответы (2)


Вы можете использовать errors.add для своего метода класса.

Пользовательская модель:

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

Контроллер пользователей:

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
person Gonzalo S    schedule 10.11.2016
comment
Не совсем верно при уничтожении элемента из коллекции has_many - сообщение об ошибке не создается: post.comments.destroy(comment) не вызовет ошибок (по крайней мере, в консоли Rails), но элемент comment в этом случае не будет уничтожен. Предполагается, что у вас есть модель Post с отношением has_many комментариев, и у вас есть обратный вызов before_destroy, определенный в модели Post непосредственно перед объявленным отношением has_many. - person belgoros; 01.04.2018

Ответ Гонсало С прекрасно. Однако, если вам нужен более чистый код, вы можете рассмотреть вспомогательный метод. Следующий код лучше всего работает в Rails 5.0 или выше, поскольку вы можете использовать модель ApplicationRecord.

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

Теперь вы можете сделать:

class User < ApplicationRecord

  before_destroy(if: :condition) { 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
person 3limin4t0r    schedule 05.09.2017