28 Jul 2008

Fetches: Bringing Your ActionController Its Slippers

There is a piece of code that shows up more than 80% of the controllers that I write, and it goes a little something like this:

class UsersController < ApplicationController
  def user
    @user ||= User.find(params[:id])
  end
  helper_method :user
end

A simple memoization method to allow me to easily grab the parameter-referred user in all of my actions. If I’m using nested routes, that means I can write two, maybe three of these methods into a controller. I’m basically using slight variations on the same code 20 different times in an application. Since we live in a world that loves to be DRY, I thought, “I can do better.”

Fetches: Memoizing Your Parameter Record Retrieval

Fetches is a simple extension to ActionController that lets you simply define those kinds of fetch methods on a one-line command. For the example above, I can rewrite it like so:

class UsersController < ApplicationController
  fetches :user
end

That’s pretty useful! Not only can I call the “user” method from the controller, but it’s automatically helperized so that I can use the same call in my views. Of course, there are times when more advanced fetching is called for, say using a method other than find or storing to a different variable name. Let’s take a look at a slightly more complex example:

# assuming a route like /users/:user_id/articles/:id
class ArticlesController < ApplicationController
  fetches :user, :as => :author, :from => :user_id, :using => :find_by_login
  fetches :article
end

Now if I were to call “author” in any of my controller actions, it would be equivalent to User.find_by_login(params[:user_id]). Similarly, calling “article” is equivalent to Article.find(params[:id]). The “from” option can also take a Proc in case your fetching is not simply a parameter key:

class UsersController < ApplicationController
  fetches :user, :from => Proc.new{ |c| c.params[:user_id] || c.params[:id] }
end

The main advantages to fetches are brevity, clarity and DRYness. I’ve found that this method covers every use case for parameter-based fetching that I’ve needed, and as such provides a much simpler, more readable, and shorter way to fetch models for use in your controller and views.

Installation

Fetches is available as a gem as well as in traditional plugin format. To install
as a gem, add this to your environment.rb:

config.gem 'mbleigh-fetches', :source => 'http://gems.github.com', :lib => "fetches"

To install it as a traditional plugin:

script/plugin install git://github.com/mbleigh/fetches.git

Resources

The source is available on GitHub, the Acts As Community project is there for general discussion, and the Lighthouse is there for bugs and feature suggestions.

UPDATE: A commenter requested that the plugin be able to handle creation of new records in addition to fetching existing records. I have added in the :initialize option to do just this. Examples:

fetches :user, :initialize => true # initialize from params[:user]
fetches :user, :initialize => :author # initialize from params[:author]
fetches :user, :initialize => Proc.new{ |c| {:login => c.params[:login], :email => c.params[:email]} }
blog comments powered by Disqus