Module ActiveRecord::Acts::Versioned::ClassMethods
In: lib/acts_as_versioned.rb


Included Modules


Public Instance methods

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. For finer control, pass either a Proc or modify Model#version_condition_met?
      acts_as_versioned :if => { |auction| !auction.expired? }


      class Auction
        def version_condition_met? # totally bypasses the <tt>:if</tt> option
  • 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.

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

    def self.down


     # File lib/acts_as_versioned.rb, line 107
107:         def acts_as_versioned(options = {})
108:           # don't allow multiple calls
109:           return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods)
111:           class_eval do
112:             include ActiveRecord::Acts::Versioned::ActMethods
113:             cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column, 
114:               :version_column, :max_version_limit, :track_changed_attributes, :version_condition, :version_sequence_name
115:             attr_accessor :changed_attributes
116:           end
118:           self.versioned_class_name = options[:class_name] || "#{self.to_s.demodulize}Version"
119:           self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key
120:           self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{Inflector.underscore(Inflector.demodulize(class_name_of_active_record_descendant(self)))}_versions#{table_name_suffix}"            
121:           self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
122:           self.version_column = options[:version_column] || 'version'
123:           self.version_sequence_name = options[:sequence_name]
124:           self.max_version_limit = options[:limit].to_i
125:           self.version_condition = options[:if] || true
127:           class_eval do
128:             has_many :versions, 
129:               :class_name  => "ActiveRecord::Acts::Versioned::#{versioned_class_name}",
130:               :foreign_key => "#{versioned_foreign_key}",
131:               :order       => 'version'
132:             before_save  :set_new_version
133:             after_create :save_version_on_create
134:             after_update :save_version
135:             after_save   :clear_old_versions
136:             after_save   :clear_changed_attributes
138:             unless options[:if_changed].nil?
139:               self.track_changed_attributes = true
140:               options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
141:               options[:if_changed].each do |attr_name|
142:                 define_method("#{attr_name}=") do |value|
143:                   (self.changed_attributes ||= []) << attr_name.to_s unless self.changed?(attr_name) or self.send(attr_name) == value
144:                   write_attribute(attr_name.to_s, value)
145:                 end
146:               end
147:             end
148:           end
150:           # create the dynamic versioned model
151:           # maybe if i sit down long enough i can think up a better way to do this.
152:           dynamic_model = "class ActiveRecord::Acts::Versioned::\#{versioned_class_name} < ActiveRecord::Base\nset_table_name \"\#{versioned_table_name}\"\nbelongs_to :\#{self.to_s.demodulize.underscore}, :class_name => \"\#{self.to_s}\"\n"
154:           dynamic_model += %Q{set_sequence_name "#{version_sequence_name}"\n} if version_sequence_name
156:           eval dynamic_model + 'end'
157:         end