The ResponderMixin adds methods that help
you manage what formats your controllers have available, determine what
format(s) the client requested and is capable of handling, and perform
content negotiation to pick the proper content format to deliver.
If you hear someone say "Use provides" they‘re talking
about the Responder. If you hear someone ask
"What happened to respond_to?" it was replaced by provides and the other Responder methods.
A simple example
The best way to understand how all of these pieces fit together is with an
example. Here‘s a simple web-service ready resource that provides a list of all the widgets
we know about. The widget list is available in 3 formats: :html (the
default), plus :xml and :text.
class Widgets < Application
provides :html # This is the default, but you can
# be explicit if you like.
provides :xml, :text
def index
@widgets = Widget.fetch
render @widgets
end
end
Let‘s look at some example requests for this list of widgets.
We‘ll assume they‘re all GET requests, but that‘s only to
make the examples easier; this works for the full set of RESTful methods.
- The simplest case, /widgets.html Since the request includes a specific
format (.html) we know what format to return. Since :html is in our list of
provided formats, that‘s what we‘ll return. render
will look for an index.html.erb (or another template format like
index.html.mab; see the documentation on Template engines)
- Almost as simple, /widgets.xml This is very similar. They want :xml, we
have :xml, so that‘s what they get. If render doesn‘t
find an index.xml.builder or similar template, it will call to_xml
on @widgets. This may or may not do something useful, but you can see how
it works.
- A browser request for /widgets This time the URL doesn‘t say what
format is being requested, so we‘ll look to the HTTP Accept: header.
If it‘s ’*/*’ (anything), we‘ll use the first
format on our list, :html by default.
If it parses to a list of accepted formats, we‘ll look through them,
in order, until we find one we have available. If we find one, we‘ll
use that. Otherwise, we can‘t fulfill the request: they asked for a
format we don‘t have. So we raise 406: Not Acceptable.
A more complex example
Sometimes you don‘t have the same code to handle each available
format. Sometimes you need to load different data to serve /widgets.xml
versus /widgets.txt. In that case, you can use content_type to determine what
format will be delivered.
class Widgets < Application
def action1
if content_type == :text
Widget.load_text_formatted(params[:id])
else
render
end
end
def action2
case content_type
when :html
handle_html()
when :xml
handle_xml()
when :text
handle_text()
else
render
end
end
end
You can do any standard Ruby flow control using content_type. If you
don‘t call it yourself, it will be called (triggering content
negotiation) by render.
Once content_type has
been called, the output format is frozen, and none of the provides methods can be used.