Audit Logs with ActiveRecord

I've been trying to create an audit log for a few (12) tables, and unfortunately ActiveRecord seems to be falling flat for what I want to do. First I'll describe whats out there already to do this, then I'll talk about what I had to do.

There are a couple nice plugins out there for helping you keep track of changes to your records. The first one is actsasversioned. actsasversioned will copy your record to a version table whenever there is an insert or update, then increase a version number in the original table. It also automatically adds a list of versions to your original model, and lets you revert to any particular version. This is great, and almost exactly what I needed. I didn't really need the ability to revert to a version, but that just seemed like gravy on top. The only problem with actsasversioned comes in when you try to keep track of changes to habtm relationships. But this is where the second plugin comes in to play.

The second plugin is called actsasversionedassociation. This plugin will help you keep track of changes to your relationships. actsasversionedassociation is built on top of actsasversioned. They way it works is by setting the owner model to actsasversioned, then when any associations are updated, it writes a new version of the owner model, then writes the associations to a associations version table. So if you were to have an Article model that has and belongs to many Documents, you would need 5 tables to represent that:

  1. articles
  2. articles_versions
  3. documents
  4. articles_documents
  5. articles_documents_versions

If the association changes, a record is written to articles (to increase the version number), articlesversions, articlesdocuments, and articlesdocumentsversions. But what happens if Article has 10 versioned habtm relationships, and just one of those relationships changes? Then a record will get written to every habtm version table for just one change. Thats 12 writes for a change to just one relationship. That will not scale....

Fortunately I don't care about reverting to a previous version. All I care about is what changed, and when. So my favorite solution for this problem is to add triggers to the tables that may change. That way I only get one extra write when a relationship changes. Just copy the row to another table with a timestamp and an action.

But what about has_many :through?

hasmany :through allows you to put a model on top of the join table. Then I could just drop actsasversioned on top of that model and be done. I would have used this solution except that I ran in to a bug. hasmany :through does not support all of the same array manipulation that habtm does. For example, you can append (<<) to hasmany :through and habtm, but the clear method does not work the same way on hasmany :through. Also, has_many :through does not set an attribute= method like habtm does.

Post a Comment

Your email is never shared. Required fields are marked *

*
*
Check Spelling
Activate Spell Check while Typing