« Book Ecosystems | Main | Annotate Models Plugin »

February 21, 2006

Logging Changes when Saving ActiveRecord Objects

I was doing some work on our internal order processing system on the plane heading home from the ThoughtWorks AwayDay (thanks for the hospitality, all of you…). One feature I needed was the ability to log the changes we make to an order. Maybe a customer phones up and says "change my street address to xxxx." When I make the change, I’d like it to log that it was me who edited the row on such and such a date, and record the old and new values of every attribute that was changed by the edit.

Turns out that it’s pretty easy. Here’s my save_after_edit action:

  # Save away an order after editing
  def save_after_edit
    if request.post?
      @order = Order.find(params[:id])
      changes = detect_changes(@order.attributes, params[:order])
      if @order.update_attributes(params[:order])
        log_changes(changes)
        redirect_to(:action => :show_order, :id => @order)
      end
    end
  end

The detect_changes method simply compares the attribute values that come back from the form with those in the object just read from the database.

  def detect_changes(from, to)
    result = []
    to.each do |key, val|
      if val != from[key]
        label = Order.human_attribute_name(key)
        result << "#{label}: '#{from[key]}' => '#{val}'"
      end
    end
    result
  end

The log_changes method writes the timestamped change details out to an audit table.

For my needs, doing this in the controller is good enough. However, it’d be really easy to produce an ActiveRecord mixin that added something like a changed_attributes() method to AR objects. Doing so would make the capability totally transparent to the rest of the application.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/t/trackback/2226312/7670426

Listed below are links to weblogs that reference Logging Changes when Saving ActiveRecord Objects:

Comments

I have a similar situation where I am keeping track of changes to an AR object by overriding the attribute= methods for the attributes I am interested in tracking. I then use an after_update callback to log the changes.

One of the attributes I am tracking is actually an association (:users) and I keep track of who is assigned or unassigned from the object.

This worked fine when I was using a HABTM association but now that I am moving to a has_many :through I can't call 'object.users <<' before object has been saved. I can work around this by not using the after_update :log_changes and instead call it after I assign users to the object but this seems pretty ugly.

Is there a better way to do this?

Post a comment

If you have a TypeKey or TypePad account, please Sign In

Now in Beta

  • Programming Ruby, 3rd Edition
    Third Edition, Covering Ruby 1.9, now in beta
My Photo

Pragmatic Stuff

Photos

  • www.flickr.com
    This is a Flickr badge showing public photos from pragdave tagged with pragdave_badge. Make your own badge here.

Site Search

  • Google Search

    The web
    PragDave