Лучшая практика: использование системных или пользовательских исключений для условий ошибок в ruby?
написание довольно простого инструмента командной строки в ruby мне нужно сообщить значимые сообщения об ошибках в аргументах командной строки или, если на то пошло, других условиях ошибки в программе. (Входной файл не найден, недопустимый формат ввода и т. д.)
пока я просто поднимаю ArgumentError с разумным описанием при обнаружении ошибок в списке аргументов. Это хорошая практика, или я рискую скрыть ошибки программирования также с этим подходом? Другими словами, система определенные исключения в ruby предназначены для использования приложений, или мы всегда должны создавать свои собственные исключения для сообщений о несистемных ошибках?
Edit: Например, ruby вызывает ArgumentError, если я вызываю метод с неправильным количеством аргументов. Это ошибка программирования, о которой я хочу рассказать со следами стека и всем остальным. Однако, когда ввод в мою программу неверен, я могу дать краткое сообщение пользователю или даже игнорировать его молча. Это наводит меня на мысль это ArgumentError не подходит для приложений собственного использования.
2 ответов
Я считаю, что лучшей практикой является создание собственной пользовательской ошибки, пространства имен в модуле. Все конкретные классы исключений должны наследовать одно исключение из пространства имен, наследуемое от StandardError. Поэтому для вашего случая:
module MyApp
class Error < StandardError; end
class ArgumentError < Error; end
end
и поднять MyApp:: ArgumentError, когда пользователь предоставляет плохие аргументы. Таким образом, он отличается от ошибки аргумента в вашем коде. И вы можете спасти любое необработанное исключение из своего приложения на высоком уровне с помощью MyApp:: Ошибка.
вы также должны проверить Тор. Он может обрабатывать большую часть аргументов пользователя для вас. Вы можете посмотреть упаковщик для хорошего примера использования cli.
иерархия исключений 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
явно.)
вы can создайте класс исключений с атрибутами, пользовательскими конструкторами и т. д. но стандартная практика состоит в том, чтобы просто создавать простые определения:
class ProcessingError < RuntimeError; end
вы можете различать определенные ошибки с различными сообщениями об ошибках или создавать иерархию ошибок. Создание разработки иерархии исключений обычно не выполняются, или, по крайней мере, я не видел примера этого (и я, как правило, читаю источник библиотек I использовать.) То, что я видел, - это использование модуля для пространства имен ваших ошибок, что я считаю хорошей идеей.
module MyLibrary
class Error < StandardError; end
class ConnectionError < Error; end
end
тогда ваши исключения будут иметь вид MyLibrary::ConnectionError
и для спасения ошибок из вашей библиотеки конкретно вы можете rescue MyLibrary::Error
и поймать их всех.
Примечание: альтернативный синтаксис MyError = Class.new(RuntimeError)
см. ссылку на сообщение в блоге Стива Клабника ниже.
ссылки:
- иерархия исключений Ruby Ник Зигер.
- поднятие Правого исключения Джеймис бак.
- Случайные Рубиновые Трюки: Класс.новый на руби как Стив клабник.
фантастическое чтение: Красивый Рубиновый Авди Гримм