我正在阅读使用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
由于我是一名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应用程序时,缺少Cart是完全正常的行为。)
如果有人拿到任何Java书籍,他们会说这是一件非常糟糕的事情 - 并且有充分的理由:错误处理不应该用作控制语句的替代品,这对于那些阅读的人来说有点误导代码。
有没有什么理由可以在Ruby(Rails)中证明这种做法是正确的?这是Ruby中的常见做法吗?
答案 0 :(得分:9)
Rails在使用异常时并不一致。如果没有找到对象,find
将引发异常,但是为了保存,您可以选择所需的行为。最常见的形式是:
if something.save
# formulate a reply
else
# formulate an error reply, or redirect back to a form, or whatever
end
即。 save
返回true或false。但也有save!
引发异常(在方法名称的末尾添加感叹号是一种Rubyism,用于显示方法是“危险的”,或者是破坏性的,或者仅仅是它有副作用,确切的含义取决于上下文)。
有一个正当理由说明find
引发异常的原因:如果RecordNotFound
异常冒泡到顶层,它将触发404页面的渲染。由于您通常不会手动捕获这些异常(在Rails应用程序中很少见到rescue ActiveRecord::RecordNotFound
),因此您可以免费获得此功能。但在某些情况下,您希望在对象不存在时执行某些操作,在这种情况下,您必须捕获异常。
我认为术语“最佳实践”实际上并不意味着任何东西,但根据我的经验,异常不再用于控制Ruby中的流程,而不是Java或我使用的任何其他语言。鉴于Ruby没有检查异常,你通常会处理异常。
最后归结为解释。由于find
的最常见用例是检索要显示它的对象,并且该对象的URL将由应用程序生成,因此很可能是无法找到该对象的特殊情况。这意味着应用程序正在生成指向不存在的对象的链接,或者用户已手动编辑URL。也可能是对象已被删除,但是它的链接仍然存在于缓存中,或者通过搜索引擎,我会说这也是一种特殊情况。
在您的示例中使用时,该参数适用于find
,即带有ID。还有其他形式的find
(包括许多find_by_*
变体)实际搜索,而那些不会引发异常(然后在Rails 3中有where
,它取代了许多在Rails中使用find
2)。
我并不是说使用异常作为流控制是一件好事,只是find
引发异常并不一定是错的,并且您的特定用例不是常见的情况
答案 1 :(得分:3)
对于特定用例,您只需执行
即可def current_cart
cart = Cart.find_or_create_by_id(session[:cart_id])
session[:cart_id] = cart.id
cart
end
这似乎会为它创建的新记录设置一个特定的id
,但由于id
始终是受保护的属性,因此不会为新记录设置它。您将获得具有指定id
的记录,或者如果不存在,则会获得具有新id
的新记录。
答案 2 :(得分:0)
我想我会做类似以下的事情(包括缓存当前购物车,以便每次调用该方法时都不会从数据库加载):
def current_cart
@current_cart ||= begin
unless cart = Cart.find_by_id(session[:cart_id])
cart = Cart.create
session[:cart_id] = cart.id
end
cart
end
end