Rails& ActiveRecord:DRY在范围和布尔方法中使用相同的逻辑

时间:2014-04-02 14:08:05

标签: ruby-on-rails ruby activerecord

我有一个范围和方法的模型:

class Model < ActiveRecord::Base

  scope :editable, where('updated_at > ? OR (updated_at IS NULL AND created_at > ?)', (Date.today - 3.days).beginning_of_day, (Date.today - 3.days).beginning_of_day)

  def editable?
    return (self.updated_at || self.created_at) > (Date.today - 3.days).beginning_of_day
  end

end

我觉得我不应该在范围和方法中写两次相同的逻辑。有没有办法可以避免这种情况?

我在Rails 3.2上

由于

2 个答案:

答案 0 :(得分:3)

不幸的是,目前没有简单的方法可以做到这一点。你能得到的最接近的是

def editable?
  self.class.editable.exists? self
end

但是它会调用数据库来检查给定记录是否存在,因此对于新记录或要更新的记录没用。

没有这样的功能的原因是这需要从SQL / Arel转换为ruby代码,这可能并不简单(因为其他模型可能与JOIN语句有关)。但是如果你安装了squeel gem,你应该可以写:

class Model < ActiveRecord::Base

  editable = -> {
    (updated_at > (Date.today - 3.days).beginning_of_day) | ((updated_at == nil) & created_at > (Date.today - 3.days).beginning_of_day)
  }

  scope :editable, where &editable

  def editable?
    instance_eval &editable
  end

end

这几乎是你所需要的。 (未经测试!)

更新:请注意,目前尚未维护squeel gem,这可能很危险。

答案 1 :(得分:1)

据我所知,那里没有代码重复。

将在类Model.editable上调用作用域,检索与该作用域匹配的所有记录。 editable?的实例将调用Model,例如:@model.editable?返回true或false。第一个是查询,第二个是逻辑表达式。

editable?方法中重用范围的一种方法如下:

class Model < ActiveRecord::Base

  scope :editable, where('updated_at > ? OR (updated_at IS NULL AND created_at > ?)', (Date.today - 3.days).beginning_of_day, (Date.today - 3.days).beginning_of_day)

  def editable?
    Model.editable.include?(self)
  end
end

但是,建议不要这样做,否则会导致对数据库进行不必要的查询。