mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #22906 from bf4/rendering_exceptions
Add ActionController:Renderers test
This commit is contained in:
commit
8788c7ce84
3 changed files with 165 additions and 90 deletions
|
@ -11,6 +11,7 @@ module ActionController
|
|||
Renderers.remove(key)
|
||||
end
|
||||
|
||||
# See <tt>Responder#api_behavior</tt>
|
||||
class MissingRenderer < LoadError
|
||||
def initialize(format)
|
||||
super "No renderer defined for format: #{format}"
|
||||
|
@ -20,74 +21,25 @@ module ActionController
|
|||
module Renderers
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# A Set containing renderer names that correspond to available renderer procs.
|
||||
# Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
|
||||
RENDERERS = Set.new
|
||||
|
||||
included do
|
||||
class_attribute :_renderers
|
||||
self._renderers = Set.new.freeze
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Used in <tt>ActionController::Base</tt>
|
||||
# and <tt>ActionController::API</tt> to include all
|
||||
# renderers by default.
|
||||
module All
|
||||
extend ActiveSupport::Concern
|
||||
include Renderers
|
||||
|
||||
# Adds, by name, a renderer or renderers to the +_renderers+ available
|
||||
# to call within controller actions.
|
||||
#
|
||||
# It is useful when rendering from an <tt>ActionController::Metal</tt> controller or
|
||||
# otherwise to add an available renderer proc to a specific controller.
|
||||
#
|
||||
# Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
|
||||
# include <tt>ActionController::Renderers::All</tt>, making all renderers
|
||||
# avaialable in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
|
||||
#
|
||||
# Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
|
||||
# must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
|
||||
# and <tt>ActionController::Renderers</tt>, and have at lest one renderer.
|
||||
#
|
||||
# Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
|
||||
# you may specify which renderers to include by passing the renderer name or names to
|
||||
# +use_renderers+. For example, a controller that includes only the <tt>:json</tt> renderer
|
||||
# (+_render_with_renderer_json+) might look like:
|
||||
#
|
||||
# class MetalRenderingController < ActionController::Metal
|
||||
# include AbstractController::Rendering
|
||||
# include ActionController::Rendering
|
||||
# include ActionController::Renderers
|
||||
#
|
||||
# use_renderers :json
|
||||
#
|
||||
# def show
|
||||
# render json: record
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# You must specify a +use_renderer+, else the +controller.renderer+ and
|
||||
# +controller._renderers+ will be <tt>nil</tt>, and the action will fail.
|
||||
def use_renderers(*args)
|
||||
renderers = _renderers + args
|
||||
self._renderers = renderers.freeze
|
||||
included do
|
||||
self._renderers = RENDERERS
|
||||
end
|
||||
alias use_renderer use_renderers
|
||||
end
|
||||
|
||||
def render_to_body(options)
|
||||
_render_to_body_with_renderer(options) || super
|
||||
end
|
||||
|
||||
def _render_to_body_with_renderer(options)
|
||||
_renderers.each do |name|
|
||||
if options.key?(name)
|
||||
_process_options(options)
|
||||
method_name = Renderers._render_with_renderer_method_name(name)
|
||||
return send(method_name, options.delete(name), options)
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# A Set containing renderer names that correspond to available renderer procs.
|
||||
# Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
|
||||
RENDERERS = Set.new
|
||||
|
||||
def self._render_with_renderer_method_name(key)
|
||||
"_render_with_renderer_#{key}"
|
||||
end
|
||||
|
||||
# Adds a new renderer to call within controller actions.
|
||||
|
@ -137,13 +89,70 @@ module ActionController
|
|||
remove_method(method_name) if method_defined?(method_name)
|
||||
end
|
||||
|
||||
module All
|
||||
extend ActiveSupport::Concern
|
||||
include Renderers
|
||||
def self._render_with_renderer_method_name(key)
|
||||
"_render_with_renderer_#{key}"
|
||||
end
|
||||
|
||||
included do
|
||||
self._renderers = RENDERERS
|
||||
module ClassMethods
|
||||
|
||||
# Adds, by name, a renderer or renderers to the +_renderers+ available
|
||||
# to call within controller actions.
|
||||
#
|
||||
# It is useful when rendering from an <tt>ActionController::Metal</tt> controller or
|
||||
# otherwise to add an available renderer proc to a specific controller.
|
||||
#
|
||||
# Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
|
||||
# include <tt>ActionController::Renderers::All</tt>, making all renderers
|
||||
# avaialable in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
|
||||
#
|
||||
# Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
|
||||
# must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
|
||||
# and <tt>ActionController::Renderers</tt>, and have at lest one renderer.
|
||||
#
|
||||
# Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
|
||||
# you may specify which renderers to include by passing the renderer name or names to
|
||||
# +use_renderers+. For example, a controller that includes only the <tt>:json</tt> renderer
|
||||
# (+_render_with_renderer_json+) might look like:
|
||||
#
|
||||
# class MetalRenderingController < ActionController::Metal
|
||||
# include AbstractController::Rendering
|
||||
# include ActionController::Rendering
|
||||
# include ActionController::Renderers
|
||||
#
|
||||
# use_renderers :json
|
||||
#
|
||||
# def show
|
||||
# render json: record
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# You must specify a +use_renderer+, else the +controller.renderer+ and
|
||||
# +controller._renderers+ will be <tt>nil</tt>, and the action will fail.
|
||||
def use_renderers(*args)
|
||||
renderers = _renderers + args
|
||||
self._renderers = renderers.freeze
|
||||
end
|
||||
alias use_renderer use_renderers
|
||||
end
|
||||
|
||||
# Called by +render+ in <tt>AbstractController::Renderering</tt>
|
||||
# which sets the return value as the +response_body+.
|
||||
#
|
||||
# If no renderer is found, +super+ returns control to
|
||||
# <tt>ActionView::Rendering.render_to_body</tt>, if present.
|
||||
def render_to_body(options)
|
||||
_render_to_body_with_renderer(options) || super
|
||||
end
|
||||
|
||||
def _render_to_body_with_renderer(options)
|
||||
_renderers.each do |name|
|
||||
if options.key?(name)
|
||||
_process_options(options)
|
||||
method_name = Renderers._render_with_renderer_method_name(name)
|
||||
return send(method_name, options.delete(name), options)
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
add :json do |json, options|
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
|
||||
class RenderOtherTest < ActionController::TestCase
|
||||
class TestController < ActionController::Base
|
||||
def render_simon_says
|
||||
render :simon => "foo"
|
||||
end
|
||||
end
|
||||
|
||||
tests TestController
|
||||
|
||||
def test_using_custom_render_option
|
||||
ActionController.add_renderer :simon do |says, options|
|
||||
self.content_type = Mime[:text]
|
||||
self.response_body = "Simon says: #{says}"
|
||||
end
|
||||
|
||||
get :render_simon_says
|
||||
assert_equal "Simon says: foo", @response.body
|
||||
ensure
|
||||
ActionController.remove_renderer :simon
|
||||
end
|
||||
end
|
90
actionpack/test/controller/renderers_test.rb
Normal file
90
actionpack/test/controller/renderers_test.rb
Normal file
|
@ -0,0 +1,90 @@
|
|||
require 'abstract_unit'
|
||||
require 'controller/fake_models'
|
||||
require 'active_support/logger'
|
||||
|
||||
class RenderersTest < ActionController::TestCase
|
||||
class XmlRenderable
|
||||
def to_xml(options)
|
||||
options[:root] ||= "i-am-xml"
|
||||
"<#{options[:root]}/>"
|
||||
end
|
||||
end
|
||||
class JsonRenderable
|
||||
def as_json(options={})
|
||||
hash = { :a => :b, :c => :d, :e => :f }
|
||||
hash.except!(*options[:except]) if options[:except]
|
||||
hash
|
||||
end
|
||||
|
||||
def to_json(options = {})
|
||||
super :except => [:c, :e]
|
||||
end
|
||||
end
|
||||
class CsvRenderable
|
||||
def to_csv
|
||||
"c,s,v"
|
||||
end
|
||||
end
|
||||
class TestController < ActionController::Base
|
||||
|
||||
def render_simon_says
|
||||
render :simon => "foo"
|
||||
end
|
||||
|
||||
def respond_to_mime
|
||||
respond_to do |type|
|
||||
type.json do
|
||||
render json: JsonRenderable.new
|
||||
end
|
||||
type.js { render json: 'JS', callback: 'alert' }
|
||||
type.csv { render csv: CsvRenderable.new }
|
||||
type.xml { render xml: XmlRenderable.new }
|
||||
type.html { render body: "HTML" }
|
||||
type.rss { render body: "RSS" }
|
||||
type.all { render body: "Nothing" }
|
||||
type.any(:js, :xml) { render body: "Either JS or XML" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tests TestController
|
||||
|
||||
def setup
|
||||
# enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
|
||||
# a more accurate simulation of what happens in "real life".
|
||||
super
|
||||
@controller.logger = ActiveSupport::Logger.new(nil)
|
||||
end
|
||||
|
||||
def test_using_custom_render_option
|
||||
ActionController.add_renderer :simon do |says, options|
|
||||
self.content_type = Mime[:text]
|
||||
self.response_body = "Simon says: #{says}"
|
||||
end
|
||||
|
||||
get :render_simon_says
|
||||
assert_equal "Simon says: foo", @response.body
|
||||
ensure
|
||||
ActionController.remove_renderer :simon
|
||||
end
|
||||
|
||||
def test_raises_missing_template_no_renderer
|
||||
assert_raise ActionView::MissingTemplate do
|
||||
get :respond_to_mime, format: 'csv'
|
||||
end
|
||||
assert_equal Mime[:csv], @response.content_type
|
||||
assert_equal "", @response.body
|
||||
end
|
||||
|
||||
def test_adding_csv_rendering_via_renderers_add
|
||||
ActionController::Renderers.add :csv do |value, options|
|
||||
send_data value.to_csv, type: Mime[:csv]
|
||||
end
|
||||
@request.accept = "text/csv"
|
||||
get :respond_to_mime, format: 'csv'
|
||||
assert_equal Mime[:csv], @response.content_type
|
||||
assert_equal "c,s,v", @response.body
|
||||
ensure
|
||||
ActionController::Renderers.remove :csv
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue