Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 在轨道上搬运多辆手推车_Ruby On Rails_Ruby On Rails 4 - Fatal编程技术网

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的意义。你有一个不同商店的网站,不是吗?作为一个用户,如果我有一个购物车,而不是有多个购物车,然后试图找出哪个购物车有哪些项目,我会更高兴:)一旦你重构了你的代码,你会有一个很好的,孤立的类,只对你传递给它的实体进行操作。这使得测试更加容易,因为您可以使用模拟对象来测试类的行为,而无需在控制器对象周围拖动或进行任何数据库查找。整洁,嗯?然后您只需要测试公共方法的行为。