Tuesday, June 19, 2007

Acts_as_versioned simple walkthrough

Acts_as_versioned is a plugin for Ruby on Rails that adds simple versioning functionality for any of your ActiveRecord models.

To install the plugin :

Navigate to your home directory and install with the following commands:

ruby script/plugin discover
ruby script/plugin install acts_as_versioned

The acts_as_versioned plugin allows you to easily make a model save each version of itself in a special database table with version identifiers that can be used to show or return to previous versions of that data.

Lets call the model that you want to version "Product", giving it the singular name used for models, knowing that the database that will be created for it will be called "products." To create this just run:
ruby script/generate model Product

To tell your program that you want this model to be versioned by adding to your model a single line:

class Product <>

This is single call to acts_as_versioned takes care of a huge amount of work for us in the background.

Now that we have the model that you want to version, it needs to have a 'version' column.
The easiest way to do this is to edit your migration for this model to include a column called version with attribute integer.

class AddProductAndVersionedTables <>

Your migration is also going to need to create a versioned table for this model. You do this by adding two lines #{model_name}.create_versioned_table and the associated drop function #{model_name}.drop_versioned_table to your migration

class AddProductAndVersionedTables <>

If you have already created your model and want to make it able to be versioned, you are going to need a new migration, this will add the version column to your model, and create a versioned table for it:

class AddVersioning <>

Now just run
rake migrate
to apply the changes, and this takes care of setting up everything we need for the database.

Anytime that our model is saved, it will store a copy of itself in the versioned database, in our case called product_versions. We are provided with most of the functions we need in the acts_as_versioned plugin.

Some useful methods are:

find_version(version) - Use this to find a specific version of a model.
find_versions(options={}) - Use this to find versions of a model. Takes an options hash like find
revert_to(version) - Use this to temporarily revert to a previous model. (doesn't save)
revert_to!(version) - Use this to revert and save to a previous model.

Some examples of how to use these methods:

To add a link to revert to previous versions and view other versions:

In your view:

<% for version in @product.find_versions.reverse %>

Version <%= version.version %>
<%= link_to '(revert)', { :action => 'revert_to_version',
:version_id => version.version,
:id => @product},
{:confirm => "Are you sure?" } %>
<%= link_to '(preview)', :action => 'show',
:version_id => version.version,
:id => @product %>
<%= version.comment %>
<% end %>

Obviously this code uses two methods which are not defined here, but in the controller

def revert_to_version
@product = Product.find( params[:id] )
@product.revert_to! params[:version_id]
redirect_to :action => 'show', :id => @product

def show
@product = Product.find(params[:id])
if params[:version_id]
@product.revert_to params[:version_id]

Thats all you need to have for a basic setup for acts_as_versioned, have fun experimenting more on your own.

For more information look up:
A good tutorial on using acts_as_versioned at http://wiki.rubyonrails.org/rails/pages/ActsAsVersioned

The acts_as_versioned documentation

Another good tutorial can be found at http://www.urbanhonking.com/ideasfordozens/archives/2006/02/learns_to_use_a_1.html


Tom Duff said...

Hi! Google Alerts found me a match for the title of your weblog.

Good idea. Good start. The world is not exactly awash in useful documentation and indexing for open-source programs and libraries. Every step towards remedying that is a step well-taken.

Anonymous said...


I think maybe there has been a publishing error with this article? The first code snippet is "class Product <>" (Product is followed by empty angle brackets) - and that is all. Is something missing? The same syntax exists on the next snippet - an neither case actually says "acts_as_versioned..."