在Rails模型范围定义中使用数组函数

时间:2012-09-13 01:16:18

标签: ruby-on-rails scope

我试图在Rails模型的范围(或self.func_name)中使用数组函数(特别是“keep_if”和“include?”),但不知道如何使其工作。我认为它是可能的,因为它似乎是“where”意味着“set.where”,所以“set.keep_if”是有意义的。

我有一个文档表(或者说,文档的元数据),其中每个文档在表中都有不同的版本,链接; doc.next_version和doc.prior_version。我有一个人员表链接到文档(通过另一个表,'作者'),因此person.documents是一个人工作的每个文档的所有版本的列表。我想获得每个人工作的文档的第一个或最后一个版本,而不是/ every / version。

以下是我对代码的猜测:

class Document < ActiveRecorrd::Base

  has_many :authors

  belongs_to :next_version, :class_name => 'Document'
  has_one :prior_version, :class_name => 'Document'
  #document.document_id is the ID of the prior version of this document

  scope :last!, lambda{ keep_if{|d| (d.next_version.nil?)||(! include?(d.next_version))}}
  # Keep a doc if it either has no next_version (and is thus the last version in 
  # the database), or it's next version isn't in this list.
end

class Person < ActiveRecord::Base
  has_many :authors
  has_many :documents, :through => :authors
end

class Author > ActiveRecord::Base
  belongs_to :person
  belongs_to :document
end

#Usage Example
documents = Person.find(id).documents.last!
published = documents.keep_if{|d| d.published}
success_rate = published.size / documents.size
# etc

我尝试转换为self.first!,但这没有帮助。 (我意识到如果一个人跳过账单的版本,这个方法就不会跳过,并且会返回该文档的两个版本)

我正在寻找更多关于“范围”内发生的事情,以及如何做我正在尝试做的事情,即使它使用完全不同的方法。

我几乎可以完全控制所有内容,虽然我自己也是从纯文本生成元数据 - 所以虽然我可以添加新的元数据字段,但我必须完成所有需要的工作。

2 个答案:

答案 0 :(得分:1)

所以 - 发生了什么:

scopes函数就像查询的命名片段一样;您使用它们来构建查询,ActiveRecord懒惰地评估生成的复合查询。因此,从范围和类方法的角度来看,Person.first.documents 不是一个数组,即使它从其他代码的角度来看也是如此 - Person.first.documents.keep_if{...}

解决方法非常简单 - 只需提示AREL评估查询并转换为数组:

def self.last!
  all.keep_if{...}  
 #^^^ It's the "all" that does it
end

注意,在这种情况下我的实际逻辑(d.next_version.nil?)||(! all.include?(d.next_version))不起作用,我还不确定原因。

编辑:这与'belongs_to:next_version ...'有关,而def next_version解决方法可以解决问题。

编辑2:我现在接受这个作为答案,因为它按照我想要的方式获得了我想要的使用代码,但是IMO / AFAIK它不是一个非常Rails的解决方案 - 所以如果你'我有一个更好的,我会完全跳到那个。

答案 1 :(得分:0)

ActiveRecord已经有一个last!方法,你要覆盖它。我不相信这是个好主意。相反,我认为以不同的方式表达你的逻辑会更好:

class Document < ActiveRecord::Base

    def self.published_documents
        keep_if do |d|
            (d.next_version.nil? || !include?(d.next_version)) && d.published
        end
    end
end

这实现了你在IMO之后的基本逻辑。

为了更进一步,我想我也会将这个方法移到Person类,因为你真的对Person发布的文档感兴趣:

class Person < ActiveRecord::Base

    def published_documents
        documents.keep_if do |d|
            (d.next_version.nil? || !documents.include?(d.next_version)) && d.published
        end
    end
end

您的用法随后变为:

Person.find(id).published_documents