make_resourceful

Posted May 21, 2007

I just (well, as of last night) got back from RailsConf 2007, which I was attending courtesy of Unspace as a sort of thank-you for working on Haml. It was great fun. I got to meet many awesome people, including Hampton Catlin, with whom I’ve been working on Haml for a long time but whom I’ve never seen in person, as well as the rest of the Unspace team; various folks with whom I’ve talked on the Haml, Scribble, and Hackety Hack mailing lists; a couple members of the Rails core team; and too many other people to list here.

I also went to lots of engaging (and a couple boring) talks. By far the most interesting of these was a talk on a soon-to-be-released plugin called make_resourceful. The basic idea is to DRY up the code in RESTful controllers. Even with the new Rails support for REST, there’s some code that’s just always repeated. For example, for even a minimalistic update:

def update
  @person = Person.find(params[:id])
  if @person.update_attributes params[:person]
    redirect_to person_url(@person)
  else
    redirect_to :back
  end
end

Now, that’s not terrible. It’s only six lines. Most of the other typical RESTful actions (index, show, new, edit, create, update, destroy) are similar in length or shorter. But they’re all the same; the code is repeated over and over again. And when you get to having nested resources the code gets messier:

def update
  @group = Group.find(params[:group_id])
  @person = @group.people.find(params[:id])
  if @person.update_attributes params[:person]
    redirect_to person_url(@group, @person)
  else
    redirect_to :back
  end
end

And what if you want to expose, say, an XML representation?

def update
  @group = Group.find(params[:group_id])
  @person = @group.people.find(params[:id])
  if @person.update_attributes params[:person]
      redirect_to person_url(@group, @person)
    end
  else
    respond_to do |format|
      format.html { redirect_to :back }
      format.xml { render :xml => @person.errors.to_xml, :status => "422 Unprocessable Entity" }
    end
  end
end

What if you wanted to use JSON as well? YAML? What if we didn’t want to expose the person’s password hash and email? It would quickly grow into an unholy mess. But it’s all repeated! We should be able to do something about that!

Well, with make_resourceful, we can. To define our ugly, ugly update function above, all we have to say is

class PeopleController
  make_resourceful do
    build :update

    belongs_to :group

    publish :html, :xml
  end
end

Yes, it’s that simple. build tells make_resourceful to make the default update action. belongs_to shows the inheritance relationship. publish tells it which representations to make available. Everything else is done behind the scenes. Also, check out the syntax: everything’s going on within that one block. This allows the plugin to have more control over the syntax while avoiding the possibility overwriting normal controller methods. Awesome, no?

Alright, it’s time for full disclosure. I confess: I’m not exactly unbiased in my observations. I didn’t just happen across make_resourceful at RailsConf; it’s Hampton’s latest project, along with Jeff Hardy (also of Unspace), and I knew about it a good while beforehand. In fact, I’m helping to implement it, and thus am listed as a co-creator (although all the ideas came from Hampton and Jeff).

But nevertheless, it’s still an awesome plugin! Not only is it excellent at making all the stuff you’d normally want to do quick and easy, it also provides plentiful callbacks so you can customize the behavior. For instance, if you wanted to log how many times someone’s profile was viewed:

make_resourceful do
  build :show, :index

  before(:show) { @person.profile_views += 1 }
end

And what if you wanted to set snappy flash messages?

make_resourceful do
  build :edit, :new, :update, :create

  after(:update, :create) { flash[:notice] = "You did it! Congrats!" }
  after(:update_fails, :create_fails) { flash[:error] = "You failed! Jerk!" }
end

Or only expose a user’s name, id, and comment bodies to the world at large?

make_resourceful do
  build :all

  publish :html, :xml, :yaml, :json,
          :attributes => [:name, :id, {:comments => :body}]
end

Now, I know this is all very exciting, but it’s not quite available yet. It’s almost ready, but not quite. There are still a few implementation details to work out, and the syntax is still a little up in the air. I predict it’ll be available within the week; when it is, the syntax may be a bit different, but all the awesomeness will still be there. I’ll make sure to mention when it is released.

Finally, I also reccomend that you check out Hampton’s post on his make_resourceful presentation. It has the (hilarious) slides he used!

Make your comments snazzy with Textile!