Treat your ActiveRecord models as if they're under source control
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



The Goal

The goal of Revisionary is to bring over as much as I can from the world of SCM to ActiveRecord. There are a number of versioning plugins, but they're generally limited to the scope of an individual table (i.e. single model) and usually fall short when handling associations.

acts_as_revisable was the premise of this plugin, and a lot of the concepts used in Revisionary are adopted from that project. I wanted to be able to drop the need for a second class, and also wanted to carry over a lot of things I love from git, such commit hashes as opposed to numeric versions. Most importantly, I needed to ability to atomically commit a model and any associations I want.


acts_as_revisable brought branching and basic association cloning to ActiveRecord. What if you could create a record, some associations, and be able to commit that record and its associations without worrying about much? A page has many parts, if I modify attributes on the page and/or the parts, I want to be able to 'commit' the page and take a snapshot of the current attributes for all affiliated models. With Revisionary, this is now possible, and by intelligently hashing the content of both the parent record and associated records, saving (committing) only takes place if content changes.

How it Works

Commits are saved with a SHA1 hash generated by concatenating the values of the parent model and the values of it's associations. Each snapshot consists of a record containing the parent object, and n-records for it's associations.

- Page: Home Page
  - Part: Welcome to our site!

... part data changed and stored ...

- Page: Home Page
  - Part: Hello from our site!

... page data changed and stored ...

- Page: Our Home Page
  - Part: Hello from our site!


class Page < ActiveRecord::Base
  has_many :parts
  is_revisionary :with => :parts

class Part < ActiveRecord::Base
  belongs_to :page


In the Page definition, the `with` option accepts either an array or a single association. Now, any modifications to the parts association will be monitored by page records.

@page = Page.create :name => "Home Page"
@part = :name => "Body", :content => "Welcome!"

@page.head?  # => true

Modifications made to an association will be committed atomically with the parent object if saved.

@part.content = "Welcome to our website!"

@page.ancestors.size  # => 1

A snapshot of that commit has been stored to the database, and updates made to either the parent or associations will be stored, again, atomically. No need to save your associations.

Revisionary will only save a snapshot if data has been altered.

@page.ancestors.size  # => 1

@page.ancestors.size  # => 1 = "About Us"

@page.ancestors.size  # => 2

It is sometimes helpful to commit with a message :commit_message => "Made some edits"

Not feelin' SHA1 hashes? Tag your commits. :tag => "final-2"

Not every save needs to be commited. :without_commit => true

Checkouts / Reverting

Checking out older commits is extremely easy to do.

@page.checkout(:previous)       # Previous commit

@page.checkout(:root)           # Base commit for branch

@page.checkout(6)               # 6 commits ago

@page.checkout("tag:final-2")   # Revision tagged 'final-2'

@page.checkout("f62e0ea")       # Like in git, you only need enough of a commit hash to guarantee uniqueness

This will return, not revert to, the requested commit.

To add a previous version to the head, you can quickly revert to any commit:


Branching has been removed for now. I don't see many practical uses in the project that this is being developed for, but perhaps this can be reintroduced at a later time?

Diffing / Merging


No extra tables, just a few columns to one table



Revisionary will only work under Rails Edge, or anything tagged >= 2.1


Many thanks go to Rich Cavanaugh, the author of acts_as_revisable. Not only was his project my inspiration in creating Revisionary, but his acts_as_scoped_model and other code samples propelled me to finish most of this plugin while on a plane.

Copyright © 2008 Brennan Dunn, released under the MIT license