我试图在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!
,但这没有帮助。 (我意识到如果一个人跳过账单的版本,这个方法就不会跳过,并且会返回该文档的两个版本)
我正在寻找更多关于“范围”内发生的事情,以及如何做我正在尝试做的事情,即使它使用完全不同的方法。
我几乎可以完全控制所有内容,虽然我自己也是从纯文本生成元数据 - 所以虽然我可以添加新的元数据字段,但我必须完成所有需要的工作。
答案 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