11 July 2008

Fork of acts_as_versioned to provide version diffs and more

Update: Added the earliest? and latest? methods, see below.

On my current project, I've recently begun using Technoweenie's acts_as_versioned (I also looked at simply_versioned which also looks great - you'll need to evaluate for your own needs). This project has some particular needs around versions that aren't covered by the existing plugin, so of course I forked it on GitHub and have been adding my enhancements to my fork. These are publicly available.

Right now there are two enhancements that may be of interest to others:

  1. For each version, there is now an updated_attributes field that stores an Array of the attribute names that were changed in the creation of this version. This is essentially the same array provided by the changed method from ActiveRecord (requires 2.0 (or 2.1?) or later for the "dirty" handling stuff). This provides a nice way of being able to show what changed between versions, without having to compute that yourself (as well as compute it each time you need to display it). Since the data is right there when making the version, I just store it off in a serialized column. This will not record "non-versioned columns". This obviously requires another column in your DB table, and I've amended the migration method, but if you already are using acts_as_versioned, you'd need to manually add the updated_attributes column (of type "text").

  2. A small tweak, but the revert_to! method now has an optional second parameter that, if set to true, will remove all newer versions. In our workflow, when you do a revert/rollback, you no longer want the newer versions, so we delete them. You can achieve this in your app without deleting them, but it just seems cleaner in our case. You can also do this manually by calling the same method that revert_to! uses, delete_newer_versions.

  3. Added the earliest? and latest? methods to the model's Version object as well, so that you can call these on an individual version instance - very convenient for me at least.

I have only tested this under Rails 2.1 and MySQL and SQLite (all the unit tests for acts_as_versioned use SQLite). I have not sent Rick a pull request, because a) I've only been using this for a few days, and I'd like to have it in use a bit longer to be sure of the approach and quality. For example, to really be ideal for the mainline distro, I suspect that adding an option to acts_as_versioned when you define it in your model, would be best for the updated_attributes storage aspect, e.g. making this part optional. It's never optional for my needs, so I haven't spent the time to do this. If you use my fork and do that, please send me a pull request.

The next piece I'm looking at is supporting versioning of associated models. This will be a bit more involved. There is acts_as_versioned_association, but it says it is not maintained and doesn't work with Rails 2.x. However, I may be able to fork that and bring it up to speed for the latest Rails and acts_as_versioned...


srboisvert said...

Did you ever get around to doing acts_as_versioned with associations?

Chris said...

Unfortunately no, not yet. I still plan to, but the feature I need it for isn't at the top of the list yet :) If you find an implementation/someone else that has, let me know.

StartBreakingFree.com said...

This might be a silly question...but I'm not quite clear on how the updated_attributes column could be used in a the view.

I would preferably like to create something visually appealing with strikeouts and green background for new text, etc. Thanks for posting this!

Chris said...

The updated_attributes column simply tells you which attributes were updated in that version. We use this for example to construct a history view where we show each revision of a record, and for the fields that were updated in that version, we color them red to show they were changed.

So, in your case, you would simply iterate the list of updated attributes, and compare them to the previous version to see what was either added, removed, or changed, or whatever you might need, and then style things accordingly.

StartBreakingFree.com said...

Thanks Chris...I'll give it a shot today.

Your forked version got mentioned on StackOverflow which is how I found it:

StackOverflow coincidentally has a pretty nice revisions feature which is similar to what I'm trying to do:

Chris said...

Thanks for letting me know about the StackOverflow mention. I've now added a comment to that thread. Good stuff.