I am trying to use array functions (specifically, “keep_if” and “include?”) within a scope (or self.func_name) for a Rails model, but don’t know how to make it work. I was thinking it was possible because it seems like a “where” implies the “set.where”, so a “set.keep_if” would make sense.
I have a table of documents (or, rather, the metadata for the documents), where each document has different versions of itself in the table, linked; doc.next_version and doc.prior_version. I have a table of people linked to the documents (via another table, ‘author’), so person.documents is a list of all the versions of each document that person worked on. I want to get the first or last version of the document that each person worked on, rather than /every/ version.
Here was my guess as to the code:
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
I tried converting to a self.first!, but that didn’t help. (I realize that if a person skips a version of a bill, this method won’t skip, and will return two versions of that doc)
I’m looking to find out both more of what’s going on within “scopes”, and how to do what I’m trying to do, even if it uses a completely different method.
I have complete control over pretty much everything, although I’m generating the metadata from plain-text myself – so while I can probably add new metadata fields, I have to do all the work that entails.
So – What’s going on:
scopesfunction like named snippets of a query; you use them to build a query, and ActiveRecord lazily evaluates the resulting composite query. So, from the perspective of scopes and class methods,Person.first.documentsisn’t an array, even though it works like one from the perspective of other code –Person.first.documents.keep_if{...}The work around is pretty simple – just prompt AREL to evaluate the query and turn into an array:
Note, my actual logic in this case,
(d.next_version.nil?)||(! all.include?(d.next_version))doesn’t work, and I’m not yet sure why.Edit: It’s something to do with ‘belongs_to :next_version…’, and a
def next_versionworkaround gets things working, if not actually solving the problem.Edit 2: I’m going to accept this as the answer for now, because it gets what I want done with the usage code the way I want, but IMO/AFAIK it’s not a very Rails solution – so if you’ve got a better one, I’ll totally jump ship to that one.