0

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

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

这是我对代码的猜测:

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

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

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

尽管我自己从纯文本生成元数据,但我几乎可以完全控制一切——所以虽然我可能可以添加新的元数据字段,但我必须完成所有相关的工作。

4

2 回答 2

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 的解决方案 - 所以如果你有一个更好的,我会完全跳槽到那个。

于 2012-09-18T20:50:53.587 回答
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
于 2012-09-13T02:10:29.143 回答