Module: Merb::ResponderMixin
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.
Child modules and classes
Module Merb::ResponderMixin::ClassMethods
Module Merb::ResponderMixin::Rest
Public Instance Methods
content_type ()
Returns the output format for this request, based on the provided formats, params[:format] and the client‘s HTTP Accept header.
The first time this is called, it triggers content negotiation and caches the value. Once you call content_type you can not set or change the list of provided formats.
Called automatically by render, so you should only call it if you need the value, not to trigger content negotiation.
# File lib/merb/mixins/responder.rb, line 345 345: def content_type 346: unless content_type_set? 347: @_content_type = perform_content_negotiation 348: raise Merb::ControllerExceptions::NotAcceptable.new("Unknown content_type for response: #{@_content_type}") unless 349: Merb.available_mime_types.has_key?(@_content_type) 350: headers['Content-Type'] = Merb.available_mime_types[@_content_type].first 351: end 352: @_content_type 353: end
content_type= (new_type)
Sets the output content_type for this request. Normally you should use provides, does_not_provide and only_provides and then let the content negotiation process determine the proper content_type. However, in some circumstances you may want to set it directly, or override what content negotiation picks.
# File lib/merb/mixins/responder.rb, line 360 360: def content_type=(new_type) 361: @_content_type = new_type 362: end
content_type_set? ()
Checks to see if content negotiation has already been performed. If it has, you can no longer modify the list of provided formats.
# File lib/merb/mixins/responder.rb, line 331 331: def content_type_set? 332: !@_content_type.nil? 333: end
does_not_provide (*formats)
Removes formats from the list of provided formats for this particular request. Usually used to remove formats from a single action. See also the controller-level provides that affects all actions in a controller.
# File lib/merb/mixins/responder.rb, line 295 295: def does_not_provide(*formats) 296: formats.flatten! 297: self.provided_formats -= formats 298: formats.each { |fmt| self.provided_format_arguments.delete(fmt) } 299: end
only_provides (*formats)
Sets list of provided formats for this particular request. Usually used to limit formats to a single action. See also the controller-level provides that affects all actions in a controller.
# File lib/merb/mixins/responder.rb, line 287 287: def only_provides(*formats) 288: self.set_provided_formats(*formats) 289: end
provided_format_arguments ()
Returns a Hash of arguments for format methods
# File lib/merb/mixins/responder.rb, line 263 263: def provided_format_arguments 264: @_provided_format_arguments ||= Hash.new.replace(class_provided_format_arguments) 265: end
provided_format_arguments_for (fmt)
Returns the arguments (if any) for the mime_transform_method call
# File lib/merb/mixins/responder.rb, line 268 268: def provided_format_arguments_for(fmt) 269: self.provided_format_arguments[fmt] || Merb.mime_transform_method_defaults(fmt) 270: end
provided_formats ()
Returns the current list of formats provided for this instance of the controller. It starts with what has been set in the controller (or :html by default) but can be modifed on a per-action basis.
# File lib/merb/mixins/responder.rb, line 245 245: def provided_formats 246: @_provided_formats ||= class_provided_formats.dup 247: end
provides (*formats, &block)
Adds formats to the list of provided formats for this particular request. Usually used to add formats to a single action. See also the controller-level provides that affects all actions in a controller.
# File lib/merb/mixins/responder.rb, line 275 275: def provides(*formats, &block) 276: raise_if_content_type_already_set! 277: options = self.class.extract_provides_options(formats, &block) 278: formats.each do |fmt| 279: self.provided_formats << fmt unless provided_formats.include?(fmt) 280: self.provided_format_arguments[fmt] = options unless options.nil? 281: end 282: end
set_provided_formats (*formats, &block)
Sets the provided formats for this action. Usually, you would use a combination of provides, only_provides and does_not_provide to manage this, but you can set it directly. If the last argument is a Hash or an Array, these are regarded as arguments to pass to the to_<mime_type> method as needed.
# File lib/merb/mixins/responder.rb, line 254 254: def set_provided_formats(*formats, &block) 255: raise_if_content_type_already_set! 256: @_provided_formats = [] 257: @_provided_format_arguments = {} 258: provides(*formats.flatten, &block) 259: end