在Ruby或Ruby on Rails中使用控制流的异常是一种好习惯吗?

Pio*_*ski 14 ruby exception-handling ruby-on-rails

我正在阅读使用Rails的Agile Web Development(第4版),我发现了以下代码

class ApplicationController < ActionController::Base
  protect_from_forgery

  private

  def current_cart
    Cart.find(session[:cart_id])
  rescue ActiveRecord::RecordNotFound
    cart = Cart.create
    session[:cart_id] = cart.id
    cart
  end
end
Run Code Online (Sandbox Code Playgroud)

由于我是一名Java开发人员,因此我对这部分代码的理解或多或少如下:

private Cart currentCard(){
  try{
    return CartManager.get_cart_from_session(cartId)
  }catch(RecordNotFoundEx e){
    Cart c = CartManager.create_cart_and_add_to_session(new Cart())
    return c;    
  }
}
Run Code Online (Sandbox Code Playgroud)

令我印象深刻的是,异常处理用于控制正常的应用程序流(当用户第一次访问Depot应用程序时,缺少Cart是完全正常的行为).

如果有人拿到任何Java书籍,他们会说这是一件非常糟糕的事情 - 并且有充分理由:错误处理不应该用作控制语句的替代品,这对于那些阅读代码的人来说是一种误导.

有没有什么理由可以在Ruby(Rails)中证明这种做法是正当的?这是Ruby中的常见做法吗?

The*_*heo 9

Rails在使用异常时并不一致.find如果没有找到对象,则会引发异常,但是为了保存,您可以选择所需的行为.最常见的形式是:

if something.save
  # formulate a reply
else
  # formulate an error reply, or redirect back to a form, or whatever
end
Run Code Online (Sandbox Code Playgroud)

save返回true或false.但是也有save!一个异常(在方法名称的末尾添加一个感叹号是一个Rubyism来表示方法是"危险的",或者是破坏性的,或者仅仅是它有副作用,确切含义取决于上下文).

但是有一个正当理由可以find引发异常:如果RecordNotFound异常冒泡到顶层,它将触发404页面的渲染.由于您通常不会手动捕获这些异常(rescue ActiveRecord::RecordNotFound在Rails应用程序中很少见到),因此您可以免费获得此功能.但在某些情况下,您希望在对象不存在时执行某些操作,并且在这些情况下您必须捕获异常.

我不认为术语"最佳实践"实际上意味着什么,但我的经验是,不再使用异常来控制Ruby中的流程,而不是Java或我使用过的任何其他语言.鉴于Ruby没有检查异常,您通常会处理异常.

最后,它归结为解释.由于最常见的用例find是检索对象以显示它,并且该对象的URL将由应用程序生成,因此很可能是无法找到该对象的特殊情况.这意味着应用程序正在生成指向不存在的对象的链接,或者用户已手动编辑URL.也可能是对象已被删除,但是它的链接仍然存在于缓存中,或者通过搜索引擎,我会说这也是一种特殊情况.

该参数适用于find在您的示例中使用时,即使用ID.还有其他形式的find(包括许多find_by_*变体)实际搜索,并且不会引发异常(然后where在Rails 3中,它取代了findRails 2中的许多用法).

我并不是说使用异常作为流控制是一件好事,只是find提出异常并不一定是错误的,并且您的特定用例不是常见的情况.