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

Methods

Public Class methods

find first version after the given version.

[Source]

# File lib/acts_as_versioned.rb, line 237
            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

find first version before the given version

[Source]

# File lib/acts_as_versioned.rb, line 231
            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

[Source]

# File lib/acts_as_versioned.rb, line 229
            def self.reloadable? ; false ; end

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
  • versions_name - name to be used to access the versions.. DH
  • 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 => Proc.new { |auction| !auction.expired? }
    

    or…

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

[Source]

# File lib/acts_as_versioned.rb, line 166
        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, :versions_name

          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.versions_name                = options[:versions_name] || :versions
          self.non_versioned_columns        = [self.primary_key, inheritance_column, self.version_column, 'lock_version', versioned_inheritance_column] + options[:non_versioned_columns].to_a.map(&:to_s)
          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 "has_many :\#{versions_name.to_s}, version_association_options do\n# finds earliest version of this record\ndef earliest\n@earliest ||= find(:first, :order => '\#{version_column}')\nend\n\n# find latest version of this record\ndef latest\n@latest ||= find(:first, :order => '\#{version_column} desc')\nend\nend\nbefore_save  :set_new_version\nafter_save   :save_version\nafter_save   :clear_old_versions\n\nunless options[:if_changed].nil?\nself.track_altered_attributes = true\noptions[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)\nself.version_if_changed = options[:if_changed].map(&:to_s)\nend\n\ninclude options[:extend] if options[:extend].is_a?(Module)\n"

          # create the dynamic versioned model
          const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
            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
          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

[Source]

# File lib/acts_as_versioned.rb, line 246
            def next
              self.class.after(self)
            end

[Source]

# File lib/acts_as_versioned.rb, line 242
            def previous
              self.class.before(self)
            end

[Source]

# File lib/acts_as_versioned.rb, line 250
            def versions_count
              page.version
            end

[Validate]