Three reasons to love ActionController::Responder
Posted by José Valim August 31, 2009 @ 06:02 PM
A couple weeks ago, I wrote about the newly added ActionController::Responder which summarizes your application behavior for a specified format in just one place. For example, the default html behavior is written as:
class ActionController::Responder
def to_html
if get?
render
elsif has_errors?
render :action => (post? ? :new : :edit)
else
redirect_to resource
end
end
end
Here are three examples of the flexibility this new layer can provide.
1) HTTP Cache
A simple, but quite powerful use for responder is apply HTTP cache to all your resources easily. For simplicity, let's cache just get requests that are not handling a collection:
module CachedResponder
def to_html
if get? && resource.respond_to?(:updated_at)
return if controller.fresh_when(:last_modified => resource.updated_at.utc)
end
super
end
end
2) I18n flash messages
Is a common practice that all controllers, when the create, update and destroy actions are handled with success, a flash message is shown to the user. We could easily remove the flash messages from our controllers and let them be handled by the responder with the help of the I18n framework. And it’s quite straightforward to accomplish:
module FlashResponder
# If it's not a get request and the object has no errors, set the flash message
# according to the current action. If the controller is users/pictures, the
# flash message lookup for create is:
#
# flash.users.pictures.create
# flash.actions.create
#
def to_html
unless get? || has_errors?
namespace = controller.controller_path.split('/')
namespace << controller.action_name
flash[:success] = I18n.t(namespace.join("."), :scope => :flash,
:default => "actions.#{controller.action_name}", :resource => resource.class.human_name)
end
super
end
end
The first question then arises: what if I don’t want to add a flash message in an specific situation? This can be solved using options, since all options sent to respond_with are sent to the responder, we could use it in our favor as well:
class MyResponder < ActionController::Responder
def to_html
unless get? || has_errors? || options.delete(:flash) == false
namespace = controller.controller_path.split('/')
namespace << controller.action_name
flash[:success] = I18n.t(namespace.join("."), :scope => :flash,
:default => "actions.#{controller.action_name}", :resource => resource.class.human_name)
end
super
end
end
And we can invoke it as:
class PostsController < ApplicationController
def create
@post = Post.create(params[:post])
respond_with(@post, :flash => false)
end
end
3) Instant pagination
Some people already start a project with pagination from scratch, others add at some point. Nonetheless, pagination is more like a rule than a exception. Can that be handled by Rails 3? First, let’s check an index action with respond_with:
class PostsController < ApplicationController
def index
@posts = Post.all
respond_with(@posts)
end
end
Right now, when we call Post.all, it returns a collection of posts in an array, so the pagination should be done before the collection is retrieved. Thanks to Emilio and his work integrating ActiveRelation with ActiveRecord, Post.all will return an ActiveRecord::Relation that will be sent to the responder:
module PaginatedResponder
# Receives a relation and sets the pagination scope in the collection
# instance variable. For example, in PostsController it would
# set the @posts variable with Post.all.paginate(params[:page]).
def to_html
if get? && resource.is_a?(ActiveRecord::Relation)
paginated = resource.paginate(controller.params[:page])
controller.instance_variable_set("@#{controller.controller_name}", paginated)
end
super
end
end
However, the code above is definitely smelling. Set the paginated scope seems more to be a controller responsability. So we can rewrite as:
module PaginatedResponder
def to_html
if get? && resource.is_a?(ActiveRecord::Relation)
controller.paginated_scope(resource)
end
super
end
end
class ApplicationController < ActionController::Base
def paginated_scope(relation)
instance_variable_set "@#{controller_name}", relation.paginate(params[:page])
end
hide_action :paginated_scope
end
As previously, you could make use of some options to customize the default pagination behavior.
Wrapping up
All the examples above were contained in modules, that means that our actual responder has yet to be created:
class MyResponder < ActionController::Responder
include CachedResponder
include FlashResponder
include PaginatedResponder
end
To activate it, we just need to overwrite the responder method in our application controller:
class ApplicationController < ActionController::Base
def paginated_scope(relation)
instance_variable_set "@#{controller_name}", relation.paginate(params[:page])
end
hide_action :paginated_scope
protected
def responder
MyResponder
end
end
While those examples are simple, they show how you can dry up your code easily using Responder. And you? Already thought in an interesting use case for it?

Readability completely sacrificed for “elegance.”
One step forward, two steps back.
@jonathan: sure, keep writing out the entire workflow of every action as much as you like.
I like the new responder. I welcome any good effort in minimizing duplication in my code. Controllers were always a major pain.
Jonathan, nothing was killed in the making of this feature.
This feature is for those with complex applications, plugin developers, and Rails extension artists. Not your everyday just-need-an-app developer.
So patch up that mortal wound and carry on with nice, readable controllers!
Really great new feature, thanks for great job José Valim.
Pagination is very important. If I don’t want to create responder, is it possible to do pagination in controller? Responder is for supporting multiple formats I guess, I don’t need it in most controllers.
In your MyResponder example, you include three modules, each of which defines to_html… don’t those duplicate methods get clobbered, or does it chain them somehow?
I like all of it, except for the setting of the instance variable in paginated_scope. It’s a bit opaque to set that instance variable in ActionController. Wouldn’t it be better to use an actual scope on the model?
@Andrew, all the to_html methods call super, so they’re chained in inverse order of inclusion.
I am torn between liking the idea but effectively DRYing out everything to the point of not being able to read it concerns me.
Cool!
Although, I’m getting “undefined local variable or method ‘flash’ for #<myresponder:0x1041d9840>” each time a perform an action.
Any ideas?