Experimental resource_controller Feature: Custom Action Discovery


May 08, 2008

A few weeks ago, Nate Wiger emailed me to ask whether I was interested in a patch for r_c. Evidently, it is possible to determine which controller actions need to be created by examining the routes that are pointing to it. Nate had also noticed that most controller actions follow a pattern:

  1. Load or build an object or a collection (i.e. Post.find(1) or Post.build(params[:post])).
  2. Optionally do something with the object(s) (i.e. @post.save).
  3. Optionally set the flash.
  4. Render a response.

Put those pieces together, and it's possible to create a controller abstraction that is aware of what's routed to it, and sets up a set of sensible defaults, even for custom actions. As you might have guessed, Nate and I have done exactly that.

For the 7 default RESTful actions, things haven't changed much. The only real difference is that you can now change step 2 (refer to list above), by passing a block to action.action.

create.action do
  @post.my_custom_save_method
end

The action block's return value determines whether the success or failure callbacks (i.e. success.flash vs failure.wants) are triggered.

For custom actions, you'll now be able to use the same DSL you're used to for customizing default actions. For example, if you had a custom action called update_collection, you might put something like this in your routes:

map.connect '/posts', :controller => 'posts', :action => 'update_collection', :conditions => {:method => :put}

Based on that route, r_c will automatically be aware of your action, and create a basic skeleton for it, following the pattern of the list above. For a collection update method, you might want to customize your action like this:

update_collection do
  before { @posts.each { |p| p.update_attributes params[:posts] } }
  update_collection.action { @posts.save }
end

That's all you'd have to do. r_c would automatically load the objects (including any parent, respecting polymorphism), and render using its internal helpers.

Note: It is also possible to modify loading by setting a block for the build accessor, which corresponds to step 1.

Highly Experimental

This is highly experimental software. I'm not sure how much I love it, since it's a bit on the magical side for my tastes. However, it is kinda neat to have your controller be aware of which actions to create, based on what is routed to it. I have been having a bit of fun playing around with this.

So, please give it a shot, and let us know how you like it on the mailing list.

Get it from github (checkout the automatic_route_discovery branch). If you aren't using git, you can get a tarball here.