Ruby动态地追加方法 - 需要更好的方法来做到这一点

时间:2014-06-26 12:36:19

标签: ruby-on-rails ruby

我这样做

    @orders = Order.unscoped.dated
    @orders = @orders.search_for(params["search"]) if params["search"].present?
    @orders = @orders.where(:items_received_status => true) if params[:air].present? && params[:air] == 't'
    @orders = @orders.tagged_with(params[:tags])            if params[:tags].present?

是否有更好的方法来编写相同的代码

类似

@orders = Order do
  self.where(some condition)
  self.joins(some table)
end

应该有办法避免重复赋值运算符

3 个答案:

答案 0 :(得分:1)

我认为最简单的方法是为此创建模型方法:

class Order
  def self.air(air)
    return self.where(items_received_status: true) if air == 't'
    self
  end

  def search(search...

所以你可以在控制器中链接它,如下所示:

Order.unscoped.dated.air(params[:air]).seach(...

答案 1 :(得分:0)

你可以这样做。它本质上是相同的代码,但它更整洁,因为逻辑并没有模糊正在做的事情:

@orders = build_orders

...

# In pure ruby, this is fine as a private method.  
# In rails, it would be better to have this method in the `Order` model, 
# perhaps split up as separate methods as Sergey Moiseev mentions.  

private

def build_orders
  orders = Order.unscoped.dated
  orders = orders.search_for(params["search"])          if params["search"].present?
  orders = orders.where(:items_received_status => true) if params[:air].present? && params[:air] == 't'
  orders = orders.tagged_with(params[:tags])            if params[:tags].present?

  orders
end

答案 2 :(得分:0)

您可以将查询委托给代理BasicObject的{​​{1}}。

ActiveRecord::Relation

这里发生的是您将代理调用方法调用到ActiveRecord :: Relation,该调用是通过调用scope返回的。然后,如果您的代理返回ActiveRecord :: Relations或其子类的方法,if将该结果分配给委托目标。

然后你会这样做:

class QueryDelegator < BasicObject
  def initialize(target)
    @target = target
  end

  def method_missing(*args, &block)
    result = target.send(*args, &block)
    @target = result if result.kind_of? @target.class
    result
  end
end

或者你可以进一步重构并做:

@orders = QueryDelegator.new(Order.unscoped.dated)
@orders.search_for(params["search"]) if params["search"].present?
@orders.where(:items_received_status => true) if params[:air].present? && params[:air] == 't'
@orders.tagged_with(params[:tags])            if params[:tags].present?

然后在您的控制器中调用您的搜索,如下所示:

class Order < ActiveRecord::Base
  # ...
  def self.search(params)
    orders = query_builder(unscoped.dated)
    orders.search_for(params["search"]) if params["search"].present?
    orders.where(:items_received_status => true) if params[:air].present? && params[:air] == 't'
    orders.tagged_with(params[:tags])            if params[:tags].present?
  end

  def self.query_builder(scope = nil)
    scope ||= scoped
    QueryDelegator.new(scope)
  end
end

Ryan Bates对这种模式做了RailsCast。使用自负。正如一些评论者所说,你可能只是为了方便而牺牲了可读性。