Подтвердить что ты не робот

Лучшая практика: использование системных или пользовательских исключений для условий ошибок в рубине?

Написание довольно простого инструмента командной строки в ruby ​​Мне нужно сообщать о значимых сообщениях об ошибках в аргументах командной строки или, что то же самое, о других условиях ошибки в программе. (Входной файл не найден, неверный формат ввода и т.д.)

На данный момент я просто поднимаю ArgumentError с разумным описанием при обнаружении ошибок в списке аргументов. Является ли это хорошей практикой, или я рискую скрывать ошибки программирования, а также с этим подходом? Другими словами, являются ли определенные в системе исключениями в рубине, предназначенными для использования приложения, или мы всегда должны создавать собственные исключения для сообщения о несистемных ошибках?

Edit: Например, ruby ​​вызывает ArgumentError, если я вызываю метод с неправильным числом аргументов. Это ошибка программирования, о которой я хочу рассказать со стеком и всеми остальными. Однако, когда вход в мою программу неверен, я могу дать краткое сообщение пользователю или даже игнорировать его молча. Это говорит мне о том, что ArgumentError не подходит для собственных приложений.

4b9b3361

Ответ 1

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

module MyApp
  class Error < StandardError; end
  class ArgumentError < Error; end
end

и поднять MyApp:: ArgumentError, когда пользователь предоставляет плохие аргументы. Таким образом, он отличается от ошибки аргумента в вашем коде. И вы можете спасти любое неперехваченное исключение из своего приложения на высоком уровне с помощью MyApp:: Error.

Вы также можете проверить Thor. Он может обрабатывать большинство аргументов пользователя для вас. Вы можете посмотреть Bundler для хорошего примера использования cli.

Ответ 2

Иерархия исключений Ruby 1.9 выглядит следующим образом:

Exception
+- NoMemoryError
+- ScriptError
|  +- LoadError
|  +- NotImplementedError
|  +- SyntaxError
+- SignalException
|  +- Interrupt
+- StandardError
|  +- ArgumentError
|  +- IOError
|  |  +- EOFError
|  +- IndexError
|  +- LocalJumpError
|  +- NameError
|  |  +- NoMethodError
|  +- RangeError
|  |  +- FloatDomainError
|  +- RegexpError
|  +- RuntimeError
|  +- SecurityError
|  +- SystemCallError
|  +- SystemStackError
|  +- ThreadError
|  +- TypeError
|  +- ZeroDivisionError
+- SystemExit
+- fatal

Иерархия исключений Ruby 2:

Exception
+- NoMemoryError
+- ScriptError
|  +- LoadError
|  +- NotImplementedError
|  +- SyntaxError
+- SecurityError
+- SignalException
|  +- Interrupt
+- StandardError # default for rescue
|  +- ArgumentError
|  |  +- UncaughtThrowError
|  +- EncodingError
|  +- FiberError
|  +- IOError
|  |  +- EOFError
|  +- IndexError
|  |  +- KeyError
|  |  +- StopIteration
|  +- LocalJumpError
|  +- NameError
|  |  +- NoMethodError
|  +- RangeError
|  |  +- FloatDomainError
|  +- RegexpError
|  +- RuntimeError # default for raise
|  +- SystemCallError
|  |  +- Errno::*
|  +- ThreadError
|  +- TypeError
|  +- ZeroDivisionError
+- SystemExit
+- SystemStackError
+- fatal # impossible to rescue

Вы можете использовать один из них, как есть, или создать свой собственный. При создании своего, есть несколько вещей, чтобы отметить. Во-первых, rescue по умолчанию только спасает StandardError и его потомков. Я нашел это с трудом. Лучше всего убедиться, что ваши пользовательские ошибки наследуются от StandardError. (Кстати, чтобы спасти любое исключение, используйте rescue Exception явно.)

Вы можете создать класс исключения с атрибутами, настраиваемыми конструкторами и т.д., но стандартная практика состоит в том, чтобы просто создавать простые определения:

class ProcessingError < RuntimeError; end

Вы можете различать определенные ошибки с различными сообщениями об ошибках или создавать иерархию ошибок. Создание сложных иерархий исключений, как правило, не выполняется, или, по крайней мере, я не видел его примера (и я, как правило, читаю источник библиотек, которые я использую). То, что я видел, - это использование модуля для пространственных имен ваших ошибок, которые, по моему мнению, являются хорошей идеей.

module MyLibrary
  class Error < StandardError; end
  class ConnectionError < Error; end
end

Тогда ваши исключения будут иметь форму MyLibrary::ConnectionError и для спасения ошибок из вашей библиотеки, вы можете rescue MyLibrary::Error и поймать их все.

Примечание: альтернативный синтаксис MyError = Class.new(RuntimeError) см. ссылку на сообщение блога Стив Клабника ниже.

Ссылки:

Сказочное чтение:  Исключительный Ruby от Avdi Grimm