Где определить пользовательские типы ошибок в Ruby и / или Rails?

149

Есть ли лучший способ определения пользовательских типов ошибок в библиотеке Ruby (gem) или приложении Ruby on Rails? В частности:

  1. Где они принадлежат структурно в проекте? Отдельный файл, выделенный соответствующим определением модуля / класса, где-нибудь еще?
  2. Существует ли какое - либо соглашение , которые устанавливают , когда к и когда не создать новый тип ошибки?

В разных библиотеках разные способы работы, и я не заметил никаких реальных шаблонов. Некоторые библиотеки всегда используют пользовательские типы ошибок, в то время как другие не используют их вообще; у некоторых есть все ошибки, расширяющие StandardError, в то время как у других есть вложенные иерархии; некоторые - просто пустые определения классов, другие имеют все виды умных уловок.

Да, и просто потому, что я чувствую, что называть эти «типы ошибок», довольно двусмысленно, я имею в виду следующее:

class AuthenticationError < StandardError; end
class InvalidUsername < AuthenticationError; end
coreyward
источник

Ответы:

219

Для драгоценных камней

Я много раз видел, как вы определяете исключения таким образом:

gem_dir / Библиотека / gem_name / exceptions.rb

и определяется как:

module GemName

  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end

end

Примером этого может быть что-то вроде этого в httparty

Для Ruby on Rails

Поместите их в свою папку lib / в файл с именем exceptions.rb, который будет выглядеть примерно так:

module Exceptions
  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end
end

и вы бы использовали это так:

raise Exceptions::InvalidUsername
Майк Льюис
источник
Для драгоценных камней, похоже, вам, возможно, придется также включить файл исключений. Посмотрите этот пример, опять же httparty: github.com/jnunemaker/httparty/blob/…
Джейсон Светт
37
Зачем им пространство имен в Exceptionsмодуль?
ABMagil
13
Я думаю, что /libэто не место для ошибок. Они очень специфичны для приложений, и у меня сложилось впечатление, что код, который я вставляю, /libдолжен быть кодом, который можно использовать в других приложениях.
Вуливонг
1
Инструкции Ruby on Rails у меня не сработали - нужен ли какой-то дополнительный шаг для загрузки этого нового файла в типичном случае?
Meekohi
1
@ABMagil, кажется, мне придется иначе, Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define itдругой вариант - один класс на исключение, я думаю
ryan2johnson9
25

Я думаю, что для того, чтобы иметь согласованные исходные файлы в вашем проекте, вы должны определить ошибки в классе, в которые они могут быть брошены, и нигде больше.

Некоторая иерархия может быть полезной - пространства имен хороши для того, чтобы исключить избыточные строки из имен типов - но это скорее дело вкуса - не нужно зацикливаться, если в вашем приложении есть хотя бы один пользовательский тип исключения, который вы используете повсеместно для дифференциации между «преднамеренным» и «случайным» исключением.

Дин Рэдклифф
источник
8
Хотя в теории вы правы, что произойдет, когда разные классы могут совершить одну и ту же ошибку в совершенно разных ситуациях?
Алена
1
@Alain Почему бы не определить те ошибки, которые используются более чем одним классом в модуле Exceptions / Errors, но оставить все остальные, определенные в одном классе, который их использует?
Скотт W
@ ScottW, в этом случае мы надеемся, что разработчик не забудет проверить.
Джош Сен Жак
22

в рельсах вы можете сделать app/errorsкаталог

# app/errors/foo_error.rb
class FooError < StandardError; end

перезапустите Spring / сервер, и он должен забрать его

schpet
источник
Как я должен поднять эти исключения?
Нихил Ваг
@NikhilWagh либо raise FooError, "Example message..."илиraise FooError.new("Example message...")
schpet
13

Это старый вопрос, но я хотел поделиться тем, как я обрабатываю пользовательские ошибки в Rails, включая прикрепление сообщений об ошибках, тестирование и как справляться с этим с помощью ActiveRecordмоделей.

Создание пользовательской ошибки

class MyClass
  # create a custome error
  class MissingRequirement < StandardError; end

  def my_instance_method
    raise MyClass::MissingRequirement, "My error msg" unless true   
  end
end

Тестирование (минитест)

test "should raise MissingRequirement if ____ is missing"
  # should raise an error
  error = assert_raises(MyClass::MissingRequirement) {
    MyClass.new.my_instance_method
  }

  assert error.message = "My error msg"
end

С ActiveRecord

Я думаю, что стоит отметить, что если вы работаете с ActiveRecordмоделью, популярным шаблоном является добавление в модель ошибки, как описано ниже, так что ваши проверки не пройдут:

def MyModel < ActiveRecord::Base
  validate :code_does_not_contain_hyphens

  def code_does_not_contain_hyphens
    errors.add(:code, "cannot contain hyphens") if code.include?("-")
  end
end

Когда проверки выполняются, этот метод будет добавлен в ActiveRecord::RecordInvalidкласс ошибок ActiveRecord и приведет к сбою проверок.

Надеюсь это поможет!

Matt
источник
9

Чтобы гарантировать, что автозагрузка работает в Rails 4.1.10 должным образом для нескольких пользовательских классов ошибок, вы должны указать отдельные файлы для каждого. Это должно работать в процессе разработки с его динамической перезагрузкой.

Вот как я устанавливаю ошибки в недавнем проекте:

В lib/app_name/error/base.rb

module AppName
    module Error
        class Base < StandardError; end
    end
end

и в последующих пользовательских ошибках, как в lib/app_name/error/bad_stuff.rb

module AppName
    module Error
        class BadStuff < ::AppName::Error::Base; end
    end
end

После этого вы сможете вызвать ваши ошибки через:

 raise AppName::Error::BadStuff.new("Bad stuff just happened")
spyle
источник
И если вам не нужен отдельный файл для каждой новой ошибки, просто вставьте их всеlib/app_name/error.rb
jlhonora
Получение uninitialized constant MyController::AppName. Я
вызываю