Ruby on Rails
ActsAsVersioned

Description

An experimental feature by technoweenie (see link at bottom). Specify this act if you want to save a copy of the row in a versioned table. This assumes there is a versioned table ready and that your model has a version field. This works with optimisic locking if the lock_version column is present as well.

%{color:red}As of July 21, 2008: Be sure to download this from the Github repository, using ./script/plugin install git://github.com/technoweenie/acts_as_versioned.git
%

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 singular. The _versions table should contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.

Acts_as_versioned comes prepared with the create_versioned_table method, perfect for a migration:

class AddPostsVersions < ActiveRecord::Migration
  require_dependency 'post'
  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

Example


class Page < ActiveRecord::Base
  # assumes page_versions table
  acts_as_versioned
end

page = Page.create(:title => 'hello world!')
page.version       # => 1

page.title = 'goodbye world'
page.save # doesn't this have to be saved before the version changes?
page.version       # => 2
page.versions.size # => 2

page.revert_to(1)  # using version number
page.title         # => 'hello world!'

page.revert_to(page.versions.last) # using versioned instance
page.title         # => 'goodbye world'

page.revert_to!(1) # reverts and saves the model's changes

Configuration options are:

Availability

Provided as a Rails plugin. Install by first updating your plugin source list if necessary, then installing the plugin:

$ ruby script/plugin discover
$ ruby script/plugin install acts_as_versioned

Caveat Emptor: The above may install an out-of-date version that will break on Rails 2.1. The most current code is in the git repository below.

Migrating to ActsAsVersioned

I had been using my own versioning system, keeping old versions in the same table as current versions in a sort of linked list. I managed to convert this to ActsAsVersioned using the following in a migration (very ugly):


  # versions is an ordered array of all old versions, oldest first
  # primary is the current version and is included in the versions array
  # the tables and models have already been set to act as versioned
  versions.each do |obsolete_rev|
    # See ActsAsVersioned set_new_version; ignores locking?
    primary.send("#{primary.class.version_column}=", (primary.versions.calculate(:max, :version) || 0) + 1)
    primary.save_without_revision!
    # See ActsAsVersioned save_version_on_create
    rev = primary.class.versioned_class.new
    primary.clone_versioned_model(obsolete_rev, rev)
    rev.version = primary.send(primary.class.version_column)
    rev.send("#{primary.class.versioned_foreign_key}=", primary.id)
    rev.save
    obsolete_rev.destroy unless obsolete_rev == primary
  end

— Patrick

Comments

Ejemplo de uso, en español
http://ljorquera.blogspot.com/2007/08/versionado-de-tablas-con-ruby-on-rails.html