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

标签 ruby-on-rails ruby exception-handling

我正在阅读使用 Rails 进行敏捷 Web 开发(第 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

由于我是一名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;    
  }
}

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

如果有人拿起任何 Java 书籍,他们都会说这是一件非常糟糕的事情 - 并且有一个很好的理由:错误处理不应该被用作控制语句的替代品,这对那些阅读的人来说有点误导代码。

这种做法在 Ruby (Rails) 中是否合理,有什么充分的理由吗?这是 Ruby 中的常见做法吗?

最佳答案

Rails 在使用异常方面绝不是一致的。 find如果未找到对象,将引发异常,但为了保存,您可以选择所需的行为。最常见的形式是这样的:

if something.save
  # formulate a reply
else
  # formulate an error reply, or redirect back to a form, or whatever
end

save返回真或假。但也有 save!这会引发异常(在方法名称末尾添加感叹号是一种 Rubyism,用于表明该方法是“危险的”或破坏性的,或者只是它具有副作用,确切的含义取决于上下文)。

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

我认为术语“最佳实践”实际上没有任何意义,但根据我的经验,与 Java 或我使用过的任何其他语言相比,Ruby 不再使用异常来控制流程。鉴于 Ruby 没有检查异常,一般情况下您处理异常的次数要少得多。

归根结底还是要靠解释。由于最常见的用例 find正在检索一个对象以显示它,并且该对象的 URL 将由应用程序生成,很可能是找不到该对象的异常情况。这意味着应用程序正在生成指向不存在的对象的链接,或者用户已经手动编辑了 URL。也可能是对象已被删除,但指向它的链接仍然存在于缓存中,或通过搜索引擎存在,我想说这也是一种异常(exception)情况。

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

我并不是说使用异常来控制流程是一件好事,只是说 find 不一定是错的引发异常,并且您的特定用例不是常见情况。

关于ruby-on-rails - 在 Ruby 或 Ruby on Rails 中使用异常控制流是一种好习惯吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4734512/

相关文章:

ruby-on-rails - 多个 has_many 通过关系

ruby-on-rails - 如何使用 Chef 预编译 Assets ?

javascript - 在 Ruby on Rails 中实现作业完成时的通知/警报弹出窗口

language-agnostic - 为什么要重新抛出异常?

ios - 一种忽略方法抛出的任何错误的优雅方法

ruby-on-rails - Rails - 无法获取 Rspec 单击链接以工作

ruby-on-rails - Rails - 如何在不触发 Net::SMTPFatalError 的情况下使用完整的电子邮件地址?

ruby - 运行 Rails 服务器时出现 asdf 消息 "No version set for command rails"

ruby - cucumber / capybara vs Selenium ?

Ruby - 数组、边界和引发异常