Responder is responsible for exposing a
resource to different mime requests, usually depending on the HTTP verb.
The responder is triggered when respond_with
is called. The
simplest case to study is a GET request:
class PeopleController < ApplicationController respond_to :html, :xml, :json def index @people = Person.find(:all) respond_with(@people) end end
When a request comes in, for example for an XML response, three steps happen:
1) the responder searches for a template at people/index.xml; 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource; 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
The default Rails responder holds semantics for each HTTP verb. Depending on the content type, verb and the resource status, it will behave differently.
Using Rails default responder, a POST request for creating an object could be written as:
def create @user = User.new(params[:user]) flash[:notice] = 'User was successfully created.' if @user.save respond_with(@user) end
Which is exactly the same as:
def create @user = User.new(params[:user]) respond_to do |format| if @user.save flash[:notice] = 'User was successfully created.' format.html { redirect_to(@user) } format.xml { render :xml => @user, :status => :created, :location => @user } else format.html { render :action => "new" } format.xml { render :xml => @user.errors, :status => :unprocessable_entity } end end end
The same happens for PUT and DELETE requests.
You can supply nested resources as you do in form_for
and
polymorphic_url
. Consider the project has many tasks example.
The create action for TasksController would be like:
def create @project = Project.find(params[:project_id]) @task = @project.comments.build(params[:task]) flash[:notice] = 'Task was successfully created.' if @task.save respond_with(@project, @task) end
Giving an array of resources, you ensure that the responder will redirect
to project_task_url
instead of task_url
.
Namespaced and singleton resources require a symbol to be given, as in polymorphic urls. If a project has one manager which has many tasks, it should be invoked as:
respond_with(@project, :manager, @task)
Check polymorphic_url
documentation for more examples.
Initializes a new responder an invoke the proper format. If the format is not defined, call to_format.
# File lib/action_controller/metal/responder.rb, line 111 def self.call(*args) new(*args).respond end
# File lib/action_controller/metal/responder.rb, line 90 def initialize(controller, resources, options={}) @controller = controller @request = @controller.request @format = @controller.formats.first @resource = resources.last @resources = resources @options = options @action = options.delete(:action) @default_response = options.delete(:default_response) end
Main entry point for responder responsible to dispatch to the proper format.
# File lib/action_controller/metal/responder.rb, line 117 def respond method = :"to_#{format}" respond_to?(method) ? send(method) : to_format end
All other formats follow the procedure below. First we try to render a template, if the template is not available, we verify if the resource responds to :#to_format and display it.
# File lib/action_controller/metal/responder.rb, line 135 def to_format default_render rescue ActionView::MissingTemplate => e api_behavior(e) end
HTML format does not render the resource, it always attempt to render a template.
# File lib/action_controller/metal/responder.rb, line 125 def to_html default_render rescue ActionView::MissingTemplate => e navigation_behavior(e) end
This is the common behavior for "API" requests, like :xml and :json.
# File lib/action_controller/metal/responder.rb, line 155 def api_behavior(error) raise error unless resourceful? if get? display resource elsif has_errors? display resource.errors, :status => :unprocessable_entity elsif post? display resource, :status => :created, :location => api_location elsif has_empty_resource_definition? display empty_resource, :status => :ok else head :ok end end
By default, render the :edit
action for HTML requests with failure, unless the verb is
POST.
# File lib/action_controller/metal/responder.rb, line 223 def default_action @action ||= ACTIONS_FOR_VERBS[request.request_method_symbol] end
If a given response block was given, use it, otherwise call render on controller.
# File lib/action_controller/metal/responder.rb, line 189 def default_render @default_response.call end
Display is just a shortcut to render a resource with the current format.
display @user, :status => :ok
For XML requests it's equivalent to:
render :xml => @user, :status => :ok
Options sent by the user are also used:
respond_with(@user, :status => :created) display(@user, :status => :ok)
Results in:
render :xml => @user, :status => :created
# File lib/action_controller/metal/responder.rb, line 210 def display(resource, given_options={}) controller.render given_options.merge!(options).merge!(format => resource) end
Return a valid empty JSON resource
# File lib/action_controller/metal/responder.rb, line 241 def empty_json_resource "{}" end
Delegate to proper empty resource method
# File lib/action_controller/metal/responder.rb, line 235 def empty_resource send("empty_#{format}_resource") end
Check whether resource needs a specific definition of empty resource to be valid
# File lib/action_controller/metal/responder.rb, line 229 def has_empty_resource_definition? respond_to?("empty_#{format}_resource") end
Check whether the resource has errors.
# File lib/action_controller/metal/responder.rb, line 216 def has_errors? resource.respond_to?(:errors) && !resource.errors.empty? end
Returns the resource location by retrieving it from the options or returning the resources array.
# File lib/action_controller/metal/responder.rb, line 180 def resource_location options[:location] || resources end
Checks whether the resource responds to the current format or not.
# File lib/action_controller/metal/responder.rb, line 173 def resourceful? resource.respond_to?(:"to_#{format}") end