首页 文章

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

提问于
浏览
12

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

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

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

2 回答

  • 28

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

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

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

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

  • 15

    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 并 grab 它们 .

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

    参考文献:

    尼克西格尔

    精彩的读物:Exceptional Ruby作者:Avdi Grimm

相关问题