Glenn Gillen

Simplifying rails controllers with make_resourceful

See the content of the picture above? Wouldn't it be wonderful if you could reduce all of your controllers down to 3 lines? Well you can't do all of them, but if you are adhering the a RESTful approach then chances are most of the controllers can be condensed considerably using make_resourceful.

Firstly I have to thank Jeremy McAnally for putting me onto this wonderful new plugin for rails. He used it in a project we were working on together and I've used it almost everywhere since. It's from the Hampton Catlin who is also known for the excellent haml plugin/templating engine.

So what is make_resourceful?

Basically it's an extension of rails to take 'convention over configuration' and 'don't repeat yourself' even further. If you're following a standard RESTful approach then make_resourceful takes care of almost all of the heavy lifting around interfacing with your models and loading them into instances.

What do you mean by 'standard RESTful approach'?

If your controllers are broken up into the index/show/update/create/edit/destroy methods and you are defining the route to them with map.resources then you are in the right track. Chances are for you user controller you've got index doing something like:

def index
  @users = User.find(:all)
end

and show doing something like:

def index
  @user = User.find(:id)
end

Hampton was smart enough to realise many of us are doing exactly that on a lot of our controllers now, so why not just assume that is the case?

So what is the improvement in code?

Very quickly we can reduce the controller definition to be:

make_resourceful do
    actions :index, :show   
  end

If we've also got a method to create a new user, we can turn that on by simply:

make_resourceful do
    actions :index, :show, :new, :create
  end

What about if we add in edit and update too? We actually want to support every action except for destroy. Well that is exactly what we specify then:

make_resourceful do
    actions :all, :except => :destroy
  end

Can I handle nested routes?

Of course! And the wonderful thing is it's just as simple as what you've seen above:

class PostsController < ApplicationController
  make_resourceful do
    actions :index
    belongs_to :user
  end
end

This will effectively do the following:

@user = User.find(params[:user_id])
@posts = User.posts

What if I want to provide additional data to my find? Or instantiate additional objects for my views? Change the name of my instance?

There are a range of helper methods that make_resourceful sets up and uses that can be overridden on a per controller basis. The key ones to look at are listed in the RDocs for Default:Accessors. You also still have access to all the same before and after filters that you normally have in your controllers.

I'm sold, how do I install it?

As it's a standard rails plugin, installation is the usual way:

ruby script/plugin install http://svn.hamptoncatlin.com/make_resourceful/trunk
mv vendor/plugins/trunk vendor/plugins/make_resourceful

Make sure you run the last step, otherwise you'll have make resourceful exported into vendor/plugins/trunk rather than vendor/plugins/trunk.

Be careful! It is no silver bullet

I've quickly come to realise that make resourceful is great for getting very simple controllers out the door quickly. What it is not however is a complete replacement for ActionController. There will be times where the number of before filters and hacks you have to accommodate to get your controller working means you're better off refactoring the code back into a more traditional approach. However, even in these scenarios it's a great way to quickly define the specs and get you to a point where you can put the application through usability testing while cleaning up the mess afterwards.

Glenn Gillen

I'm an advisor to, and investor in, early-stage tech startups. Beyond that I'm an incredibly fortunate husband and father. Working on a developer-facing tool or service? Thinking about starting one? Email me and let me know or come to one of our days to help make it a reality.