Overview of make_resourceful 0.1.0

Posted May 26, 2007

I said last Monday I’d mention when the make_resourceful Rails plugin, which Hampton Catlin, Jeff Hardy, and I have been working on, is released. Well, as of yesterday (the 25th) around midnight, it was. See Hampton’s blog post for the official announcement. The main points are as follows:

  • Very alpha release. The API is subject to change, although it probably won’t for the most part.
  • There’s a Google group.
  • Install with ./script/plugin install http://svn.hamptoncatlin.com/make_resourceful/trunk.

For those who didn’t catch my last post about it and are too lazy to go back and read it, make_resourceful is a plugin that allows you to factor out all the repetitive REST code that clutters up the controller a surprising amount. It manages this by automatically creating typical RESTful actions, while allowing plenty of room for user customization.

actions

The core functionality of make_resourceful comes from the actions method1. This tells make_resourceful which of the typical RESTful actions (index, show, new, create, edit, update, and destroy) should be auto-generated. For example:

class VideosController < ApplicationController
  make_resourceful do
    actions :index, :show,
          :new, :create,
          :destroy
  end
end

It simply takes a list of symbols with the action names, and exposes the proper actions, all ready with their typical operation. If you want to auto-generate all the possible actions, it’s even easier:

class VideosController < ApplicationController
  make_resourceful do
    actions :all
  end
end

I’m sure most folks are familiar with the default actions, but for the sake of those who aren’t and for specifying exactly what make_resourceful handles, I’ll go over what each auto-generated action does.

index

index simply loads in a list of relevant objects and renders the template. For example, for VideosController, it would assign the attribute current_objects and the instance variable @videos to a list of all Videos.

show

show also just loads the relevant object. For VideosController, it would assign both current_object and @video to the video specified by params[:id].

edit

edit works identically to show: it finds the current object and loads it into the proper instance variables.

new

new is similar to show, but instead of finding an object, it simply creates a new one. Thus, for VideosController, current_object and @video would both be assigned to (the same) newly created Video object.

create

This is where it starts getting tricky. Don’t be frightened, though; the default actions getting tricky means that the tricky stuff is now stuff that’s taken care of for you.

create builds a new instance of the current model with the parameters passed in (presumably by a form rendered with new), then tries to save that instance. For example, for VideosController, it would build current_object and @video with the parameters from params[:video].

If the saving succeeds, flash[:notice] is set with an appropriate success message and the user is redirected to the show page for the newly created object. Otherwise, if it fails, flash[:error] and an appropriate HTTP error are set, and the new action is rendered. Note that in this case the user isn’t actually redirected; thus, the object that’s made available to new.rhtml has all the user’s attributes set, as well as whatever errors caused the save to fail.

update

update is nearly identical to create. The only difference is that instead of building a brand new object, it finds the existing one and updates the attributes, just as you’d expect. Also, if the update fails, edit is rendered rather than new.

destroy

destroy also works similarly to create. It finds the relevant object, then tries to destroy it. If the object is successfully destroyed, flash[:notice] is set and the user is redirected to index. Otherwise, flash[:error] is set and the user is redirected back to the page they were previously viewing.

belongs_to

make_resourceful also deals easily with nested resources. In order to activate this functionality, you simply have to declare the chain of ancestors using the belongs_to method. For example, if each Video had several Scenes and each Scene in turn has several Frames, you would declare it as so:

class FramesController < ApplicationController
  make_resourceful do
    actions :all

    belongs_to :video, :scene
  end
end

You’re simply declaring the chain of ancestors, from the topmost (Video) down.

belongs_to does two very useful things. First, it automatically causes all the parent objects to be assigned. For example, for FramesController, @video and @scene would both be assigned before every action.

Second, it ensures that all model searches are properly scoped. For example, suppose the Frame with id 42 belonged to the Scene with id 3. The URL “videos/12/scenes/5/frames/42” wouldn’t be valid; Frame 42 doesn’t belong to Scene 5. belongs_to ensures that this won’t blithely serve up Frame 42 anyway; instead, it acts just as though Frame 42 couldn’t be found at all. The URL “videos/12/scenes/3/frames/42”, on the other hand, will work fine.

before and after

Now, I think I know what you might be thinking. “All this automation is great,” you may cognate, “but what if I want my action to do something special? Do I just give up and define it all myself?”

Not at all, my friend! make_resourceful provides a vast array of ways for you to customize your actions to behave in specialized ways. The simplest way to do so is to use the before and after methods. These methods take the name of an action and a block to execute, and then executes the block in the relevant place. For example2:

before :show do
  @video.views += 1
end

This shows a few important aspects of before. It’s not actually the first thing that happens; the relevant instance variables are loaded beforehand. The callback order is typically as follows:

  • Instance variables are loaded.
  • before callbacks are called.
  • Business logic is executed.
  • after callbacks are called.
  • The response is sent out (see response_for, below).

Note that index, show, new, and edit don’t actually have any business logic2. Thus, they don’t actually call any after callbacks. create, update, and destroy, on the other hand, have plenty of it. Each of these actually define two after callbacks; one for when the action succeeds, and one for when it fails. For example:

after :create do
  logger.info "Video #{@video.filename} successfully created!" 
end

after :create_fails do
  logger.info "Video #{@video.filename} creation failed!" 
end

A similar pattern applies to update and destroy.

response_for

You may have noticed in my list of the steps in a resourceful action a step where “the response is sent out.” This is the step where stuff that the user will see is dealt with, such as assigning flash notices and errors, rendering pages, and redirecting.

Although the default responses for each action are very sensible, it’s concievable that you might want to change them around for whatever reason. The response_for method makes this not only possible but easy. At its most basic, it works just like the other two callbacks. For example:

response_for :new do
  render :action => 'edit'
end

You can also respond to different formats, using the same syntax as the built-in respond_to method:

response_for :show do |format|
  format.html
  format.json { render :json => @video.to_json }
  format.xml { render :xml => @video.to_xml }
end

A Finer Grain of Control of flash and Redirecting

make_resourceful offers one last bit of power4: the ability to set the flash message and the redirect from the client side. If you post “_flash[notice]” or “_flash[error]” along with form data, make_resourceful will use those instead of the defaults. The same goes for “_redirect_on[success]” and “_redirect_on[failure]”.

This gives you a surprising amount of power. Instead of forcing, say, create to always say “Video created successfully!”, you can have it say “Video uploaded successfully!” when the user uploads a video, “Videos merges successfully!” when the user merges two videos into one, etc. You can similarly control where the user will be redirected to on what sort of outcome. All that without adding any extra logic to the controller!

So that’s the plugin in all its present glory! I can assure you the glory will only increase, and I’ll be sure to tell all about it when it does. Until then, go forth and make_resourceful!

1 This method used to be known as build, but was changed to actions as that’s more declarative.

2 For simplicity’s sake, I’ll exclude the controller class and call to make_resourceful for this and future examples. We’ll say they’re all in VideoController.

3 By “business logic” I mean stuff like saving or deleting models.

4 If you’re super-demanding and want even more cool features, just be patient; they’re in the works.

vastydeep said May 26, 2007:

Great plugin! REST is the right direction, but already needs a bit of DRYing out. Niiiiiice.

Jeffrey Hardy said May 26, 2007:

This is a great writeup, Nathan. Thanks for putting it out there.

Jamie Hill said May 27, 2007:

We are using something very similar at SonicIQ called ‘define_crud’ which can be found here

Documentation is a bit on the poor side at the moment but a glance at the code should explain what’s going on.

Ray said May 28, 2007:

Could you add a bit about associate_with. I think I understand it, but wanted to know if there is anything I am missing with it.

Ray said May 28, 2007:

Never mind… the Google Group answered that for me!

Nathan said May 28, 2007:

I’ve added a section on it anyway.

Ian White said June 01, 2007:

A similar plugin that’s been around for a little while is resources_controller.

Nathan said June 04, 2007:

I’ve just removed the section on associate_with, because it was removed from make_resourceful. Turns out it’s trivially easy to reproduce with a single line of code:

  before(:create) { current_object.user = current_user }

We just didn’t realize quite how much power make_resourceful gave us.

Jonas Nicklas said August 07, 2007:

I have been waiting for someone to come up with something like this. I knew it would happen sooner or later, but seriously you guys have exceeded my expectations quite a bit, this is brilliantly done! I’m off to dry some apps…

Make your comments snazzy with Textile!