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...

01 July 2008

Update on Rails, jQuery, autocomplete

Today I changed my auto_complete_jquery plugin (which I blogged about previously) to work with a different jQuery autocomplete plugin (that's not confusing is it?!). Previously, I'd been using the "jquery-autocomplete" plugin, but had been having problems with it always being case sensitive, and with it being pretty darn slow. To solve that, I wound up switching to Dylan Verheul's jquery autocomplete plugin, which is fantastic! So, I had to update my plugin (literally changed two lines, along with the readme and some comments).

So, what does it all mean? First, things are no longer case-sensitive (although you can tweak that if you need it, see the docs for DV's jquery plugin). Second, the speed is near instant, and if it's taking any time at all, there's now a CSS style you can set to show an indicator while the AJAX call is running (nice!). Further, there are a slew of options you can set, but one of the coolest things is the ability to set a formatter JavaScript function to adjust the display of the returned results, but without affecting the actual value that is placed in your text field. This is really cool for providing further information about a given matched item. For example, I use it to display, on a line below the matched name, the location of the item. There's an interactive example of this on Dylan Verheul's site.

I highly recommend updating all around on this. For those that might be using my prior version, the changes you'd need to make are:

  • Change your JavaScript calls that make a HTML input into an auto-complete from this format:

    $("input#post_title").autocomplete({ ajax: "auto_complete_for_post_title" })

    to this:


  • Update to the HEAD of my plugin.

  • Remove the old jquery.ui.autocomplete*.js files, and install Dylan's single jquery.autocomplete.js file, updating your JS includes accordingly. Same goes for the stylesheet.

Note, to do the cool 2nd line output of auto-complete items, you will need to write your own auto-complete Rails action, which means you don't need my plugin :) I may update my plugin at some point so you can pass a block in to the autocomplete function to create this same scenario, but this is pretty specific stuff, so we'll see. As a very brief set of instructions to do this, you can essentially copy-paste the auto-complete method that gets defined by my plugin, and then update what you return as text. To do the 2nd line bit, you want to return items in the format, "item|2nd line stuff", (so use the pipe symbol to separate the two lines). Then, you can use a simple JavaScript formatter function like:

function formatAutocompleteItem(row) {
return row[0] + "<br><i>" + row[1] + "</i>";

Then, update your call to the jQuery autocomplete that sets up a field as autocomplete to be:

$("input#post_title").autocomplete("auto_complete_for_post_title", { formatItem:formatAutocompleteItem })