最佳实践:在ruby中使用系统提供的或自定义异常来处理错误情况?

har*_*ald 12 ruby exception-handling

在ruby中编写一个相当简单的命令行工具我需要在命令行参数中报告有关错误的有意义的消息,或者在程序中报告其他错误条件.(未找到输入文件,输入格式无效等)

现在,我只是在检测参数列表中的错误时使用合理的描述引发ArgumentError.这是一种好的做法,还是冒着隐藏编程错误的风险?换句话说,系统定义的ruby异常是否适用于应用程序,或者我们是否应始终创建自己的异常以报告非系统错误?

编辑: 作为一个例子,如果我调用一个参数数量错误的方法,ruby会引发ArgumentError.这是一个编程错误,我想通过堆栈跟踪和所有来告诉我.但是,当我的程序输入不正确时,我可能想给用户一个简短的消息,或者甚至无声地忽略它.这告诉我,ArgumentError不适合应用程序自己使用.

Mar*_*mas 28

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

您可以使用其中一种,也可以创建自己的.在创建自己的时候,有几点需要注意.例如,rescue默认情况下只有救援StandardError及其后代.我发现这很困难.最好确保您的自定义错误继承自StandardError.(顺便说一句,为了解救任何异常,请rescue Exception明确使用.)

可以创建一个包含属性,自定义构造函数等的异常类,但标准做法是创建简单的定义:

class ProcessingError < RuntimeError; end
Run Code Online (Sandbox Code Playgroud)

您可以使用不同的错误消息区分特定错误,或创建错误层次结构.通常没有创建复杂的异常层次结构,或者至少我没有看到它的一个例子(我倾向于阅读我使用的库的来源).我所看到的是使用模块命名您的错误,我认为这是一个好主意.

module MyLibrary
  class Error < StandardError; end
  class ConnectionError < Error; end
end
Run Code Online (Sandbox Code Playgroud)

然后你的例外将是形式,MyLibrary::ConnectionError并特别是你可以从你的库中拯救错误rescue MyLibrary::Error并抓住它们.

注意:另一种语法是MyError = Class.new(RuntimeError)参见下面Steve Klabnik的博客文章.

参考文献:

精彩的阅读: Avdi Grimm的特殊Ruby

  • 而不是使用`class MyError <StandardError; 最后,Steve Klabnik最近在[他的博客](http://blog.steveklabnik.com/posts/2012-09-09-random-ruby-tricks--class-new)中指出你可以使用`MyError = Class .new(StandardError)`这实际上非常好,虽然在野外的Ruby代码中没有看到过. (3认同)

Jus*_*ake 15

我相信最好的做法是在模块中引发自己的自定义错误.所有特定的异常类都应该从一个继承自StandardError的命名空间异常继承.所以对于你的情况:

module MyApp
  class Error < StandardError; end
  class ArgumentError < Error; end
end
Run Code Online (Sandbox Code Playgroud)

并在用户提供错误参数时引发MyApp :: ArgumentError.这样它就可以区别于代码中的参数错误.您可以使用MyApp :: Error从您的应用程序中拯救任何未捕获的异常.

你也应该看看雷神.它可以为您处理大多数用户参数.您可以查看Bundler以获得良好的cli用法示例.