mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Move respond_with to the responders gem
respond_with (and consequently the class-level respond_to) are being removed from Rails. Instead of moving it to a 3rd library, the functionality will be moved to responders gem (at github.com/plataformatec/responders) which already provides some responders extensions.
This commit is contained in:
parent
aee1ba4233
commit
ee77770d57
4 changed files with 8 additions and 1265 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
* Move `respond_with` (and the class-level `respond_to`) to
|
||||||
|
the `responders` gem.
|
||||||
|
|
||||||
|
*José Valim*
|
||||||
|
|
||||||
* When your templates change, browser caches bust automatically.
|
* When your templates change, browser caches bust automatically.
|
||||||
|
|
||||||
New default: the template digest is automatically included in your ETags.
|
New default: the template digest is automatically included in your ETags.
|
||||||
|
|
|
@ -5,58 +5,6 @@ module ActionController #:nodoc:
|
||||||
module MimeResponds
|
module MimeResponds
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
|
||||||
class_attribute :responder, :mimes_for_respond_to
|
|
||||||
self.responder = ActionController::Responder
|
|
||||||
clear_respond_to
|
|
||||||
end
|
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
# Defines mime types that are rendered by default when invoking
|
|
||||||
# <tt>respond_with</tt>.
|
|
||||||
#
|
|
||||||
# respond_to :html, :xml, :json
|
|
||||||
#
|
|
||||||
# Specifies that all actions in the controller respond to requests
|
|
||||||
# for <tt>:html</tt>, <tt>:xml</tt> and <tt>:json</tt>.
|
|
||||||
#
|
|
||||||
# To specify on per-action basis, use <tt>:only</tt> and
|
|
||||||
# <tt>:except</tt> with an array of actions or a single action:
|
|
||||||
#
|
|
||||||
# respond_to :html
|
|
||||||
# respond_to :xml, :json, except: [ :edit ]
|
|
||||||
#
|
|
||||||
# This specifies that all actions respond to <tt>:html</tt>
|
|
||||||
# and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
|
|
||||||
# <tt>:json</tt>.
|
|
||||||
#
|
|
||||||
# respond_to :json, only: :create
|
|
||||||
#
|
|
||||||
# This specifies that the <tt>:create</tt> action and no other responds
|
|
||||||
# to <tt>:json</tt>.
|
|
||||||
def respond_to(*mimes)
|
|
||||||
options = mimes.extract_options!
|
|
||||||
|
|
||||||
only_actions = Array(options.delete(:only)).map(&:to_s)
|
|
||||||
except_actions = Array(options.delete(:except)).map(&:to_s)
|
|
||||||
|
|
||||||
new = mimes_for_respond_to.dup
|
|
||||||
mimes.each do |mime|
|
|
||||||
mime = mime.to_sym
|
|
||||||
new[mime] = {}
|
|
||||||
new[mime][:only] = only_actions unless only_actions.empty?
|
|
||||||
new[mime][:except] = except_actions unless except_actions.empty?
|
|
||||||
end
|
|
||||||
self.mimes_for_respond_to = new.freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
# Clear all mime types in <tt>respond_to</tt>.
|
|
||||||
#
|
|
||||||
def clear_respond_to
|
|
||||||
self.mimes_for_respond_to = Hash.new.freeze
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Without web-service support, an action which collects the data for displaying a list of people
|
# Without web-service support, an action which collects the data for displaying a list of people
|
||||||
# might look something like this:
|
# might look something like this:
|
||||||
#
|
#
|
||||||
|
@ -253,189 +201,13 @@ module ActionController #:nodoc:
|
||||||
def respond_to(*mimes, &block)
|
def respond_to(*mimes, &block)
|
||||||
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
|
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
|
||||||
|
|
||||||
if collector = retrieve_collector_from_mimes(mimes, &block)
|
|
||||||
response = collector.response
|
|
||||||
response ? response.call : render({})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# For a given controller action, respond_with generates an appropriate
|
|
||||||
# response based on the mime-type requested by the client.
|
|
||||||
#
|
|
||||||
# If the method is called with just a resource, as in this example -
|
|
||||||
#
|
|
||||||
# class PeopleController < ApplicationController
|
|
||||||
# respond_to :html, :xml, :json
|
|
||||||
#
|
|
||||||
# def index
|
|
||||||
# @people = Person.all
|
|
||||||
# respond_with @people
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# then the mime-type of the response is typically selected based on the
|
|
||||||
# request's Accept header and the set of available formats declared
|
|
||||||
# by previous calls to the controller's class method +respond_to+. Alternatively
|
|
||||||
# the mime-type can be selected by explicitly setting <tt>request.format</tt> in
|
|
||||||
# the controller.
|
|
||||||
#
|
|
||||||
# If an acceptable format is not identified, the application returns a
|
|
||||||
# '406 - not acceptable' status. Otherwise, the default response is to render
|
|
||||||
# a template named after the current action and the selected format,
|
|
||||||
# e.g. <tt>index.html.erb</tt>. If no template is available, the behavior
|
|
||||||
# depends on the selected format:
|
|
||||||
#
|
|
||||||
# * for an html response - if the request method is +get+, an exception
|
|
||||||
# is raised but for other requests such as +post+ the response
|
|
||||||
# depends on whether the resource has any validation errors (i.e.
|
|
||||||
# assuming that an attempt has been made to save the resource,
|
|
||||||
# e.g. by a +create+ action) -
|
|
||||||
# 1. If there are no errors, i.e. the resource
|
|
||||||
# was saved successfully, the response +redirect+'s to the resource
|
|
||||||
# i.e. its +show+ action.
|
|
||||||
# 2. If there are validation errors, the response
|
|
||||||
# renders a default action, which is <tt>:new</tt> for a
|
|
||||||
# +post+ request or <tt>:edit</tt> for +patch+ or +put+.
|
|
||||||
# Thus an example like this -
|
|
||||||
#
|
|
||||||
# respond_to :html, :xml
|
|
||||||
#
|
|
||||||
# def create
|
|
||||||
# @user = User.new(params[:user])
|
|
||||||
# flash[:notice] = 'User was successfully created.' if @user.save
|
|
||||||
# respond_with(@user)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# is equivalent, in the absence of <tt>create.html.erb</tt>, to -
|
|
||||||
#
|
|
||||||
# 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 }
|
|
||||||
# else
|
|
||||||
# format.html { render action: "new" }
|
|
||||||
# format.xml { render xml: @user }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# * for a JavaScript request - if the template isn't found, an exception is
|
|
||||||
# raised.
|
|
||||||
# * for other requests - i.e. data formats such as xml, json, csv etc, if
|
|
||||||
# the resource passed to +respond_with+ responds to <code>to_<format></code>,
|
|
||||||
# the method attempts to render the resource in the requested format
|
|
||||||
# directly, e.g. for an xml request, the response is equivalent to calling
|
|
||||||
# <code>render xml: resource</code>.
|
|
||||||
#
|
|
||||||
# === Nested resources
|
|
||||||
#
|
|
||||||
# As outlined above, the +resources+ argument passed to +respond_with+
|
|
||||||
# can play two roles. It can be used to generate the redirect url
|
|
||||||
# for successful html requests (e.g. for +create+ actions when
|
|
||||||
# no template exists), while for formats other than html and JavaScript
|
|
||||||
# it is the object that gets rendered, by being converted directly to the
|
|
||||||
# required format (again assuming no template exists).
|
|
||||||
#
|
|
||||||
# For redirecting successful html requests, +respond_with+ also supports
|
|
||||||
# the use of nested resources, which are supplied in the same way as
|
|
||||||
# in <code>form_for</code> and <code>polymorphic_url</code>. For example -
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
# This would cause +respond_with+ to redirect to <code>project_task_url</code>
|
|
||||||
# instead of <code>task_url</code>. For request formats other than html or
|
|
||||||
# JavaScript, if multiple resources are passed in this way, it is the last
|
|
||||||
# one specified that is rendered.
|
|
||||||
#
|
|
||||||
# === Customizing response behavior
|
|
||||||
#
|
|
||||||
# Like +respond_to+, +respond_with+ may also be called with a block that
|
|
||||||
# can be used to overwrite any of the default responses, e.g. -
|
|
||||||
#
|
|
||||||
# def create
|
|
||||||
# @user = User.new(params[:user])
|
|
||||||
# flash[:notice] = "User was successfully created." if @user.save
|
|
||||||
#
|
|
||||||
# respond_with(@user) do |format|
|
|
||||||
# format.html { render }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# The argument passed to the block is an ActionController::MimeResponds::Collector
|
|
||||||
# object which stores the responses for the formats defined within the
|
|
||||||
# block. Note that formats with responses defined explicitly in this way
|
|
||||||
# do not have to first be declared using the class method +respond_to+.
|
|
||||||
#
|
|
||||||
# Also, a hash passed to +respond_with+ immediately after the specified
|
|
||||||
# resource(s) is interpreted as a set of options relevant to all
|
|
||||||
# formats. Any option accepted by +render+ can be used, e.g.
|
|
||||||
# respond_with @people, status: 200
|
|
||||||
# However, note that these options are ignored after an unsuccessful attempt
|
|
||||||
# to save a resource, e.g. when automatically rendering <tt>:new</tt>
|
|
||||||
# after a post request.
|
|
||||||
#
|
|
||||||
# Two additional options are relevant specifically to +respond_with+ -
|
|
||||||
# 1. <tt>:location</tt> - overwrites the default redirect location used after
|
|
||||||
# a successful html +post+ request.
|
|
||||||
# 2. <tt>:action</tt> - overwrites the default render action used after an
|
|
||||||
# unsuccessful html +post+ request.
|
|
||||||
def respond_with(*resources, &block)
|
|
||||||
if self.class.mimes_for_respond_to.empty?
|
|
||||||
raise "In order to use respond_with, first you need to declare the " \
|
|
||||||
"formats your controller responds to in the class level."
|
|
||||||
end
|
|
||||||
|
|
||||||
if collector = retrieve_collector_from_mimes(&block)
|
|
||||||
options = resources.size == 1 ? {} : resources.extract_options!
|
|
||||||
options = options.clone
|
|
||||||
options[:default_response] = collector.response
|
|
||||||
(options.delete(:responder) || self.class.responder).call(self, resources, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# Collect mimes declared in the class method respond_to valid for the
|
|
||||||
# current action.
|
|
||||||
def collect_mimes_from_class_level #:nodoc:
|
|
||||||
action = action_name.to_s
|
|
||||||
|
|
||||||
self.class.mimes_for_respond_to.keys.select do |mime|
|
|
||||||
config = self.class.mimes_for_respond_to[mime]
|
|
||||||
|
|
||||||
if config[:except]
|
|
||||||
!config[:except].include?(action)
|
|
||||||
elsif config[:only]
|
|
||||||
config[:only].include?(action)
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a Collector object containing the appropriate mime-type response
|
|
||||||
# for the current request, based on the available responses defined by a block.
|
|
||||||
# In typical usage this is the block passed to +respond_with+ or +respond_to+.
|
|
||||||
#
|
|
||||||
# Sends :not_acceptable to the client and returns nil if no suitable format
|
|
||||||
# is available.
|
|
||||||
def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
|
|
||||||
mimes ||= collect_mimes_from_class_level
|
|
||||||
collector = Collector.new(mimes, request.variant)
|
collector = Collector.new(mimes, request.variant)
|
||||||
block.call(collector) if block_given?
|
block.call(collector) if block_given?
|
||||||
format = collector.negotiate_format(request)
|
|
||||||
|
|
||||||
if format
|
if format = collector.negotiate_format(request)
|
||||||
_process_format(format)
|
_process_format(format)
|
||||||
collector
|
response = collector.response
|
||||||
|
response ? response.call : render({})
|
||||||
else
|
else
|
||||||
raise ActionController::UnknownFormat
|
raise ActionController::UnknownFormat
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,297 +0,0 @@
|
||||||
require 'active_support/json'
|
|
||||||
|
|
||||||
module ActionController #:nodoc:
|
|
||||||
# Responsible for exposing a resource to different mime requests,
|
|
||||||
# usually depending on the HTTP verb. The responder is triggered when
|
|
||||||
# <code>respond_with</code> is called. The simplest case to study is a GET request:
|
|
||||||
#
|
|
||||||
# class PeopleController < ApplicationController
|
|
||||||
# respond_to :html, :xml, :json
|
|
||||||
#
|
|
||||||
# def index
|
|
||||||
# @people = Person.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.
|
|
||||||
#
|
|
||||||
# === Built-in HTTP verb semantics
|
|
||||||
#
|
|
||||||
# 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 PATCH/PUT and DELETE requests.
|
|
||||||
#
|
|
||||||
# === Nested resources
|
|
||||||
#
|
|
||||||
# You can supply nested resources as you do in <code>form_for</code> and <code>polymorphic_url</code>.
|
|
||||||
# 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.tasks.build(params[:task])
|
|
||||||
# flash[:notice] = 'Task was successfully created.' if @task.save
|
|
||||||
# respond_with(@project, @task)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Giving several resources ensures that the responder will redirect to
|
|
||||||
# <code>project_task_url</code> instead of <code>task_url</code>.
|
|
||||||
#
|
|
||||||
# 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)
|
|
||||||
#
|
|
||||||
# Note that if you give an array, it will be treated as a collection,
|
|
||||||
# so the following is not equivalent:
|
|
||||||
#
|
|
||||||
# respond_with [@project, :manager, @task]
|
|
||||||
#
|
|
||||||
# === Custom options
|
|
||||||
#
|
|
||||||
# <code>respond_with</code> also allows you to pass options that are forwarded
|
|
||||||
# to the underlying render call. Those options are only applied for success
|
|
||||||
# scenarios. For instance, you can do the following in the create method above:
|
|
||||||
#
|
|
||||||
# def create
|
|
||||||
# @project = Project.find(params[:project_id])
|
|
||||||
# @task = @project.tasks.build(params[:task])
|
|
||||||
# flash[:notice] = 'Task was successfully created.' if @task.save
|
|
||||||
# respond_with(@project, @task, status: 201)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# This will return status 201 if the task was saved successfully. If not,
|
|
||||||
# it will simply ignore the given options and return status 422 and the
|
|
||||||
# resource errors. You can also override the location to redirect to:
|
|
||||||
#
|
|
||||||
# respond_with(@project, location: root_path)
|
|
||||||
#
|
|
||||||
# To customize the failure scenario, you can pass a block to
|
|
||||||
# <code>respond_with</code>:
|
|
||||||
#
|
|
||||||
# def create
|
|
||||||
# @project = Project.find(params[:project_id])
|
|
||||||
# @task = @project.tasks.build(params[:task])
|
|
||||||
# respond_with(@project, @task, status: 201) do |format|
|
|
||||||
# if @task.save
|
|
||||||
# flash[:notice] = 'Task was successfully created.'
|
|
||||||
# else
|
|
||||||
# format.html { render "some_special_template" }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>.
|
|
||||||
class Responder
|
|
||||||
attr_reader :controller, :request, :format, :resource, :resources, :options
|
|
||||||
|
|
||||||
DEFAULT_ACTIONS_FOR_VERBS = {
|
|
||||||
:post => :new,
|
|
||||||
:patch => :edit,
|
|
||||||
:put => :edit
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
delegate :head, :render, :redirect_to, :to => :controller
|
|
||||||
delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request
|
|
||||||
|
|
||||||
# Undefine :to_json and :to_yaml since it's defined on Object
|
|
||||||
undef_method(:to_json) if method_defined?(:to_json)
|
|
||||||
undef_method(:to_yaml) if method_defined?(:to_yaml)
|
|
||||||
|
|
||||||
# Initializes a new responder and invokes the proper format. If the format is
|
|
||||||
# not defined, call to_format.
|
|
||||||
#
|
|
||||||
def self.call(*args)
|
|
||||||
new(*args).respond
|
|
||||||
end
|
|
||||||
|
|
||||||
# Main entry point for responder responsible to dispatch to the proper format.
|
|
||||||
#
|
|
||||||
def respond
|
|
||||||
method = "to_#{format}"
|
|
||||||
respond_to?(method) ? send(method) : to_format
|
|
||||||
end
|
|
||||||
|
|
||||||
# HTML format does not render the resource, it always attempt to render a
|
|
||||||
# template.
|
|
||||||
#
|
|
||||||
def to_html
|
|
||||||
default_render
|
|
||||||
rescue ActionView::MissingTemplate => e
|
|
||||||
navigation_behavior(e)
|
|
||||||
end
|
|
||||||
|
|
||||||
# to_js simply tries to render a template. If no template is found, raises the error.
|
|
||||||
def to_js
|
|
||||||
default_render
|
|
||||||
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.
|
|
||||||
#
|
|
||||||
def to_format
|
|
||||||
if get? || !has_errors? || response_overridden?
|
|
||||||
default_render
|
|
||||||
else
|
|
||||||
display_errors
|
|
||||||
end
|
|
||||||
rescue ActionView::MissingTemplate => e
|
|
||||||
api_behavior(e)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
|
|
||||||
def navigation_behavior(error)
|
|
||||||
if get?
|
|
||||||
raise error
|
|
||||||
elsif has_errors? && default_action
|
|
||||||
render :action => default_action
|
|
||||||
else
|
|
||||||
redirect_to navigation_location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# This is the common behavior for formats associated with APIs, such as :xml and :json.
|
|
||||||
def api_behavior(error)
|
|
||||||
raise error unless resourceful?
|
|
||||||
raise MissingRenderer.new(format) unless has_renderer?
|
|
||||||
|
|
||||||
if get?
|
|
||||||
display resource
|
|
||||||
elsif post?
|
|
||||||
display resource, :status => :created, :location => api_location
|
|
||||||
else
|
|
||||||
head :no_content
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Checks whether the resource responds to the current format or not.
|
|
||||||
#
|
|
||||||
def resourceful?
|
|
||||||
resource.respond_to?("to_#{format}")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the resource location by retrieving it from the options or
|
|
||||||
# returning the resources array.
|
|
||||||
#
|
|
||||||
def resource_location
|
|
||||||
options[:location] || resources
|
|
||||||
end
|
|
||||||
alias :navigation_location :resource_location
|
|
||||||
alias :api_location :resource_location
|
|
||||||
|
|
||||||
# If a response block was given, use it, otherwise call render on
|
|
||||||
# controller.
|
|
||||||
#
|
|
||||||
def default_render
|
|
||||||
if @default_response
|
|
||||||
@default_response.call(options)
|
|
||||||
else
|
|
||||||
controller.default_render(options)
|
|
||||||
end
|
|
||||||
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
|
|
||||||
#
|
|
||||||
def display(resource, given_options={})
|
|
||||||
controller.render given_options.merge!(options).merge!(format => resource)
|
|
||||||
end
|
|
||||||
|
|
||||||
def display_errors
|
|
||||||
controller.render format => resource_errors, :status => :unprocessable_entity
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check whether the resource has errors.
|
|
||||||
#
|
|
||||||
def has_errors?
|
|
||||||
resource.respond_to?(:errors) && !resource.errors.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check whether the necessary Renderer is available
|
|
||||||
def has_renderer?
|
|
||||||
Renderers::RENDERERS.include?(format)
|
|
||||||
end
|
|
||||||
|
|
||||||
# By default, render the <code>:edit</code> action for HTML requests with errors, unless
|
|
||||||
# the verb was POST.
|
|
||||||
#
|
|
||||||
def default_action
|
|
||||||
@action ||= DEFAULT_ACTIONS_FOR_VERBS[request.request_method_symbol]
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_errors
|
|
||||||
respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors
|
|
||||||
end
|
|
||||||
|
|
||||||
def json_resource_errors
|
|
||||||
{:errors => resource.errors}
|
|
||||||
end
|
|
||||||
|
|
||||||
def response_overridden?
|
|
||||||
@default_response.present?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,737 +0,0 @@
|
||||||
require 'abstract_unit'
|
|
||||||
require 'controller/fake_models'
|
|
||||||
|
|
||||||
class RespondWithController < ActionController::Base
|
|
||||||
class CustomerWithJson < Customer
|
|
||||||
def to_json; super; end
|
|
||||||
end
|
|
||||||
|
|
||||||
respond_to :html, :json, :touch
|
|
||||||
respond_to :xml, :except => :using_resource_with_block
|
|
||||||
respond_to :js, :only => [ :using_resource_with_block, :using_resource, 'using_hash_resource' ]
|
|
||||||
|
|
||||||
def using_resource
|
|
||||||
respond_with(resource)
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_hash_resource
|
|
||||||
respond_with({:result => resource})
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_resource_with_block
|
|
||||||
respond_with(resource) do |format|
|
|
||||||
format.csv { render :text => "CSV" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_resource_with_overwrite_block
|
|
||||||
respond_with(resource) do |format|
|
|
||||||
format.html { render :text => "HTML" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_resource_with_collection
|
|
||||||
respond_with([resource, Customer.new("jamis", 9)])
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_resource_with_parent
|
|
||||||
respond_with(Quiz::Store.new("developer?", 11), Customer.new("david", 13))
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_resource_with_status_and_location
|
|
||||||
respond_with(resource, :location => "http://test.host/", :status => :created)
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_resource_with_json
|
|
||||||
respond_with(CustomerWithJson.new("david", request.delete? ? nil : 13))
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_invalid_resource_with_template
|
|
||||||
respond_with(resource)
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_options_with_template
|
|
||||||
@customer = resource
|
|
||||||
respond_with(@customer, :status => 123, :location => "http://test.host/")
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_resource_with_responder
|
|
||||||
responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" }
|
|
||||||
respond_with(resource, :responder => responder)
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_resource_with_action
|
|
||||||
respond_with(resource, :action => :foo) do |format|
|
|
||||||
format.html { raise ActionView::MissingTemplate.new([], "bar", ["foo"], {}, false) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def using_responder_with_respond
|
|
||||||
responder = Class.new(ActionController::Responder) do
|
|
||||||
def respond; @controller.render :text => "respond #{format}"; end
|
|
||||||
end
|
|
||||||
respond_with(resource, :responder => responder)
|
|
||||||
end
|
|
||||||
|
|
||||||
def respond_with_additional_params
|
|
||||||
@params = RespondWithController.params
|
|
||||||
respond_with({:result => resource}, @params)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
def self.params
|
|
||||||
{
|
|
||||||
:foo => 'bar'
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource
|
|
||||||
Customer.new("david", request.delete? ? nil : 13)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class InheritedRespondWithController < RespondWithController
|
|
||||||
clear_respond_to
|
|
||||||
respond_to :xml, :json
|
|
||||||
|
|
||||||
def index
|
|
||||||
respond_with(resource) do |format|
|
|
||||||
format.json { render :text => "JSON" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RenderJsonRespondWithController < RespondWithController
|
|
||||||
clear_respond_to
|
|
||||||
respond_to :json
|
|
||||||
|
|
||||||
def index
|
|
||||||
respond_with(resource) do |format|
|
|
||||||
format.json { render :json => RenderJsonTestException.new('boom') }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
resource = ValidatedCustomer.new(params[:name], 1)
|
|
||||||
respond_with(resource) do |format|
|
|
||||||
format.json do
|
|
||||||
if resource.errors.empty?
|
|
||||||
render :json => { :valid => true }
|
|
||||||
else
|
|
||||||
render :json => { :valid => false }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class CsvRespondWithController < ActionController::Base
|
|
||||||
respond_to :csv
|
|
||||||
|
|
||||||
class RespondWithCsv
|
|
||||||
def to_csv
|
|
||||||
"c,s,v"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def index
|
|
||||||
respond_with(RespondWithCsv.new)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class EmptyRespondWithController < ActionController::Base
|
|
||||||
def index
|
|
||||||
respond_with(Customer.new("david", 13))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RespondWithControllerTest < ActionController::TestCase
|
|
||||||
def setup
|
|
||||||
super
|
|
||||||
@request.host = "www.example.com"
|
|
||||||
Mime::Type.register_alias('text/html', :iphone)
|
|
||||||
Mime::Type.register_alias('text/html', :touch)
|
|
||||||
Mime::Type.register('text/x-mobile', :mobile)
|
|
||||||
end
|
|
||||||
|
|
||||||
def teardown
|
|
||||||
super
|
|
||||||
Mime::Type.unregister(:iphone)
|
|
||||||
Mime::Type.unregister(:touch)
|
|
||||||
Mime::Type.unregister(:mobile)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_respond_with_shouldnt_modify_original_hash
|
|
||||||
get :respond_with_additional_params
|
|
||||||
assert_equal RespondWithController.params, assigns(:params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
get :using_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal "<name>david</name>", @response.body
|
|
||||||
|
|
||||||
@request.accept = "application/json"
|
|
||||||
assert_raise ActionView::MissingTemplate do
|
|
||||||
get :using_resource
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_js_simply_tries_to_render_the_template
|
|
||||||
@request.accept = "text/javascript"
|
|
||||||
get :using_resource
|
|
||||||
assert_equal "text/javascript", @response.content_type
|
|
||||||
assert_equal "alert(\"Hi\");", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_hash_resource_with_js_raises_an_error_if_template_cant_be_found
|
|
||||||
@request.accept = "text/javascript"
|
|
||||||
assert_raise ActionView::MissingTemplate do
|
|
||||||
get :using_hash_resource
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_hash_resource
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
get :using_hash_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n <name>david</name>\n</hash>\n", @response.body
|
|
||||||
|
|
||||||
@request.accept = "application/json"
|
|
||||||
get :using_hash_resource
|
|
||||||
assert_equal "application/json", @response.content_type
|
|
||||||
assert @response.body.include?("result")
|
|
||||||
assert @response.body.include?('"name":"david"')
|
|
||||||
assert @response.body.include?('"id":13')
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_hash_resource_with_post
|
|
||||||
@request.accept = "application/json"
|
|
||||||
assert_raise ArgumentError, "Nil location provided. Can't build URI." do
|
|
||||||
post :using_hash_resource
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_block
|
|
||||||
@request.accept = "*/*"
|
|
||||||
get :using_resource_with_block
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 'Hello world!', @response.body
|
|
||||||
|
|
||||||
@request.accept = "text/csv"
|
|
||||||
get :using_resource_with_block
|
|
||||||
assert_equal "text/csv", @response.content_type
|
|
||||||
assert_equal "CSV", @response.body
|
|
||||||
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
get :using_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal "<name>david</name>", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_overwrite_block
|
|
||||||
get :using_resource_with_overwrite_block
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal "HTML", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_not_acceptable
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
assert_raises(ActionController::UnknownFormat) do
|
|
||||||
get :using_resource_with_block
|
|
||||||
end
|
|
||||||
|
|
||||||
@request.accept = "text/javascript"
|
|
||||||
assert_raises(ActionController::UnknownFormat) do
|
|
||||||
get :using_resource_with_overwrite_block
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_post_with_html_redirects_on_success
|
|
||||||
with_test_route_set do
|
|
||||||
post :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 302, @response.status
|
|
||||||
assert_equal "http://www.example.com/customers/13", @response.location
|
|
||||||
assert @response.redirect?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_post_with_html_rerender_on_failure
|
|
||||||
with_test_route_set do
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
post :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 200, @response.status
|
|
||||||
assert_equal "New world!\n", @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_post_with_xml_yields_created_on_success
|
|
||||||
with_test_route_set do
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
post :using_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 201, @response.status
|
|
||||||
assert_equal "<name>david</name>", @response.body
|
|
||||||
assert_equal "http://www.example.com/customers/13", @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_post_with_xml_yields_unprocessable_entity_on_failure
|
|
||||||
with_test_route_set do
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
post :using_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
assert_equal errors.to_xml, @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_post_with_json_yields_unprocessable_entity_on_failure
|
|
||||||
with_test_route_set do
|
|
||||||
@request.accept = "application/json"
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
post :using_resource
|
|
||||||
assert_equal "application/json", @response.content_type
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
errors = {:errors => errors}
|
|
||||||
assert_equal errors.to_json, @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_patch_with_html_redirects_on_success
|
|
||||||
with_test_route_set do
|
|
||||||
patch :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 302, @response.status
|
|
||||||
assert_equal "http://www.example.com/customers/13", @response.location
|
|
||||||
assert @response.redirect?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_patch_with_html_rerender_on_failure
|
|
||||||
with_test_route_set do
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
patch :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 200, @response.status
|
|
||||||
assert_equal "Edit world!\n", @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_patch_with_html_rerender_on_failure_even_on_method_override
|
|
||||||
with_test_route_set do
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
@request.env["rack.methodoverride.original_method"] = "POST"
|
|
||||||
patch :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 200, @response.status
|
|
||||||
assert_equal "Edit world!\n", @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_put_with_html_redirects_on_success
|
|
||||||
with_test_route_set do
|
|
||||||
put :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 302, @response.status
|
|
||||||
assert_equal "http://www.example.com/customers/13", @response.location
|
|
||||||
assert @response.redirect?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_put_with_html_rerender_on_failure
|
|
||||||
with_test_route_set do
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
put :using_resource
|
|
||||||
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 200, @response.status
|
|
||||||
assert_equal "Edit world!\n", @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_put_with_html_rerender_on_failure_even_on_method_override
|
|
||||||
with_test_route_set do
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
@request.env["rack.methodoverride.original_method"] = "POST"
|
|
||||||
put :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 200, @response.status
|
|
||||||
assert_equal "Edit world!\n", @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_put_with_xml_yields_no_content_on_success
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
put :using_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 204, @response.status
|
|
||||||
assert_equal "", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_put_with_json_yields_no_content_on_success
|
|
||||||
@request.accept = "application/json"
|
|
||||||
put :using_resource_with_json
|
|
||||||
assert_equal "application/json", @response.content_type
|
|
||||||
assert_equal 204, @response.status
|
|
||||||
assert_equal "", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_put_with_xml_yields_unprocessable_entity_on_failure
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
put :using_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
assert_equal errors.to_xml, @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_put_with_json_yields_unprocessable_entity_on_failure
|
|
||||||
@request.accept = "application/json"
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
put :using_resource
|
|
||||||
assert_equal "application/json", @response.content_type
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
errors = {:errors => errors}
|
|
||||||
assert_equal errors.to_json, @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_delete_with_html_redirects_on_success
|
|
||||||
with_test_route_set do
|
|
||||||
Customer.any_instance.stubs(:destroyed?).returns(true)
|
|
||||||
delete :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 302, @response.status
|
|
||||||
assert_equal "http://www.example.com/customers", @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_delete_with_xml_yields_no_content_on_success
|
|
||||||
Customer.any_instance.stubs(:destroyed?).returns(true)
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
delete :using_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 204, @response.status
|
|
||||||
assert_equal "", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_delete_with_json_yields_no_content_on_success
|
|
||||||
Customer.any_instance.stubs(:destroyed?).returns(true)
|
|
||||||
@request.accept = "application/json"
|
|
||||||
delete :using_resource_with_json
|
|
||||||
assert_equal "application/json", @response.content_type
|
|
||||||
assert_equal 204, @response.status
|
|
||||||
assert_equal "", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_for_delete_with_html_redirects_on_failure
|
|
||||||
with_test_route_set do
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
Customer.any_instance.stubs(:destroyed?).returns(false)
|
|
||||||
delete :using_resource
|
|
||||||
assert_equal "text/html", @response.content_type
|
|
||||||
assert_equal 302, @response.status
|
|
||||||
assert_equal "http://www.example.com/customers", @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_parent_for_get
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
get :using_resource_with_parent
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 200, @response.status
|
|
||||||
assert_equal "<name>david</name>", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_parent_for_post
|
|
||||||
with_test_route_set do
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
|
|
||||||
post :using_resource_with_parent
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 201, @response.status
|
|
||||||
assert_equal "<name>david</name>", @response.body
|
|
||||||
assert_equal "http://www.example.com/quiz_stores/11/customers/13", @response.location
|
|
||||||
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
post :using_resource
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
assert_equal errors.to_xml, @response.body
|
|
||||||
assert_nil @response.location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_collection
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
get :using_resource_with_collection
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal 200, @response.status
|
|
||||||
assert_match(/<name>david<\/name>/, @response.body)
|
|
||||||
assert_match(/<name>jamis<\/name>/, @response.body)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_action
|
|
||||||
@controller.instance_eval do
|
|
||||||
def render(params={})
|
|
||||||
self.response_body = "#{params[:action]} - #{formats}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
|
|
||||||
post :using_resource_with_action
|
|
||||||
assert_equal "foo - #{[:html].to_s}", @controller.response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_respond_as_responder_entry_point
|
|
||||||
@request.accept = "text/html"
|
|
||||||
get :using_responder_with_respond
|
|
||||||
assert_equal "respond html", @response.body
|
|
||||||
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
get :using_responder_with_respond
|
|
||||||
assert_equal "respond xml", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_clear_respond_to
|
|
||||||
@controller = InheritedRespondWithController.new
|
|
||||||
@request.accept = "text/html"
|
|
||||||
assert_raises(ActionController::UnknownFormat) do
|
|
||||||
get :index
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_first_in_respond_to_has_higher_priority
|
|
||||||
@controller = InheritedRespondWithController.new
|
|
||||||
@request.accept = "*/*"
|
|
||||||
get :index
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_equal "<name>david</name>", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_block_inside_respond_with_is_rendered
|
|
||||||
@controller = InheritedRespondWithController.new
|
|
||||||
@request.accept = "application/json"
|
|
||||||
get :index
|
|
||||||
assert_equal "JSON", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_json_object_responds_to_str_still_produce_json
|
|
||||||
@controller = RenderJsonRespondWithController.new
|
|
||||||
@request.accept = "application/json"
|
|
||||||
get :index, :format => :json
|
|
||||||
assert_match(/"message":"boom"/, @response.body)
|
|
||||||
assert_match(/"error":"RenderJsonTestException"/, @response.body)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_api_response_with_valid_resource_respect_override_block
|
|
||||||
@controller = RenderJsonRespondWithController.new
|
|
||||||
post :create, :name => "sikachu", :format => :json
|
|
||||||
assert_equal '{"valid":true}', @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_api_response_with_invalid_resource_respect_override_block
|
|
||||||
@controller = RenderJsonRespondWithController.new
|
|
||||||
post :create, :name => "david", :format => :json
|
|
||||||
assert_equal '{"valid":false}', @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_no_double_render_is_raised
|
|
||||||
@request.accept = "text/html"
|
|
||||||
assert_raise ActionView::MissingTemplate do
|
|
||||||
get :using_resource
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_status_and_location
|
|
||||||
@request.accept = "text/html"
|
|
||||||
post :using_resource_with_status_and_location
|
|
||||||
assert @response.redirect?
|
|
||||||
assert_equal "http://test.host/", @response.location
|
|
||||||
|
|
||||||
@request.accept = "application/xml"
|
|
||||||
get :using_resource_with_status_and_location
|
|
||||||
assert_equal 201, @response.status
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_status_and_location_with_invalid_resource
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
|
|
||||||
@request.accept = "text/xml"
|
|
||||||
|
|
||||||
post :using_resource_with_status_and_location
|
|
||||||
assert_equal errors.to_xml, @response.body
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
assert_equal nil, @response.location
|
|
||||||
|
|
||||||
put :using_resource_with_status_and_location
|
|
||||||
assert_equal errors.to_xml, @response.body
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
assert_equal nil, @response.location
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_invalid_resource_with_template
|
|
||||||
errors = { :name => :invalid }
|
|
||||||
Customer.any_instance.stubs(:errors).returns(errors)
|
|
||||||
|
|
||||||
@request.accept = "text/xml"
|
|
||||||
|
|
||||||
post :using_invalid_resource_with_template
|
|
||||||
assert_equal errors.to_xml, @response.body
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
assert_equal nil, @response.location
|
|
||||||
|
|
||||||
put :using_invalid_resource_with_template
|
|
||||||
assert_equal errors.to_xml, @response.body
|
|
||||||
assert_equal 422, @response.status
|
|
||||||
assert_equal nil, @response.location
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_options_with_template
|
|
||||||
@request.accept = "text/xml"
|
|
||||||
|
|
||||||
post :using_options_with_template
|
|
||||||
assert_equal "<customer-name>david</customer-name>", @response.body
|
|
||||||
assert_equal 123, @response.status
|
|
||||||
assert_equal "http://test.host/", @response.location
|
|
||||||
|
|
||||||
put :using_options_with_template
|
|
||||||
assert_equal "<customer-name>david</customer-name>", @response.body
|
|
||||||
assert_equal 123, @response.status
|
|
||||||
assert_equal "http://test.host/", @response.location
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_responder
|
|
||||||
get :using_resource_with_responder
|
|
||||||
assert_equal "Resource name is david", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_using_resource_with_set_responder
|
|
||||||
RespondWithController.responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" }
|
|
||||||
get :using_resource
|
|
||||||
assert_equal "Resource name is david", @response.body
|
|
||||||
ensure
|
|
||||||
RespondWithController.responder = ActionController::Responder
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_uses_renderer_if_an_api_behavior
|
|
||||||
ActionController::Renderers.add :csv do |obj, options|
|
|
||||||
send_data obj.to_csv, type: Mime::CSV
|
|
||||||
end
|
|
||||||
@controller = CsvRespondWithController.new
|
|
||||||
get :index, format: 'csv'
|
|
||||||
assert_equal Mime::CSV, @response.content_type
|
|
||||||
assert_equal "c,s,v", @response.body
|
|
||||||
ensure
|
|
||||||
ActionController::Renderers.remove :csv
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_raises_missing_renderer_if_an_api_behavior_with_no_renderer
|
|
||||||
@controller = CsvRespondWithController.new
|
|
||||||
assert_raise ActionController::MissingRenderer do
|
|
||||||
get :index, format: 'csv'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_removing_renderers
|
|
||||||
ActionController::Renderers.add :csv do |obj, options|
|
|
||||||
send_data obj.to_csv, type: Mime::CSV
|
|
||||||
end
|
|
||||||
@controller = CsvRespondWithController.new
|
|
||||||
@request.accept = "text/csv"
|
|
||||||
get :index, format: 'csv'
|
|
||||||
assert_equal Mime::CSV, @response.content_type
|
|
||||||
|
|
||||||
ActionController::Renderers.remove :csv
|
|
||||||
assert_raise ActionController::MissingRenderer do
|
|
||||||
get :index, format: 'csv'
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
ActionController::Renderers.remove :csv
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called
|
|
||||||
@controller = EmptyRespondWithController.new
|
|
||||||
@request.accept = "*/*"
|
|
||||||
assert_raise RuntimeError do
|
|
||||||
get :index
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def with_test_route_set
|
|
||||||
with_routing do |set|
|
|
||||||
set.draw do
|
|
||||||
resources :customers
|
|
||||||
resources :quiz_stores do
|
|
||||||
resources :customers
|
|
||||||
end
|
|
||||||
get ":controller/:action"
|
|
||||||
end
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class FlashResponder < ActionController::Responder
|
|
||||||
def initialize(controller, resources, options={})
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_html
|
|
||||||
controller.flash[:notice] = 'Success'
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class FlashResponderController < ActionController::Base
|
|
||||||
self.responder = FlashResponder
|
|
||||||
respond_to :html
|
|
||||||
|
|
||||||
def index
|
|
||||||
respond_with Object.new do |format|
|
|
||||||
format.html { render :text => 'HTML' }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class FlashResponderControllerTest < ActionController::TestCase
|
|
||||||
tests FlashResponderController
|
|
||||||
|
|
||||||
def test_respond_with_block_executed
|
|
||||||
get :index
|
|
||||||
assert_equal 'HTML', @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_flash_responder_executed
|
|
||||||
get :index
|
|
||||||
assert_equal 'Success', flash[:notice]
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue