mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Clean up ActionView some:
* Call _evaluate_assigns_and_ivars at the two entry points so we don't have to do a check at every render. * Make template.render viable without having to go through a wrapper method * Remove old TemplateHandler#render(template, local_assigns) path so we don't have to set self.template every time we render a template. * Move Template rescuing code to Template#render so it gets caught every time. * Pull in some tests from Pratik that test render @object in ActionView
This commit is contained in:
parent
a363dba790
commit
27adcd1c1a
6 changed files with 56 additions and 75 deletions
|
@ -255,15 +255,6 @@ module ActionView #:nodoc:
|
||||||
@view_paths = self.class.process_view_paths(paths)
|
@view_paths = self.class.process_view_paths(paths)
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_template(current_template)
|
|
||||||
_evaluate_assigns_and_ivars
|
|
||||||
last_template, self.template = template, current_template
|
|
||||||
last_formats, self.formats = formats, current_template.formats
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
self.template, self.formats = last_template, last_formats
|
|
||||||
end
|
|
||||||
|
|
||||||
def punctuate_body!(part)
|
def punctuate_body!(part)
|
||||||
flush_output_buffer
|
flush_output_buffer
|
||||||
response.body_parts << part
|
response.body_parts << part
|
||||||
|
@ -272,18 +263,11 @@ module ActionView #:nodoc:
|
||||||
|
|
||||||
# Evaluates the local assigns and controller ivars, pushes them to the view.
|
# Evaluates the local assigns and controller ivars, pushes them to the view.
|
||||||
def _evaluate_assigns_and_ivars #:nodoc:
|
def _evaluate_assigns_and_ivars #:nodoc:
|
||||||
@assigns_added ||= _copy_ivars_from_controller
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def _copy_ivars_from_controller #:nodoc:
|
|
||||||
if @controller
|
if @controller
|
||||||
variables = @controller.instance_variable_names
|
variables = @controller.instance_variable_names
|
||||||
variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
|
variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
|
||||||
variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
|
variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
|
||||||
end
|
end
|
||||||
true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -265,7 +265,9 @@ module ActionView
|
||||||
@locals[:object] = @locals[template.variable_name] = object
|
@locals[:object] = @locals[template.variable_name] = object
|
||||||
@locals[@options[:as]] = object if @options[:as]
|
@locals[@options[:as]] = object if @options[:as]
|
||||||
|
|
||||||
content = @view._render_single_template(template, @locals, &@block)
|
content = template.render(@view, @locals) do |*names|
|
||||||
|
@view._layout_for(names, &@block)
|
||||||
|
end
|
||||||
return content if @block || !@options[:layout]
|
return content if @block || !@options[:layout]
|
||||||
find_template(@options[:layout]).render(@view, @locals) { content }
|
find_template(@options[:layout]).render(@view, @locals) { content }
|
||||||
end
|
end
|
||||||
|
@ -302,7 +304,7 @@ module ActionView
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_partial(options)
|
def render_partial(options)
|
||||||
@assigns_added = false
|
_evaluate_assigns_and_ivars
|
||||||
# TODO: Handle other details here.
|
# TODO: Handle other details here.
|
||||||
self.formats = options[:_details][:formats] if options[:_details]
|
self.formats = options[:_details][:formats] if options[:_details]
|
||||||
_render_partial(options)
|
_render_partial(options)
|
||||||
|
|
|
@ -12,8 +12,6 @@ module ActionView
|
||||||
# as the locals hash.
|
# as the locals hash.
|
||||||
def render(options = {}, locals = {}, &block) #:nodoc:
|
def render(options = {}, locals = {}, &block) #:nodoc:
|
||||||
case options
|
case options
|
||||||
when String, NilClass
|
|
||||||
_render_partial(:partial => options, :locals => locals || {})
|
|
||||||
when Hash
|
when Hash
|
||||||
layout = options[:layout]
|
layout = options[:layout]
|
||||||
|
|
||||||
|
@ -35,26 +33,8 @@ module ActionView
|
||||||
end
|
end
|
||||||
when :update
|
when :update
|
||||||
update_page(&block)
|
update_page(&block)
|
||||||
end
|
else
|
||||||
end
|
_render_partial(:partial => options, :locals => locals)
|
||||||
|
|
||||||
def _render_content(content, layout, locals)
|
|
||||||
return content unless layout
|
|
||||||
|
|
||||||
locals ||= {}
|
|
||||||
|
|
||||||
if controller && layout
|
|
||||||
@_layout = layout.identifier
|
|
||||||
logger.info("Rendering template within #{layout.identifier}") if logger
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
old_content, @_content_for[:layout] = @_content_for[:layout], content
|
|
||||||
|
|
||||||
@cached_content_for_layout = @_content_for[:layout]
|
|
||||||
_render_single_template(layout, locals)
|
|
||||||
ensure
|
|
||||||
@_content_for[:layout] = old_content
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,30 +88,17 @@ module ActionView
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def _render_single_template(template, locals = {}, &block)
|
|
||||||
with_template(template) do
|
|
||||||
template.render(self, locals) do |*names|
|
|
||||||
_layout_for(names, &block)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue Exception => e
|
|
||||||
if e.is_a?(TemplateError)
|
|
||||||
e.sub_template_of(template)
|
|
||||||
raise e
|
|
||||||
else
|
|
||||||
raise TemplateError.new(template, assigns, e)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def _render_inline(inline, layout, options)
|
def _render_inline(inline, layout, options)
|
||||||
handler = Template.handler_class_for_extension(options[:type] || "erb")
|
handler = Template.handler_class_for_extension(options[:type] || "erb")
|
||||||
template = Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
|
template = Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
|
||||||
content = _render_single_template(template, options[:locals] || {})
|
locals = options[:locals] || {}
|
||||||
layout ? _render_content(content, layout, options[:locals]) : content
|
content = template.render(self, locals)
|
||||||
|
content = layout.render(self, locals) { content } if layout
|
||||||
|
content
|
||||||
end
|
end
|
||||||
|
|
||||||
def _render_text(text, layout, options)
|
def _render_text(text, layout, options)
|
||||||
layout ? _render_content(text, layout, options[:locals]) : text
|
text = layout.render(self, options[:locals]) { text } if layout
|
||||||
end
|
end
|
||||||
|
|
||||||
# This is the API to render a ViewContext's template from a controller.
|
# This is the API to render a ViewContext's template from a controller.
|
||||||
|
@ -141,7 +108,7 @@ module ActionView
|
||||||
# _layout:: The layout, if any, to wrap the Template in
|
# _layout:: The layout, if any, to wrap the Template in
|
||||||
# _partial:: true if the template is a partial
|
# _partial:: true if the template is a partial
|
||||||
def render_template(options)
|
def render_template(options)
|
||||||
@assigns_added = nil
|
_evaluate_assigns_and_ivars
|
||||||
template, layout, partial = options.values_at(:_template, :_layout, :_partial)
|
template, layout, partial = options.values_at(:_template, :_layout, :_partial)
|
||||||
_render_template(template, layout, options, partial)
|
_render_template(template, layout, options, partial)
|
||||||
end
|
end
|
||||||
|
@ -158,10 +125,18 @@ module ActionView
|
||||||
content = if partial
|
content = if partial
|
||||||
_render_partial_object(template, options)
|
_render_partial_object(template, options)
|
||||||
else
|
else
|
||||||
_render_single_template(template, locals)
|
template.render(self, locals)
|
||||||
end
|
end
|
||||||
|
|
||||||
_render_content(content, layout, locals)
|
@cached_content_for_layout = content
|
||||||
|
@_content_for[:layout] = content
|
||||||
|
|
||||||
|
if layout
|
||||||
|
@_layout = layout.identifier
|
||||||
|
logger.info("Rendering template within #{layout.identifier}") if logger
|
||||||
|
content = layout.render(self, locals)
|
||||||
|
end
|
||||||
|
content
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -26,7 +26,7 @@ module ActionView
|
||||||
self.default_format = Mime::HTML
|
self.default_format = Mime::HTML
|
||||||
|
|
||||||
def self.call(template)
|
def self.call(template)
|
||||||
"#{name}.new(self).render(template, local_assigns)"
|
raise "Need to implement #{self.class.name}#call(template)"
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(view = nil)
|
def initialize(view = nil)
|
||||||
|
|
|
@ -26,9 +26,17 @@ module ActionView
|
||||||
@details[:formats] = Array.wrap(format.to_sym)
|
@details[:formats] = Array.wrap(format.to_sym)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(view, locals, &blk)
|
def render(view, locals, &block)
|
||||||
method_name = compile(locals, view)
|
method_name = compile(locals, view)
|
||||||
view.send(method_name, locals, &blk)
|
block ||= proc {|*names| view._layout_for(names) }
|
||||||
|
view.send(method_name, locals, &block)
|
||||||
|
rescue Exception => e
|
||||||
|
if e.is_a?(TemplateError)
|
||||||
|
e.sub_template_of(self)
|
||||||
|
raise e
|
||||||
|
else
|
||||||
|
raise TemplateError.new(self, view.assigns, e)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Figure out how to abstract this
|
# TODO: Figure out how to abstract this
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
require 'abstract_unit'
|
require 'abstract_unit'
|
||||||
require 'controller/fake_models'
|
require 'controller/fake_models'
|
||||||
|
|
||||||
|
class TestController < ActionController::Base
|
||||||
|
end
|
||||||
|
|
||||||
module RenderTestCases
|
module RenderTestCases
|
||||||
def setup_view(paths)
|
def setup_view(paths)
|
||||||
@assigns = { :secret => 'in the sauce' }
|
@assigns = { :secret => 'in the sauce' }
|
||||||
@view = ActionView::Base.new(paths, @assigns)
|
@view = ActionView::Base.new(paths, @assigns)
|
||||||
|
@controller_view = ActionView::Base.for_controller(TestController.new)
|
||||||
|
|
||||||
# Reload and register danish language for testing
|
# Reload and register danish language for testing
|
||||||
I18n.reload!
|
I18n.reload!
|
||||||
|
@ -158,6 +162,25 @@ module RenderTestCases
|
||||||
assert_nil @view.render(:partial => [])
|
assert_nil @view.render(:partial => [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_partial_using_string
|
||||||
|
assert_equal "Hello: Anonymous", @controller_view.render('customer')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_partial_with_locals_using_string
|
||||||
|
assert_equal "Hola: david", @controller_view.render('customer_greeting', :greeting => 'Hola', :customer_greeting => Customer.new("david"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_partial_using_object
|
||||||
|
assert_equal "Hello: lifo",
|
||||||
|
@controller_view.render(Customer.new("lifo"), :greeting => "Hello")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_partial_using_collection
|
||||||
|
customers = [ Customer.new("Amazon"), Customer.new("Yahoo") ]
|
||||||
|
assert_equal "Hello: AmazonHello: Yahoo",
|
||||||
|
@controller_view.render(customers, :greeting => "Hello")
|
||||||
|
end
|
||||||
|
|
||||||
# TODO: The reason for this test is unclear, improve documentation
|
# TODO: The reason for this test is unclear, improve documentation
|
||||||
def test_render_partial_and_fallback_to_layout
|
def test_render_partial_and_fallback_to_layout
|
||||||
assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" })
|
assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" })
|
||||||
|
@ -196,17 +219,6 @@ module RenderTestCases
|
||||||
assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
|
assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
|
||||||
end
|
end
|
||||||
|
|
||||||
class LegacyHandler < ActionView::TemplateHandler
|
|
||||||
def render(template, local_assigns)
|
|
||||||
"source: #{template.source}; locals: #{local_assigns.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_legacy_handler_with_custom_type
|
|
||||||
ActionView::Template.register_template_handler :foo, LegacyHandler
|
|
||||||
assert_equal 'source: Hello, <%= name %>!; locals: {:name=>"Josh"}', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_ignores_templates_with_malformed_template_handlers
|
def test_render_ignores_templates_with_malformed_template_handlers
|
||||||
%w(malformed malformed.erb malformed.html.erb malformed.en.html.erb).each do |name|
|
%w(malformed malformed.erb malformed.html.erb malformed.en.html.erb).each do |name|
|
||||||
assert_raise(ActionView::MissingTemplate) { @view.render(:file => "test/malformed/#{name}") }
|
assert_raise(ActionView::MissingTemplate) { @view.render(:file => "test/malformed/#{name}") }
|
||||||
|
|
Loading…
Reference in a new issue