public
Fork of delynn/userstamp
Description: This Rails plugin extends ActiveRecord::Base to add automatic updating of created_by and updated_by attributes of your models in much the same way that the ActiveRecord::Timestamp module updates created_(at/on) and updated_(at/on) attributes.
Clone URL: git://github.com/bwyrosdick/userstamp.git
Search Repo:
bwyrosdick (author)
Tue Mar 25 20:44:17 -0700 2008
commit  554aa55786c677fe3fea6e64004d554afce085aa
tree    96eb8463d6b261f5ca25e7213b67a452b8c4fe27
parent  779c2c133e47a5fb146875568ca2a72208b76969
README
Userstamp Plugin (v 2.0)
========================

Overview
--------
The Userstamp Plugin extends ActiveRecord::Base
(http://api.rubyonrails.com/classes/ActiveRecord/Base.html) to add automatic updating of 'creator',
'updater', and 'deleter' attributes. It is based loosely on the ActiveRecord::Timestamp
(http://api.rubyonrails.com/classes/ActiveRecord/Timestamp.html) module.

Two class methods ('acts_as_stamper' and 'acts_as_stampable') are implemented in this plugin.
The acts_as_stamper method is used in models that are responsible for creating, updating, or
deleting other objects. The acts_as_stampable method is used in models that are subject to being
created, updated, or deleted by 'stampers'.


Installation
------------
Installation of the plugin can be done using the built in Rails plugin script. Issue the following
command from the root of your application:

  script/plugin install svn://delynnberry.com/code/plugins/userstamp/trunk userstamp

Once installed you will need to restart your application for the plugin to be loaded into the Rails
environment.

You might also be interested in using Piston (http://piston.rubyforge.org/index.html) to manage the
importing and future updating of this plugin.

Usage
-----
In this new version of the Userstamp plug-in, the assumption is that you have two different
categories of objects; those that manipulate, and those that are manipulated. For those objects
that are being manipulated there's the Stampable module and for the manipulators there's the
Stamper module. There's also the actual Userstamp module for your controllers that assists in
setting up your environment on a per request basis.

To better understand how all this works, I think an example is in order. For this example we will
assume that we are creating a weblog application that is comprised of User and Post objects. Since
Users manipulate Posts, we'll do the following:

  class User < ActiveRecord::Base
    model_stamper
  end

And Posts are manipulated by Users, so it should look like this:

  class Post < ActiveRecord::Base
    stampable
  end
  
We need to be sure that the Post model has the appropriate attributes (creator_id & updater_id etc.) which can 
be added in the migration with the userstamps method:

  class CreatePosts < ActiveRecord::Migration
    def self.up
      create_table :posts, :force => true do |t|
        t.string :title
        t.text :body
        t.datetime :published_at
        
        t.userstamps # creates the userstamp attributes (creator_id & updater_id etc.)
        t.timestamps
      end
    end

    def self.down
      drop_table :assignments
    end
  end

Now we just need to setup our controllers to set the current user of the application. It's
recommended that you do this in your ApplicationController (or a controller that all your stamper
objects inherit from):

  class ApplicationController < ActionController::Base
    include Userstamp
  end

So, what did we just do? The acts_as_stamper class method injects two methods into the User class.
They are #stamper= and #stamper and look like this:

  def stamper=(object)
    object_stamper = if object.is_a?(ActiveRecord::Base)
      object.send("#{object.class.primary_key}".to_sym)
    else
      object
    end
    
    Thread.current["#{self.to_s.downcase}_#{self.object_id}_stamper"] = object_stamper
  end

  def stamper
    Thread.current["#{self.to_s.downcase}_#{self.object_id}_stamper"]
  end

The big change with this new version is that we are now using Thread.current to save the current
stamper so as to avoid conflict with concurrent requests.

The Userstamp module that we included into our ApplicationController uses the setter method to
tell the set which user is currently making the request. By default the 'set_stampers' method looks
like this:

  def set_stampers
    User.stamper = self.current_user
  end

This implementation assumes you are using ActsAsAuthenticated, but you can obviously change
over-ride this method to better match your implementation.

Now, let's get back to Stampable (since it really is the interesting one). If you've already
created your database with columns called 'creator_id', 'updater_id' and 'deleter_id', and you're
Stamper object is 'User' than your set and ready to go. Stampable sets up before_* filters
that are responsible for setting those attributes at the appropriate times. It also creates the
belongs_to relationships for you.

If you need to use something different the method can be completely customized. Here's an example of
how you would upgrade an application currently using the initial version of the plugin:

  class Post < ActiveRecord::Base
    stampable :stamper_class_name => :person,
                      :creator_attribute  => :created_by,
                      :updater_attribute  => :updated_by,
                      :deleter_attribute  => :deleted_by
  end


Uninstall
---------
Uninstalling the plugin can be done using the built in Rails plugin script. Issue the following
command from the root of your application:

  script/plugin remove userstamp


Documentation
-------------
#RDoc has been run on the plugin directory and is available in the doc directory. You can also view
#the documentation online at: http://code.delynnberry.com/rdoc/userstamp/.


Running Unit Tests
------------------
There are extensive unit tests in the "test" directory of the plugin. 


Bugs & Feedback
---------------
Bug reports and feedback are welcome via my delynn+userstamp@gmail.com email address. I also
encouraged everyone to clone the git repository and make modifications--I'll be more than happy
to merge any changes from other people's branches that would be beneficial to the whole project.


Credits and Special Thanks
--------------------------
The original idea for this plugin came from the Rails Wiki article entitled
"Extending ActiveRecord" (http://wiki.rubyonrails.com/rails/pages/ExtendingActiveRecordExample).