Revisioning with acts_as_audited

acts_as_audited | plugin | rails | ruby June 18 2007

When I first created acts_as_audited over a year ago, I intended to add versioning/revisioning capabilities, but found I never really had a need. Well, it just so happened that I finally had a need on an app that we are working on. So, acts_as_audited now allows you to revert back to previous revisions.

You can get all the revisions of a model:

article.revisions.each |revision|
  puts revision.class   #=> Article
  puts revision.version #=> 7, 6, 5, 4...
end

or a specific revision:

revision = article.revision(5)
puts revision.title     #=> "Old Title"

or the previous revision. Reverting is as simple as saving a revision:

previous = article.revision(:previous)
previous.save           # revert to previous revision

See the original post for more details about installing and using the plugin.

How is this different from acts_as_versioned?

I’ve never used acts_as_versioned1 (because I’ve never had a need to do versioning), but I do know that it has a few annoying limitations: 1) it requires a separate table (and model) for each model that you want to version, and 2) it saves every attribute, even if it’s not changed.

acts_as_audited already stories all the changes (and only the changes) in one table, so adding versioning was rather trivial. Revisions are created by walking backward through the audits, collecting the changes, and returning an instance of the model.

Upgrading

To use the versioning support, you must add a version field in the audits table:

add_column :audits, :version, :integer, :default => 0

Note: if you already have audit records, the version field will have to be initialized.

Feedback

This code is fairly specific to what I needed, so if you’re using it, I would love to hear how it is working for you. Suggestions and patches welcome.

  1. acts_as_audited was actually one of the first plugins that I wrote, so I looked at the best code I could find as an example for writing a plugin, which happened to be acts_as_versioned. Kudos to technoweenie.
posted by brandon | updated June 18th 02:23 PM
comments feed

19 comments

  1. That’s funny.. I just looked over your old blog post and thought I could use this… :-) Thanks for your work.

    Alexander Flatter Alexander Flatter
    June 18, 2007 at 03:47 PM
  2. w00t! Thanks very much. I commented on the original blog post about adding this feature. I will test it out as soon as I get a chance.

    carlivar carlivar
    June 18, 2007 at 05:47 PM
  3. Sigh, just an update. I got bogged down in my project (wife is pregnant, I have a good excuse) and wasn’t able to test the revisioning yet. But hopefully someone has… :)

    carlivar carlivar
    July 26, 2007 at 02:04 AM
  4. Hello, I’m trying to use acts_as_audited with ActiveScaffold (http://activescaffold.com) but for some reason, it pukes in the “after” filter in the caching.

    If I comment out line 602 in vendor/rails/actionpack/lib/action_controller/filters.rb it works great, but I’m guessing this is bad since cached objects won’t be invalidated or something…

    Any suggestions?

    shadoi shadoi
    July 30, 2007 at 06:28 PM
  5. Here’s the relevant exception:

    NoMethodError (You have a nil object when you didn’t expect it! The error occurred while evaluating nil.controller_name): /vendor/rails/actionpack/lib/action_controller/caching.rb:602:in `callback’ /vendor/rails/actionpack/lib/action_controller/caching.rb:595:in `after’ /vendor/rails/actionpack/lib/action_controller/filters.rb:602:in `proxy_before_and_after_filter’

    shadoi shadoi
    July 30, 2007 at 06:30 PM
  6. Hey this is cool! =)

    Irfan Baig Irfan Baig
    August 09, 2007 at 07:56 PM
  7. Would be nice if you could retrieve a revision by date as well, since the audit table holds a created_at.

    Geoffrey Wiseman Geoffrey Wiseman
    August 23, 2007 at 02:11 PM
  8. Would also love this to co-operate better with Globalize model translations.

    Geoffrey Wiseman Geoffrey Wiseman
    August 23, 2007 at 02:43 PM
  9. A great plugin. Unfortunately it seems to have suffered on Edge rails as a result of the decision to make acts_as_list a plugin… even after installing the acts_as_list plugin one has to comment out line 14 of audit.rb in order to get the server to start…

    Is there an easy fix? Thanks.

    Tim Chater Tim Chater
    September 18, 2007 at 10:49 AM
  10. I am trying to test the moves from one revision to the other in my functional tests and it seems like the models are not keeping all the changes, what am I supposed to do to make the test works?

    Mantat Mantat
    October 07, 2007 at 06:05 PM
  11. This is a great addition to my rails/ruby arsenal! I was wondering if anyone has developed a controller and view to display all of the revisions and possibly revert back to one?

    Ryan Ryan
    October 10, 2007 at 04:37 PM
  12. Ryan,

    I’ll try to clean up the one that I have and post it. Stay tuned.

    Brandon Brandon
    October 12, 2007 at 09:57 AM
  13. Hi Brandon,

    Thanks for the plugin. It is exactly what I was looking for.

    I was looking for information of maintaining an audit trail for a project I am working on and I stumbled upon your previous blog post.

    Regards Surat

    Surat Mukker Surat Mukker
    October 15, 2007 at 04:32 PM
  14. Is there a way to only revert certain attributes? If we have updated first and last name, can I accept the changed last name but not first name?

    Ryan Ryan
    December 06, 2007 at 01:05 PM
  15. Ryan,

    Not really. You could retrieve a revision and just manually set the attributes. If you’d like to submit a patch, I’d be happy to accept it.

    Brandon Brandon
    December 06, 2007 at 10:07 PM
  16. Hi Brandon,

    Great plugin, thanks.

    I just wonder how could I get the updated date/time of each revision?

    e.g. i would like to show a revision history, then i have:

    for rev in @article.revisions puts rev.version puts rev.updated_at end

    but then updated_at always show the last revision’s timestamp.

    AH AH
    December 21, 2007 at 06:13 AM
  17. I vote to add Peter’s diff, which further enhances acts_as_audited to save a record’s attributes when it is deleted, and gives you an easy way to restore it, with the original id (e.g., Client.revive(2344)).

    Aaron Aaron
    January 31, 2008 at 03:41 PM
  18. I’m getting the following error when calling the

    revisions
    method on an audited object.

    NoMethodError: undefined method `first' for Sun Jun 29 17:54:32 +0200 2008:Time
        from /home/daniel/Projects/mango/vendor/plugins/acts_as_audited/lib/acts_as_audited.rb:149:in `changes'
        from (irb):5:in `inject'
        from /home/daniel/Projects/mango/vendor/plugins/acts_as_audited/lib/acts_as_audited.rb:148:in `each'
        from /home/daniel/Projects/mango/vendor/plugins/acts_as_audited/lib/acts_as_audited.rb:148:in `inject'
        from /home/daniel/Projects/mango/vendor/plugins/acts_as_audited/lib/acts_as_audited.rb:148:in `changes'
        from /home/daniel/Projects/mango/vendor/plugins/acts_as_audited/lib/acts_as_audited.rb:147:in `collect'
        from /home/daniel/Projects/mango/vendor/plugins/acts_as_audited/lib/acts_as_audited.rb:147:in `changes'
        from /home/daniel/Projects/mango/vendor/plugins/acts_as_audited/lib/acts_as_audited.rb:133:in `revision'
        from (irb):5

    Am I the only one with this problem?

    Daniel Schierbeck Daniel Schierbeck
    June 29, 2008 at 12:07 PM
  19. I’m surprised that acts_as_audited was your plugin. It’ll probably make a guest appearance here at Bluefish.

    Nolan Eakins Nolan Eakins
    July 15, 2008 at 08:01 PM

Speak your mind:

(Required)

(Required)


(You may use textile in your comments.)

About

I'm Brandon Keepers, a web application developer that likes beautiful code, valid markup and adherence to standards. As a part of Collective Idea in Holland, Michigan, I practice Agile software development primarily using Ruby on Rails.

-86.103171 42.785037

Contact:

more ยป

Syndicate