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