使用ActiveRecord进行条件链接

时间:2012-10-16 07:06:02

标签: ruby-on-rails activerecord

我试图实现条件链接,这就是我得到的:

控制器索引操作代码:

@range_start = params[:range_start]
@range_stop = params[:range_stop]
Contract.within_range(@range_start, @range_stop)

型号代码:

def self.within_range(range_start = Date.today - 1.month, range_stop = nil)
  self.started_after(range_start).started_before(range_stop)
end

def self.started_after(range_start)
  if range_start.blank?
    self
  else
    self.where('start_date >=?', range_start)
  end
end

def self.started_before(range_stop)
  if range_stop.blank?
    self
  else
    self.where('start_date<=?', range_stop)
  end
end

它有效,但看起来不太好。我尝试使用tap稍微改进一下,但没有成功。如何改进此代码?

更新:在可以转换为内联条件,但也许可以改进其他东西?

range_start.blank? ? self : self.where('start_date >=?', range_start)

UPDATE2:如果未设置range_stop,则此代码无效,started_after条件不适用。

我必须从started_before返回到不放松的第一个条件?

4 个答案:

答案 0 :(得分:10)

时间已过,denis.peplin的解决方案已被弃用。否则它是正确的,你需要一个链接关系。因此,不应使用scoped,而应使用all,如下所示:

def self.started_before(range_stop)
  if range_stop.blank?
    all
  else
    where('start_date<=?', range_stop)
  end
end

但你也可以把它写成一个更简洁的范围:

scope :started_before, ->(range_stop){ range_stop.blank? ? all : where('start_date<=?', range_stop) }

答案 1 :(得分:3)

在红宝石世界中,有一种名为scope的东西可以满足您的需要。

scope :started_after, lambda {|x| where("start_date >=?", x) }
scope :started_before, lambda{|x| where("start_date <=?", x) }

或在一行

scope :starts_between, lambda{|start,finish| where("start_date >=? AND start_date <= ?", start, finish) }

范围是一种有效的记录功能,因此您只需调用:

Contract.starts_between(date1, date2)

如果希望secound参数有条件:

def self.within_range(start, finish = nil)
  data = Contract.started_after(start)
  data = data.started_before(finish) unless finish.nil?
  data
end

答案 2 :(得分:3)

我想尝试澄清以这种方式进行条件链接的目的:想法是在某些方法和链式方法中隐藏条件,因此生成的方法将很简单。

有可能,但ActiveRecord :: Base类的子节点本身无法链接。只有关系可以链接。

所以不要这样做:

def self.started_before(range_stop)
  if range_stop.blank?
    self
  else
    self.where('start_date<=?', range_stop)
  end
end

应该这样做:

def self.started_before(range_stop)
  if range_stop.blank?
    scoped
  else
    self.where('start_date<=?', range_stop)
  end
end

只有一项更改:self已替换为scoped,现在方法始终返回范围,并且可以进行链接。

感谢这篇文章的提示:http://blog.mitchcrowe.com/blog/2012/04/14/10-most-underused-activerecord-relation-methods/

答案 3 :(得分:1)

我添加了这个:

scope :dynamic, -> (chain=nil) { chain.present? ? chain.call : nil }

现在你可以像这样使用它了

conditional_scope = x ? Model.scope1 : Model.scope2
Model.scope3.dynamic(conditional_scope).scope4