Who Done What? a.k.a. User Stamping

Something I do in every app I create is add creator_id and updater_id to nearly every model. As created_at and updated_at are known as timestamping, I refer to my creator and updater attributes as “user stamping.” The annoying part is in every controller I had to assign those attributes to the currently logged in user. This isn’t a big deal but I’m lazy so I started to look for solutions.

Thread.current

The first thing that popped into my head was Thread.current. Thread.current is basically a hash that allows you to assign thread-safe key value pairs for the current thread. My thought, based on some research, was to wrap Thread.current[:myapp_user_id] with User.current so I could just use User.current in any active record model. Only problem is that smelled a little bit and I figured would be frowned upon by the community as you really shouldn’t access request stuff in your models like that. You can read more about threads and Thread.current if you want.

Solution: Sweeper

I brainstormed a bit with Brandon Keepers and he suggested a few things. Eventually, we decided on a sweeper as they have access to controllers which would have access to the current user. Tada! User Stamp, the plugin, was born. I whipped it together last night (< 50 LOC), added a few specs, and put it up on github this morning.

Installation

Installation is uber predictable.

script/plugin install git://github.com/jnunemaker/user_stamp.git

Once plugin is installed and user_stamp call with list of models to track in application.rb.

class ApplicationController < ActionController::Base
  user_stamp Post, Asset, Job
end

If you actually want to access this stuff through associations and show it in your app you could do something like this:

class Post < ActiveRecord::Base
  belongs_to :creator, :class_name => 'User'
  belongs_to :updater, :class_name => 'User'
end

Then, say in a view, you could do the following:

<h1><%=h @post.name %></h1>

<div><%= @post.content %></div>

<p>Posted by <%=h @post.creator.name %></p>

Let me know what you think in the comments. Bugs can be reported in lighthouse.

11 Comments

  1. court3nay court3nay

    Oct 17, 2008

    Umm. Just so you know. From 2006?

    http://agilewebdevelopment.com/plugins/userstamp

  2. @court3nay – Yeah I used that back in the day. Seemed like a lot of code for something simple. Also, that uses Thread.current which isn’t bad per say but I think my solution is a little more simple.

  3. For those using Merb with DataMapper, I’ve knocked up something similar.

    http://github.com/rlivsey/dm-userstamp/tree/master

  4. great plugin!

    im using make_resourceful, so i did this in after :update,:create
    but sweepers seem even cleaner, ill giv it a try in the next days

    just an idea: will the plugin crash when my model does not have an updater_id ?

  5. @grosser – No, it won’t fail. It checks if the model responds to creator_id and updater_id before assigning each attribute. It also checks if your controller responds to current_user, so it won’t fail if you are missing that either.

  6. But, there are another thread, which manages another threads and give them priority?

  7. @Mark – Not sure I understand what you are asking/stating.

  8. Hey, useful plugin. I’ve built on it and implemented a full audit-trail of user actions on the models being watched by User Stamp.

    Auditing models with User Stamp

  9. Good plugin. I’ll like it even better when I get it working! ;-)

    Question from rails noob, which controller needs the current_user method? How is it defined?

  10. @Chris – Check out restful_authentication. It works out of the box with that. If you aren’t using that, current_user should be defined in application.rb.

  11. Eric Schwartz Eric Schwartz

    Nov 14, 2008

    We built something similar for our rails app: http://github.com/centro/tracktor/tree/master

    I like our model a little better because you annotate each model with whether or not it’s tracked in the model’s class, instead of in a controller class. But hey— at the end of the day, there’s plenty of ways to slice this particular cheesecake!

Sorry, comments are closed for this article to ease the burden of pruning spam.

About

Authored by John Nunemaker (Noo-neh-maker), a programmer who has fallen deeply in love with Ruby. Learn More.

Projects

Flipper
Release your software more often with fewer problems.
Flip your features.