acts_as_current

This library extends ActiveRecord classes so they can carry a reference to the instance defined as current for the given request.

acts_as_current can be applied to any model, but it’s most common use case is storing the authenticated user on the corresponding User model. Doing so allows other models and observers to access information about the user who made the request. This is particularly useful for auditing, versioning, etc.

Here’s how you’d set up the user example.

First, extend the User model.

class User < ActiveRecord::Base
    acts_as_current
end

Then, tell your application controller to set the current user before all requests. (The controller method can obviously be anything you want. Here, we use :current_user because that’s our convention at Coroutine.)

class ApplicationController < ActionController::Base

    before_filter { |controller| User.current = controller.send(:current_user) }

    def current_user
        # return user or nil
    end
end

Finally, in your observer, you can retrieve the value of current_user by using the accessor mixed into the model class.

class AuditObserver < ActiveRecord::Observer
    observe :account, :balance

    def after_update(record)
        AuditTrail.new({ :record => record, :action => :update, :whodunnit => User.current })
    end
end

Design Notes

acts_as_current uses the hash Thread.current to store the current value for each class extended by the library. Many consider this technique a hack, but for now, it is the only thread safe way to store such data. By coding the technique as a model extension, acts_as_current abstracts reads and writes against Thread.current, greatly reducing the likelihood of conflicts and errors.

We think the real benefits outweigh the perceived risks.

Installation & Generators

Install me from RubyGems.org and add a gem dependency in your configuration file.

$ sudo gem install acts_as_current

Or install me as a plugin.

$ script/plugin install git://github.com/coroutine/acts_as_current.git

Simple Controller Recipe

He’s an example of how you might set a current instance on a User class via a current_user method, using a before filter in the application controller.

before_filter { |controller| User.current = controller.send(:current_user) }

Why Doesn’t the Library Handle the Controller Logic For Me?

Primarily, because we think ActiveRecord extensions have no business altering controller logic. Doing so couples aspects of your application that Rails is going out of its way to separate.

But also because the before_filter syntax is already configurable and extremely expressive. Writing the before filter is no harder than writing a module include statement, but the former tells a code maintainer considerably more information than the latter.

In summary, suck it up and write the controller code yourself. :-)

License

Copyright © 2010 Coroutine LLC, released under the MIT license.

acts_as_current was inspired by sentient_user, copyright © 2009 bokmann, also released under the MIT license.