我的服务对象模块中的未定义方法

时间:2018-10-16 14:56:06

标签: ruby-on-rails ruby

我想清理和重构订单控制器中的create方法代码,我读到使用服务对象是一种好习惯。从这里的可怕代码开始:

def create
  if current_user.orders.where(paid: false).present?
    order = current_user.orders.last
    order_id = order.id
    product_id = @product.id
    @product.ordinable = false
    @product.save
    order_amount = order.amount
    if order.products << @product
      order.products.each do |x|
        @order_amountnew = order_amount + x.price
      end
      order.amount = @order_amountnew
      order.save
      respond_to do |format|
        format.html { redirect_to products_path, notice: 'Product added to the cart!' }
      end
    else
      respond_to do |format|
        format.html { redirect_to products_path, notice: 'There was a problem while adding the product to the cart!' }
      end
    end
  else
    product_id = @product.id
    order = current_user.orders.new
    order.save
    order_id = order.id
    @product.ordinable = false
    @product.save
    order_amount = order.amount
    if order.products << @product
      order.products.each do |x|
        @order_amountnew = order_amount + x.price
      end
      order.amount = @order_amountnew
      order.save
      respond_to do |format|
        format.html { redirect_to products_path, notice: 'Product added to the cart!' }
      end
      OrderPaidCheckJob.set(wait: 3.minutes).perform_later(order_id)
    else
      respond_to do |format|
        format.html { redirect_to products_path, notice: 'There was a problem while adding the product to the cart!' }
      end
    end
  end
end

我想将方法​​基本上分为两部分,因此我在services文件夹中创建了两个模块,如下所示。 我叫第一个order_present_create_service

module OrderPresentCreateService
  class << self
    def create(params)
      order = current_user.orders.last
      order_id = order.id
      product_id = @product.id
      @product.ordinable = false
      @product.save
      order_amount = order.amount
      if order.products << @product
        order.products.each do |x|
          @order_amountnew = order_amount + x.price
        end
        order.amount = @order_amountnew
        order.save
        respond_to do |format|
          format.html { redirect_to products_path, notice: 'Product added to the cart!' }
        end
      else
        respond_to do |format|
          format.html { redirect_to products_path, notice: 'There was a problem while adding the product to the cart!' }
        end
      end
    end
  end
end

然后我打电话给第二个order_new_create_service

module OrderNewCreateService
  class << self
    def create(params)
      product_id = params[:id]
      order = current_user.orders.new
      order.save
      order_id = order.id
      @product.ordinable = false
      @product.save
      order_amount = order.amount
      if order.products << @product
        order.products.each do |x|
          @order_amountnew = order_amount + x.price
        end
        order.amount = @order_amountnew
        order.save
        respond_to do |format|
          format.html { redirect_to products_path, notice: 'Product added to the cart!' }
        end
        OrderPaidCheckJob.set(wait: 3.minutes).perform_later(order_id)
      else
        respond_to do |format|
          format.html { redirect_to products_path, notice: 'There was a problem while adding the product to the cart!' }
        end
      end
    end
  end
end

这是我的新控制器:

def create
  if current_user.orders.where(paid: false).present?
    OrderPresentCreateService.create(params)
  else
    OrderNewCreateService.create(params)
  end
end

我只是按照本文here进行操作。 当我尝试创建订单时,现在出现此错误:

  

OrderNewCreateService:Module的未定义局部变量或方法“ current_user”

在一开始,我在product_id = @ product.id时遇到了类似的错误,因此我在product_id = params [:id]中进行了更改,并使其以某种方式起作用。我在哪里做错了?

2 个答案:

答案 0 :(得分:1)

如果我理解正确,那么任务就是从控制器重构原始动作。您的重构代码中列出了一系列问题,因此,为了使答案更短,让我跳过对其进行复审,而专注于原始操作。

我看到的问题是:

  • product_id变量已初始化但未使用
  • order_id条件的第一个分支中的
  • if变量已初始化但未使用
  • respond_to块仅具有html格式的条件。由于没有其他格式的代码,因此可以简化
  • OrderPaidCheckJob.set方法放置在redirect_to之后,因此将永远不会调用它。要使其正常工作,请将其放在redirect_to
  • 之前
  • 尽管有if的条件,但操作以redirect_to结束相同的路径,只有notice发生了变化。因此,您可以直接设置notice,然后将redirect_toif中移出
  • @product.ordinable = false; @product.save可以简化为@product.update(ordinable: false)。由于它位于if的两个分支中,因此可以将其移出该块。
  • 不确定为什么要if order.products << @product。您何时认为这种情况是假的?如果将其添加到数据库时出现问题,它将引发异常。
  • order.products.each do |x|等块可以简化为order.amount = @order_amountnew = order_amount + order.products.last.price(我怀疑您在这里有一个错误,因为您迭代了products,但只保存了最后一个产品的结果)
  • 您初始化实例变量@order_amountnew,但由于重定向而以后不再使用。我认为可以将其删除

初步结果:

def create
  @product.update(ordinable: false)

  if current_user.orders.where(paid: false).present?
    order = current_user.orders.last
    order_amount = order.amount
    if order.products << @product
      order.amount = @order_amountnew = order_amount + order.products.last.price
      order.save

      notice = 'Product added to the cart!'
    else
      notice = 'There was a problem while adding the product to the cart!'
    end

  else
    order = current_user.orders.new
    order.save
    order_id = order.id
    order_amount = order.amount
    if order.products << @product
      order.amount = @order_amountnew = order_amount + order.products.last.price
      order.save

      OrderPaidCheckJob.set(wait: 3.minutes).perform_later(order_id)

      notice = 'Product added to the cart!'
    else
      notice = 'There was a problem while adding the product to the cart!'
    end
  end

  redirect_to products_path, notice: notice
end

看起来更好并且更具可读性?由于它仍然具有重复代码,因此可以将其进一步干燥。但是我建议您先解决我指出的问题,然后再进行重构。

答案 1 :(得分:1)

在我看来,您可以在不使用服务的情况下重构create动作。 (我是服务的忠实拥护者。我只是认为您在这里不需要它们。)

首先,我将为您的User模型添加一些方法,如下所示:

class User < ApplicationRecord

  def unpaid_orders
    orders.where(paid: false)
  end

  def unpaid_orders?
    unpaid_orders.any?
  end

end

然后,我将amount用作方法而不是属性,例如:

class Order < ApplicationRecord 

  def amount
    products.sum(&:price)
  end

end

然后,在您的控制器中,您可以执行以下操作:

delegate *%w(
  unpaid_orders?
  orders
), to: :current_user

def create
  order = unpaid_orders? ? orders.last : orders.create!
  @product.update(ordinable: false)
  if order.products << @product 
    @notice = 'Product added to the Cart!'
    OrderPaidCheckJob.set(wait: 3.minutes).perform_later(order.id) unless unpaid_orders?
  else 
    @notice = 'There was a problem while adding the product to the cart!'
  end
  redirect_to products_path, notice: @notice
end

如果您不想在amount上使用Order作为方法,则可以执行以下操作:

delegate *%w(
  unpaid_orders?
  orders
), to: :current_user

def create
  order = unpaid_orders? ? orders.last : orders.create!
  @product.update(ordinable: false)
  if order.products << @product 
    order.update(amount: order.products.sum(&:price))
    @notice = 'Product added to the Cart!'
    OrderPaidCheckJob.set(wait: 3.minutes).perform_later(order.id) unless unpaid_orders?
  else 
    @notice = 'There was a problem while adding the product to the cart!'
  end
  redirect_to products_path, notice: @notice
end