1
0
Fork 0
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:
Rafael França 2016-01-04 21:12:11 -02:00
commit 8788c7ce84
3 changed files with 165 additions and 90 deletions

View file

@ -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|

View file

@ -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

View 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