lib/acts_as_versioned.rb
def acts_as_versioned(options = {}, &extension)
          # don't allow multiple calls
          return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods)

          send :include, ActiveRecord::Acts::Versioned::ActMethods

          cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column, 
            :version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns,
            :version_association_options, :version_if_changed

          self.versioned_class_name         = options[:class_name]  || "Version"
          self.versioned_foreign_key        = options[:foreign_key] || self.to_s.foreign_key
          self.versioned_table_name         = options[:table_name]  || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}"
          self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
          self.version_column               = options[:version_column]     || 'version'
          self.version_sequence_name        = options[:sequence_name]
          self.max_version_limit            = options[:limit].to_i
          self.version_condition            = options[:if] || true
          self.non_versioned_columns        = [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column, 'created_at', 'created_on']
          self.version_association_options  = {
                                                :class_name  => "#{self.to_s}::#{versioned_class_name}",
                                                :foreign_key => versioned_foreign_key,
                                                :dependent   => :delete_all
                                              }.merge(options[:association_options] || {})

          if block_given?
            extension_module_name = "#{versioned_class_name}Extension"
            silence_warnings do
              self.const_set(extension_module_name, Module.new(&extension))
            end

            options[:extend] = self.const_get(extension_module_name)
          end

          class_eval do
            has_many :versions, version_association_options do
              # finds earliest version of this record
              def earliest
                @earliest ||= find(:first, :order => 'version')
              end

              # find latest version of this record
              def latest
                @latest ||= find(:first, :order => 'version desc')
              end
            end
            before_save  :set_new_version
            after_save   :save_version
            after_save   :clear_old_versions

            unless options[:if_changed].nil?
              self.track_altered_attributes = true
              options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
              self.version_if_changed = options[:if_changed]
            end
            
            include options[:extend] if options[:extend].is_a?(Module)
          end

          # create the dynamic versioned model
          const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
            serialize :updated_attributes, Array
            
            def self.reloadable? ; false ; end
            # find first version before the given version
            def self.before(version)
              find :first, :order => 'version desc',
                :conditions => ["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version]
            end

            # find first version after the given version.
            def self.after(version)
              find :first, :order => 'version',
                :conditions => ["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version]
            end

            def previous
              self.class.before(self)
            end

            def next
              self.class.after(self)
            end

            def versions_count
              page.version
            end
            
            def latest?
              self.next.nil?
            end
            
            def earliest?
              self.previous.nil?
            end
          end

          versioned_class.cattr_accessor :original_class
          versioned_class.original_class = self
          versioned_class.set_table_name versioned_table_name
          versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym, 
            :class_name  => "::#{self.to_s}", 
            :foreign_key => versioned_foreign_key
          versioned_class.send :include, options[:extend]         if options[:extend].is_a?(Module)
          versioned_class.set_sequence_name version_sequence_name if version_sequence_name
        end
# == Configuration options
#
# * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example)
# * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example)
# * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
# * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI.  (default: versioned_type)
# * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version)
# * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model.
# * <tt>limit</tt> - number of revisions to keep, defaults to unlimited
# * <tt>if</tt> - symbol of method to check before saving a new version.  If this method returns false, a new version is not saved.
#   For finer control, pass either a Proc or modify Model#version_condition_met?
#
#     acts_as_versioned :if => Proc.new { |auction| !auction.expired? }
#
#   or...
#
#     class Auction
#       def version_condition_met? # totally bypasses the <tt>:if</tt> option
#         !expired?
#       end
#     end
#
# * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model.  This takes
#   either a symbol or array of symbols.  WARNING - This will attempt to overwrite any attribute setters you may have.
#   Use this instead if you want to write your own attribute setters (and ignore if_changed):
# 
#     def name=(new_name)
#       write_changed_attribute :name, new_name
#     end
#
# * <tt>extend</tt> - Lets you specify a module to be mixed in both the original and versioned models.  You can also just pass a block
#   to create an anonymous mixin:
#
#     class Auction
#       acts_as_versioned do
#         def started?
#           !started_at.nil?
#         end
#       end
#     end
#
#   or...
#
#     module AuctionExtension
#       def started?
#         !started_at.nil?
#       end
#     end
#     class Auction
#       acts_as_versioned :extend => AuctionExtension
#     end
#
#  Example code:
#
#    @auction = Auction.find(1)
#    @auction.started?
#    @auction.versions.first.started?
#
# == Database Schema
#
# The model that you're versioning needs to have a 'version' attribute. The model is versioned 
# into a table called #{model}_versions where the model name is singlular. The _versions table should 
# contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.
#
# A lock_version field is also accepted if your model uses Optimistic Locking.  If your table uses Single Table inheritance,
# then that field is reflected in the versioned model as 'versioned_type' by default.
#
# Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table 
# method, perfect for a migration.  It will also create the version column if the main model does not already have it.
#
#   class AddVersions < ActiveRecord::Migration
#     def self.up
#       # create_versioned_table takes the same options hash
#       # that create_table does
#       Post.create_versioned_table
#     end
# 
#     def self.down
#       Post.drop_versioned_table
#     end
#   end
# 
# == Changing What Fields Are Versioned
#
# By default, acts_as_versioned will version all but these fields: 
# 
#   [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
#
# You can add or change those by modifying #non_versioned_columns.  Note that this takes strings and not symbols.
#
#   class Post < ActiveRecord::Base
#     acts_as_versioned
#     self.non_versioned_columns << 'comments_count'
#   end
# 
Configuration options
class_name - versioned model class name (default: PageVersion in the above example)
table_name - versioned model table name (default: page_versions in the above example)
foreign_key - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
inheritance_column - name of the column to save the model's inheritance_column value for STI.  (default: versioned_type)
version_column - name of the column in the model that keeps the version number (default: version)
sequence_name - name of the custom sequence to be used by the versioned model.
limit - number of revisions to keep, defaults to unlimited
if - symbol of method to check before saving a new version.  If this method returns false, a new version is not saved.



or

class Auction
def version_condition_met. # totally bypasses the :if option
expired.

if_changed - Simple way of specifying attributes that are required to be changed before saving a model.  This takes
either a symbol or array of symbols.  WARNING - This will attempt to overwrite any attribute setters you may have.
Use this instead if you want to write your own attribute setters (and ignore if_changed).



extend - Lets you specify a module to be mixed in both the original and versioned models.  You can also just pass a block
to create an anonymous mixin.

class Auction
acts_as_versioned do
def started.

or

module AuctionExtension
def started.

Example code.



Database Schema

The model that you're versioning needs to have a 'version' attribute. The model is versioned
into a table called #{model}_versions where the model name is singlular. The _versions table should
contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.

A lock_version field is also accepted if your model uses Optimistic Locking.  If your table uses Single Table inheritance,
then that field is reflected in the versioned model as 'versioned_type' by default.



class AddVersions < ActiveRecord::Migration
def self.up
create_versioned_table takes the same options hash
that create_table does
Post.create_versioned_table
end



Changing What Fields Are Versioned

By default, acts_as_versioned will version all but these fields.



You can add or change those by modifying #non_versioned_columns.  Note that this takes strings and not symbols.


Configuration options class_name - versioned model class name ( default : PageVersion in the above example ) table_name - versioned model table name ( default : page_versions in the above example ) foreign_key - foreign key used to relate the versioned model to the original model ( default : page_id in the above example ) inheritance_column - name of the column to save the model ' s inheritance_column value for STI . ( default : versioned_type ) version_column - name of the column in the model that keeps the version number ( default : version ) sequence_name - name of the custom sequence to be used by the versioned model . limit - number of revisions to keep defaults to unlimited if - symbol of method to check before saving a new version . If this method returns false a new version is not saved . or class Auction def version_condition_met . # totally bypasses the : if option expired . if_changed - Simple way of specifying attributes that are required to be changed before saving a model . This takes either a symbol or array of symbols . WARNING - This will attempt to overwrite any attribute setters you may have . Use this instead if you want to write your own attribute setters ( and ignore if_changed ) . extend - Lets you specify a module to be mixed in both the original and versioned models . You can also just pass a block to create an anonymous mixin . class Auction acts_as_versioned do def started . or module AuctionExtension def started . Example code . Database Schema The model that you ' re versioning needs to have a ' version ' attribute . The model is versioned into a table called # { model } _versions where the model name is singlular . The _versions table should contain all the fields you want versioned the same version column and a # { model } _id foreign key field . A lock_version field is also accepted if your model uses Optimistic Locking . If your table uses Single Table inheritance then that field is reflected in the versioned model as ' versioned_type ' by default . class AddVersions < ActiveRecord :: Migration def self . up create_versioned_table takes the same options hash that create_table does Post . create_versioned_table end Changing What Fields Are Versioned By default acts_as_versioned will version all but these fields . You can add or change those by modifying #non_versioned_columns . Note that this takes strings and not symbols .
def acts_as_versioned(options = {}, &extension)
          return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods)
          send :include, ActiveRecord::Acts::Versioned::ActMethods
          cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column, 
            :version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns,
            :version_association_options, :version_if_changed
          self.versioned_class_name         = options[:class_name]  || "Version"
          self.versioned_foreign_key        = options[:foreign_key] || self.to_s.foreign_key
          self.versioned_table_name         = options[:table_name]  || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}"
          self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
          self.version_column               = options[:version_column]     || 'version'
          self.version_sequence_name        = options[:sequence_name]
          self.max_version_limit            = options[:limit].to_i
          self.version_condition            = options[:if] || true
          self.non_versioned_columns        = [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column, 'created_at', 'created_on']
          self.version_association_options  = {
                                                :class_name  => "#{self.to_s}::#{versioned_class_name}",
                                                :foreign_key => versioned_foreign_key,
                                                :dependent   => :delete_all
                                              }.merge(options[:association_options] || {})
          if block_given?
            extension_module_name = "#{versioned_class_name}Extension"
            silence_warnings do
              self.const_set(extension_module_name, Module.new(&extension))
            end
            options[:extend] = self.const_get(extension_module_name)
          end
          class_eval do
            has_many :versions, version_association_options do
              def earliest
                @earliest ||= find(:first, :order => 'version')
              end
              def latest
                @latest ||= find(:first, :order => 'version desc')
              end
            end
            before_save  :set_new_version
            after_save   :save_version
            after_save   :clear_old_versions
            unless options[:if_changed].nil?
              self.track_altered_attributes = true
              options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
              self.version_if_changed = options[:if_changed]
            end
            include options[:extend] if options[:extend].is_a?(Module)
          end
          const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
            serialize :updated_attributes, Array
            def self.reloadable? ; false ; end
            def self.before(version)
              find :first, :order => 'version desc',
                :conditions => ["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version]
            end
            def self.after(version)
              find :first, :order => 'version',
                :conditions => ["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version]
            end
            def previous
              self.class.before(self)
            end
            def next
              self.class.after(self)
            end
            def versions_count
              page.version
            end
            def latest?
              self.next.nil?
            end
            def earliest?
              self.previous.nil?
            end
          end
          versioned_class.cattr_accessor :original_class
          versioned_class.original_class = self
          versioned_class.set_table_name versioned_table_name
          versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym, 
            :class_name  => "::#{self.to_s}", 
            :foreign_key => versioned_foreign_key
          versioned_class.send :include, options[:extend]         if options[:extend].is_a?(Module)
          versioned_class.set_sequence_name version_sequence_name if version_sequence_name
        end
def acts_as_versioned ( options = { } , & extension ) return if self . included_modules . include? ( ActiveRecord :: Acts :: Versioned :: ActMethods ) send :include , ActiveRecord :: Acts :: Versioned :: ActMethods cattr_accessor :versioned_class_name , :versioned_foreign_key , :versioned_table_name , :versioned_inheritance_column , :version_column , :max_version_limit , :track_altered_attributes , :version_condition , :version_sequence_name , :non_versioned_columns , :version_association_options , :version_if_changed self . versioned_class_name = options [ :class_name ] || "Version" self . versioned_foreign_key = options [ :foreign_key ] || self . to_s . foreign_key self . versioned_table_name = options [ :table_name ] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}" self . versioned_inheritance_column = options [ :inheritance_column ] || "versioned_#{inheritance_column}" self . version_column = options [ :version_column ] || 'version' self . version_sequence_name = options [ :sequence_name ] self . max_version_limit = options [ :limit ] . to_i self . version_condition = options [ :if ] || true self . non_versioned_columns = [ self . primary_key , inheritance_column , 'version' , 'lock_version' , versioned_inheritance_column , 'created_at' , 'created_on' ] self . version_association_options = { :class_name => "#{self.to_s}::#{versioned_class_name}" , :foreign_key => versioned_foreign_key , :dependent => :delete_all } . merge ( options [ :association_options ] || { } ) if block_given? extension_module_name = "#{versioned_class_name}Extension" silence_warnings do self . const_set ( extension_module_name , Module . new ( & extension ) ) end options [ :extend ] = self . const_get ( extension_module_name ) end class_eval do has_many :versions , version_association_options do def earliest @earliest ||= find ( :first , :order => 'version' ) end def latest @latest ||= find ( :first , :order => 'version desc' ) end end before_save :set_new_version after_save :save_version after_save :clear_old_versions unless options [ :if_changed ] . nil? self . track_altered_attributes = true options [ :if_changed ] = [ options [ :if_changed ] ] unless options [ :if_changed ] . is_a? ( Array ) self . version_if_changed = options [ :if_changed ] end include options [ :extend ] if options [ :extend ] . is_a? ( Module ) end const_set ( versioned_class_name , Class . new ( ActiveRecord :: Base ) ) . class_eval do serialize :updated_attributes , Array def self . reloadable? ; false ; end def self . before ( version ) find :first , :order => 'version desc' , :conditions => [ "#{original_class.versioned_foreign_key} = ? and version < ?" , version . send ( original_class . versioned_foreign_key ) , version . version ] end def self . after ( version ) find :first , :order => 'version' , :conditions => [ "#{original_class.versioned_foreign_key} = ? and version > ?" , version . send ( original_class . versioned_foreign_key ) , version . version ] end def previous self . class . before ( self ) end def next self . class . after ( self ) end def versions_count page . version end def latest? self . next . nil? end def earliest? self . previous . nil? end end versioned_class . cattr_accessor :original_class versioned_class . original_class = self versioned_class . set_table_name versioned_table_name versioned_class . belongs_to self . to_s . demodulize . underscore . to_sym , :class_name => "::#{self.to_s}" , :foreign_key => versioned_foreign_key versioned_class . send :include , options [ :extend ] if options [ :extend ] . is_a? ( Module ) versioned_class . set_sequence_name version_sequence_name if version_sequence_name end
Configuration options
 <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example)
 <tt>table_name</tt> - versioned model table name (default: page_versions in the above example)
 <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
 <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI.
Configuration options <tt > class_name< / tt > - versioned model class name ( default : PageVersion in the above example ) <tt > table_name< / tt > - versioned model table name ( default : page_versions in the above example ) <tt > foreign_key< / tt > - foreign key used to relate the versioned model to the original model ( default : page_id in the above example ) <tt > inheritance_column< / tt > - name of the column to save the model ' s inheritance_column value for STI .
# don't allow multiple calls # finds earliest version of this record # find latest version of this record # create the dynamic versioned model # find first version before the given version # find first version after the given version.
options
extension