我想知道我的问题是否有更短的解决方案。
我目前正在建立一个多店铺系统,这意味着一个拥有不同商店的网站,每个商店都有一个开放式购物车。
此购物车可以来自访客或用户。
只有在添加项目时才能创建购物车/订单。
以下定义放在应用程序控制器中。
def find_order_by_shop(shop)
shop = Shop.find(shop.to_i) if !(shop.class == Shop)
if session["order_id_for_shop_#{shop.try(:id)}"]
order = Order.find_by_id_and_state(session["order_id_for_shop_#{shop.id}"],'open')
if order
if current_user
current_user.orders.where(:shop_id => shop.id , :state => 'open').where.not(:id => session["order_id_for_shop_#{shop.id}"]).delete_all
order.update_attribute(:user_id, current_user.id)
order = current_user.orders.where(:shop_id => shop.id , :state => 'open').first
if order
# delete all exept first
current_user.orders.where(:shop_id => shop.id , :state => 'open').where.not(:id => order.id).delete_all
else
# create new
order = current_user.orders.new(:shop_id => shop.id , :state => 'open')
end
end
else
order = Order.new(:shop_id => shop.id, :state => 'open')
end
else
if current_user
order = current_user.orders.where(:shop_id => shop.id , :state => 'open').first
if order
current_user.orders.where(:shop_id => shop.id , :state => 'open').where.not(:id => order.id).delete_all
else
order = current_user.orders.new(:shop_id => shop.id , :state => 'open')
end
else
# guest
order = Order.new(:shop_id => shop.id , :state => 'open')
end
end
session["order_id_for_shop_#{shop.try(:id)}"] = order.id
return order
end
在将商品添加到购物车/订单时更新会话
...
def create # add item to order
@order = find_order_by_shop(params[:shop_id].to_i)
@order.save # save cart
session["order_id_for_shop_#{params[:shop_id]}"] = @order.id
...
这似乎不是正确的轨道方式。
有什么建议吗?
答案 0 :(得分:1)
我不知道'更短',但只是看一下你的代码,我会提出一些建议。
您所做的每项更改都会测试代码。希望你有单元和功能测试,如果没有,那么在浏览器中检查预期的行为。
尝试将您的逻辑划分为合理,小型,紧凑的单位,并使用合理的名称。把它们变成方法。当您这样做时,您可能会发现一些可以优化的问题或点。例如:
if order
if current_user
current_user.orders.where(:shop_id => shop.id , :state => 'open').where.not(:id => session["order_id_for_shop_#{shop.id}"]).delete_all
order.update_attribute(:user_id, current_user.id)
order = current_user.orders.where(:shop_id => shop.id , :state => 'open').first
if order
# delete all exept first
current_user.orders.where(:shop_id => shop.id , :state => 'open').where.not(:id => order.id).delete_all
else
# create new
order = current_user.orders.new(:shop_id => shop.id , :state => 'open')
end
end
else
order = Order.new(:shop_id => shop.id, :state => 'open')
end
由于围绕整个块有一个'if order'条件,内部'if order'是多余的,整个'else'分支可以删除:
if order
if current_user
current_user.orders.where(:shop_id => shop.id , :state => 'open').where.not(:id => session["order_id_for_shop_#{shop.id}"]).delete_all
order.update_attribute(:user_id, current_user.id)
order = current_user.orders.where(:shop_id => shop.id , :state => 'open').first
# delete all except first
current_user.orders.where(:shop_id => shop.id , :state => 'open').where.not(:id => order.id).delete_all
end
else
order = Order.new(:shop_id => shop.id, :state => 'open')
end
现在,您可以将功能拆分为可重复使用的小块:
def order_from_shop(shop_id)
Order.new(:shop_id => shop_id, :state => 'open')
end
查看代码,您至少可以看到两个可以使用此方法的地方。
请注意,没有return语句。 Ruby / Rails'方式'是允许自动返回 - 在返回方法的最后一个语句的结果而不显式声明它。您可以将其应用于主方法的末尾:
...
session["order_id_for_shop_#{shop.try(:id)}"] = order.id
order
end
回到其余代码,开始提取更多方法,例如:
def user_order_from_shop(shop_id)
current_user.orders.where(:shop_id => shop.id , :state => 'open').first
end
也有一些地方可以使用它。
以小方法封装您的if
语句,格式为:
def method
if xxx
a_method
else
a_different_method
end
end
根据Sandi Metz的说法,任何方法都不应超过5行。你不必那么极端,但这样做很有用。
最终你会看到更像英语的东西,所以更容易阅读和确定一个glace的行为。那时你可能会注意到更多的重复或不必要的,无法访问的代码。对两者都无情。
最后,整个事情看起来需要它自己的类。
# app/services/shop_order_query.rb
class ShopOrderQuery
attr_accessor :shop, :order, :current_user
def initialize(shop, order, current_user)
self.shop = shop
self.order = order
self.current_user = current_user
end
def find_order_by_shop
...
...
end
private
# support methods for main look-up
def order_from_shop(shop_id)
...
end
...
...
end
然后用
调用它ShopOrderQuery.new(shop, order, current_user).find_order_by_shop
然后它完全隐藏起来,并且可以在任何可以通过商店,订单和当前用户的地方使用......并且它不会使控制器混乱。
如需进一步阅读,请查看有关制作瘦控制器,提取服务对象和Sandi Metz规则的博客文章。另外,购买Sandi关于Ruby OO编程的书。