Michael Daines
2005-Apr-25 03:10 UTC
Help?/Question about instance methods in ActiveRecord (and probably generally)
I''m working on a method of saving previous versions of only certain
parts of my tables when the main record is saved. To do this, I''ve
written something like the ActiveRecord::Acts things that I''ve put in
my lib/ directory, and then included from the ActiveRecord class I wish
to use it in.
So, I put the following code in my "Article" class, and versions of
the
fields ''body'', ''short_body'', and
''published'' are saved in a table
called "article_versions":
saves_versions :of => [:body, :short_body, :published]
This works fine, but I also wanted a way to prevent versions from being
saved if certain parts of the record are unchanged. If my
''published''
field is toggled and the ''body'' and
''short_body'' fields remain the
same, I don''t want a version with duplicates of those and a different
''published'' field, because it wastes space in the database.
What I did, then, in the function that saves the versions, is call a
function called ''save_version?'', which returns true if a
version is to
be saved. In the Article class, I wish to override it with something
like this:
def save_version?
self.attributes_modified?(:body, :short_body)
end
The "attributes_modified?" function, which looks at the attributes of
the last version that was saved and the attributes of the current
version and tells us if any are different, should go with the instance
methods of this Acts-like thing. (At least, that seems like the most
appropriate place for it.)
However, if I put it there, it can''t be found when called in the
context of actually using this stuff in a running application! I get
"undefined method `attributes_modified?'' for
#<Article:0x54697ec>". If
I put it actually in the Article class, then it works! The whole thing
tests just fine, though, which is maddening! I''m really not sure what
I''m doing wrong here, because this seems to be the way the Acts work?
Please help!
Here is the source, with my apologies for posting all of it: (maybe
this should be Acts::VersionSaver, but whatever...)
module ActiveRecord
module SavesVersions
def self.append_features(base)
super
base.extend(ClassMethods)
end
module ClassMethods
def saves_versions(options = {})
class_eval <<-EOV
include ActiveRecord::SavesVersions::InstanceMethods
has_many :versions, :class_name =>
"#{self.name}::#{self.name}Version", :order => ''id
desc'', :dependent =>
true
def versioned_attributes
[:#{options[:of].join('', :'')}]
end
def versions_class
#{self.name}::#{self.name}Version
end
class #{self.name}Version < ActiveRecord::Base
belongs_to :#{self.table_name.singularize}
end
after_save :save_version
EOV
end
end
module InstanceMethods
def save_version
unless self.save_version? || self.versions.length == 0
self.versions.delete(self.versions.last)
end
self.versions << copy_version_attrs(self, versions_class.new)
end
def save_version?
true
end
def attributes_modified?(*attrs)
return true if self.versions.length == 0
attrs.each do |attr|
return true if self[attr] != self.versions.last[attr]
end
return false
end
private
def copy_version_attrs(from, to=self.dup)
self.versioned_attributes.each{|attr| to[attr] = from[attr]}
to
end
end
end
end