Ruby on rails 在轨道上搬运多辆手推车
我想知道我的问题是否有一个较短的解决方案 我目前正在建立一个多商店系统,这意味着一个网站有不同的商店,你有一个开放的购物车为每个商店 此购物车可以由客人或用户拥有 购物车/订单仅应在添加项目时创建 以下定义放置在应用程序控制器中Ruby on rails 在轨道上搬运多辆手推车,ruby-on-rails,ruby-on-rails-4,Ruby On Rails,Ruby On Rails 4,我想知道我的问题是否有一个较短的解决方案 我目前正在建立一个多商店系统,这意味着一个网站有不同的商店,你有一个开放的购物车为每个商店 此购物车可以由客人或用户拥有 购物车/订单仅应在添加项目时创建 以下定义放置在应用程序控制器中 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)}
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
...
这似乎不是正确的方式
有什么建议吗?我不知道“shorter”这个词,但只要看一下你的代码,我就会提出一些建议 每次更改都要测试代码。希望您有单元和功能测试,如果没有,则检查浏览器中的预期行为 试着把你的逻辑分成合理的、小的、紧凑的单元,并用合理的名称。把它们变成方法。这样做时,您可能会发现一些问题或您可以优化的点。例如:
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
通过查看代码,您可以看到至少两个可以使用此方法的地方
请注意,没有返回语句。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行。你不必那么极端,但这样做很有用
最终,你会有一些读起来更像英语的东西,因此会更容易阅读,也更容易判断学生的行为。在这一点上,您可能会注意到更多的重复或不必要的、无法访问的死代码块。对两者都要无情
最后,整个事情看起来需要自己的类
# 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
然后它被很好地隐藏起来,并且可以从任何你可以传递给它的商店、订单和当前用户的地方使用。。。而且它不会弄乱你的控制器
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
要进一步阅读,请查看有关制作精简控制器、提取服务对象和Sandi Metz规则的博客文章。另外,请购买Sandi关于Ruby OO编程的书。你不认为用户无论买什么都应该拥有一个购物车吗?你的代码不可读,只是有太多的if-else语句。用户应该可以一次签出多个订单,在商店之间切换时,他应该看到这个商店的当前购物车,因为他也可以从一个商店签出一个订单。是的,我知道有很多if/else,这是因为我不知道如何解决它…仍然不明白有多个cart的意义。你有一个不同商店的网站,不是吗?作为一个用户,如果我有一个购物车,而不是有多个购物车,然后试图找出哪个购物车有哪些项目,我会更高兴:)一旦你重构了你的代码,你会有一个很好的,孤立的类,只对你传递给它的实体进行操作。这使得测试更加容易,因为您可以使用模拟对象来测试类的行为,而无需在控制器对象周围拖动或进行任何数据库查找。整洁,嗯?然后您只需要测试公共方法的行为。