Posted on January 5, 2009 at 5:20 pm

In just about every Rails project I’ve worked on over the last several years we’ve needed track the user who creates and updates database records. The most popular solution seems to be DeLynn Berry’s userstamp plugin and, until recently, that’s what we’ve always chosen to use too. But after fighting incompatibility with newer versions of Rails I decided to write my own plugin. Enter Blame.

Blame automatically userstamps create and update operations if the table has columns named created_by and/or updated_by.

Installation:

ruby script/plugin install git://github.com/infused/blame.git

Blame assumes that you are using restful-authentication and expects User.current_user to return the current user. You can override this behavior by overriding the default userstamp_object method in ActiveRecord::Base or in any of your models. For example, maybe you just want to find the current user bases on the a session variable:

# In environment.rb
class ActiveRecord::Base
  def userstamp_object
    User.find(session[:user_id])
  end
end

Maybe you don’t like the default column names of created_by/updated_by. You can customize the column names globally or for individual models:

# Globally in environment.rb
ActiveRecord::Base.created_userstamp_column = :creator_id

# In a model definition
class Subscription
  self.created_userstamp_column = :creator_id
  self.updated_userstamp_column = :updater_id
end

Automatic userstamping can be turned off by setting record_userstamps:

# Globally in environment.rb
ActiveRecord::Base.record_userstamps = false

# In a model definition
class Subscription
  self.record_userstamps = false
end

Blame also adds a migration helper which will add the created_by and updated_by columns (or your custom column names) to your table:

create_table :widgets do |t|
  t.string :name
  t.timestamps
  t.userstamps
end
5 Comments:
Posted on January 15, 2009 at 10:23 pm by Stuart Coyle

The code below does not seem to work.
ActiveRecord’s not available at this point. It works in my init.rb…except it would seem that
the session variable is not available in an ActiveRecord model.
Have I done something wrong?

# In environment.rb
class ActiveRecord::Base
  def userstamp_object
    User.find(session[:user_id])
  end
end
Posted on January 16, 2009 at 12:44 am by Keith

Stuart,

Sorry, I guess that’s not a well thought out example. So, again off the top of my head… :-)

You can do something similar to the default behavior. The default implementation of userstamp_object is:

def userstamp_object
  User.current_user
end

This simply requires that you add a class accessor to the model:

class User < ActiveRecord::Base
  cattr_accessor :current_user
end

Then, once the user logs in and you have access to the session, you can add a before_filter to ApplicationController:

class ApplicationController  :logged_in?

  def get_current_user
    User.current_user = User.find(session[:user_id])
  end

  def logged_in?
     !session[:user_id].nil?
  end
end

I’ll update the post with a better example.

Cheers,
Keith

Posted on January 16, 2009 at 3:51 pm by Stuart Coyle

Ah, yes. That makes sense! It’s pretty close to what I ended up doing.
Thanks,
And thanks for Blame, it’s made my life easier.

Posted on February 2, 2009 at 2:10 pm by Scott Morrison

Can you have it output messages to syslog or another logging facility as well?

Posted on February 2, 2009 at 4:32 pm by Keith

I usually combine blame with the acts_as_versioned or acts_as_audited plugins when I need to track record updates.