如何将控制器代码重构为模型代码

时间:2014-11-10 01:59:54

标签: ruby-on-rails ruby refactoring

我已经使用了一段时间的导轨,我的控制器代码开始失控(没有双关语。)我听说你想要瘦的控制器和胖模型,对我来说这个没有& #39;在我的轨道进程中自然而然。

我发布了我的rails应用程序的一个模型。

line_item #create

def create
  @cart = current_cart
  #product is built from base_product, after finding associated product
  @base_product_id = params[:line_item][:base_product_id]
  get_product_from_base_product
  @line_item = @cart.line_items.build(
    :product_id => @product_id,
    :order_id => nil,
    :weight => params[:line_item][:weight],
    :quantity => params[:line_item][:quantity]
    )
  ## Does a line item with the same product_id already exist in cart?
  if @line_item.exists_in_collect?(current_cart.line_items)
    #if so, change quantity, check if there's enough stock
      if  current_cart.where_line_item_with(@product_id).update_quantity(@line_item.quantity) == true
        @line_item.destroy
        redirect_to base_products_path
        flash[:success] = "Detected Producted In Cart,  Added #{@line_item.quantity} More to Quantity"
      else 
        redirect_to cart_path
        flash[:failure] = "Cannot Add To Cart, We Only Have #{@line_item.product.stock_qty} In Stock"
      end
  else
    if @line_item.stock_check == true
      if @line_item.save
        respond_to do |format|
        format.html { redirect_to base_products_path, 
          :notice => "(#{@line_item.quantity}) #{@line_item.product.base_product.title} Added to Cart." }
        format.xml  { render :xml => @line_item,
          :status => :created, :location => @line_item }
        end
      else
        format.xml  { render :xml => @line_item.errors,
          :status => :unprocessable_entity }
      end
    else
      redirect_to base_products_path
      if @line_item.product.stock_qty > 0
      flash[:failure] = "Sorry! We Only Have #{@line_item.product.stock_qty} In Stock"
      else
      flash[:failure] = "Sorry! That Item Is Out Stock"
      end
    end 
  end
end

控制器方法:

private 
def get_product_from_base_product
  @product_id = Product.where(:base_product_id => @base_product_id).where(:size_id => params[:line_item][:size]).first.id
end

LineItem模型

class LineItem < ActiveRecord::Base
  belongs_to :product
  belongs_to :cart
  after_create :set_order_weight#, :set_package_dimentions
  after_update :set_order_weight#, :set_package_dimentions
  #max capactiy here
def have_enough_product?
  if self.product.stock_qty > self.quantity
    return true
  else
    self.quantity = self.quantity_was
    self.save
    return false
  end
end
def over_order_cap?
    if self.quantity > 50
        return true
    end
end
def update_quantity(qty)
  if self.have_enough_product? == true
  self.quantity += qty
  self.save
  end
end

def stock_check
  if self.product.stock_qty > self.quantity == true
    return true
  else
    return false
  end
end
def exists_in_collect?(items)
  items.each do |item|
    return true if self.product_id == item.product_id
  end
  return false
end

如您所见,这是一个有点大的控制器。这是正常的吗?所有的逻辑都在那里发生。用户可能已经在购物车中有此商品,此商品可能没有库存,此商品具有容量,并且用户正尝试订购更多商品。还有什么,我

我知道这不是一个典型的堆栈问题。什么都没有打破,但我对代码感觉不好。

我不想做一个&#39;作业&#39;问题,但我很欣赏有关如何使用模型重构控制器代码的建议。我很感激参考和建议。

谢谢! 更新添加了cart.rb

 class Cart < ActiveRecord::Base
   has_many :line_items#, dependent: :destroy
   has_one :order
     def where_line_item_with(prod_id)
       line_items.where(:product_id => prod_id).first
     end
     def sub_total
       self.line_items.to_a.sum { |item| item.total_price }
     end 
 end

1 个答案:

答案 0 :(得分:2)

我的回答不是因为我是专家,而是因为我最近才亲自经历过这个问题。对我而言,当我开始将我的应用程序放在测试套件下时,实现了。我在测试控制器操作时遇到了非常艰难的时间,因为他们做了很多不同的事情。

以下是我认为您应该将控制器的逻辑移动并分离为模型方法的几个原因:

  • 重复使用代码。我发现不是在不同的控制器中编写相同的代码3或4次,而是在模型中编写一次(并测试一次!)并在控制器中调用这些方法。

  • 测试。您可以在模型规范中测试一次(更容易在那里测试),而不是在3或4个控制器操作中测试相同的逻辑。它不仅可以节省您编写测试的时间,而且还可以消除您在控制器之间以不同的逻辑实现不同内容的可能性。

  • 可读性。说实话,我没有读完整个create行动,因为它不具备可读性。我有几个类似的动作,我讨厌尝试调试它们,因为很难跟踪发生的事情。将该逻辑移动到模型中(并将其分成更小,更具体的方法)使您可以专注于控制器应该做的事情(auth,指导,处理参数等)。

那么,如何重构呢?我在SO问题中读到了一些内容(现在无法找到),基本上说:如果逻辑处理资源的关联方式,它应该在模型中。我认为这是一个非常好的一般规则。我会从那里开始。如果你没有测试套件,我会同时添加它。