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

时间:2010-12-07 10:56:52

标签: ruby exception-handling

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

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

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

2 个答案:

答案 0 :(得分: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

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),请参阅下面对Steve Klabnik博客文章的引用。

参考文献:

精彩的阅读:    {4}由Avdi Grimm提出

答案 1 :(得分:15)

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

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

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

您还应该查看Thor。它可以为您处理大多数用户参数。您可以查看Bundler以获取良好的cli用法示例。