Finally moved the find template logic to the views.

This commit is contained in:
José Valim 2010-03-08 11:32:01 +01:00
parent 34b2180451
commit 0a85380966
8 changed files with 185 additions and 225 deletions

@ -264,7 +264,7 @@ def _layout
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def _layout
if template_exists?("#{_implied_layout_name}", :_prefix => #{_prefix.inspect})
if template_exists?("#{_implied_layout_name}", #{_prefix.inspect})
"#{_implied_layout_name}"
else
super
@ -277,7 +277,9 @@ def _layout
end
end
def render_to_body(options)
def _normalize_options(options)
super
if _include_layout?(options)
layout = options.key?(:layout) ? options[:layout] : :default
value = _layout_for_option(layout)
@ -288,7 +290,6 @@ def render_to_body(options)
# TODO Revisit this. :layout with :partial from controllers are not the same as in views
options[:layout] = view_context._find_layout(options[:layout]) if options.key?(:partial)
end
super
end
private

@ -10,7 +10,7 @@ def initialize(message = nil)
end
end
module Rendering
module ViewPaths
extend ActiveSupport::Concern
included do
@ -21,80 +21,12 @@ module Rendering
delegate :formats, :formats=, :to => :template_lookup
delegate :_view_paths, :to => :'self.class'
# An instance of a view class. The default view class is ActionView::Base
#
# The view class must have the following methods:
# View.for_controller[controller] Create a new ActionView instance for a
# controller
# View#render_partial[options]
# - responsible for setting options[:_template]
# - Returns String with the rendered partial
# options<Hash>:: see _render_partial in ActionView::Base
# View#render_template[template, layout, options, partial]
# - Returns String with the rendered template
# template<ActionView::Template>:: The template to render
# layout<ActionView::Template>:: The layout to render around the template
# options<Hash>:: See _render_template_with_layout in ActionView::Base
# partial<Boolean>:: Whether or not the template to render is a partial
#
# Override this method in a module to change the default behavior.
def view_context
@_view_context ||= ActionView::Base.for_controller(self)
end
# Mostly abstracts the fact that calling render twice is a DoubleRenderError.
# Delegates render_to_body and sticks the result in self.response_body.
def render(*args, &block)
options = _normalize_args(*args, &block)
_normalize_options(options)
self.response_body = render_to_body(options)
end
# Raw rendering of a template to a Rack-compatible body.
#
# ==== Options
# _partial_object<Object>:: The object that is being rendered. If this
# exists, we are in the special case of rendering an object as a partial.
#
# :api: plugin
def render_to_body(options = {})
# TODO: Refactor so we can just use the normal template logic for this
if options.key?(:partial)
_render_partial(options)
else
_determine_template(options)
_render_template(options)
end
end
# Raw rendering of a template to a string. Just convert the results of
# render_to_body into a String.
#
# :api: plugin
def render_to_string(options={})
_normalize_options(options)
AbstractController::Rendering.body_to_s(render_to_body(options))
end
# Renders the template from an object.
#
# ==== Options
# _template<ActionView::Template>:: The template to render
# _layout<ActionView::Template>:: The layout to wrap the template in (optional)
def _render_template(options)
view_context.render_template(options)
end
# Renders the given partial.
#
# ==== Options
# partial<String|Object>:: The partial name or the object to be rendered
def _render_partial(options)
view_context.render_partial(options)
end
def template_lookup
@template_lookup ||= ActionView::Template::Lookup.new(_view_paths, details_for_render)
@template_lookup ||= ActionView::Template::Lookup.new(_view_paths, details_for_lookup)
end
def details_for_lookup
{ }
end
# The list of view paths for this controller. See ActionView::ViewPathSet for
@ -111,101 +43,14 @@ def prepend_view_path(path)
template_lookup.view_paths.unshift(*path)
end
# The prefix used in render "foo" shortcuts.
def _prefix
controller_path
protected
def template_exists?(*args)
template_lookup.exists?(*args)
end
# Return a string representation of a Rack-compatible response body.
def self.body_to_s(body)
if body.respond_to?(:to_str)
body
else
strings = []
body.each { |part| strings << part.to_s }
body.close if body.respond_to?(:close)
strings.join
end
end
private
# Normalize options, by converting render "foo" to render :template => "prefix/foo"
# and render "/foo" to render :file => "/foo".
def _normalize_args(action=nil, options={})
case action
when Hash
options, action = action, nil
when String, Symbol
action = action.to_s
case action.index("/")
when NilClass
options[:_prefix] = _prefix
options[:_template_name] = action
when 0
options[:file] = action
else
options[:template] = action
end
end
options
end
def _normalize_options(options)
end
# Take in a set of options and determine the template to render
#
# ==== Options
# _template<ActionView::Template>:: If this is provided, the search is over
# _template_name<#to_s>:: The name of the template to look up. Otherwise,
# use the current action name.
# _prefix<String>:: The prefix to look inside of. In a file system, this corresponds
# to a directory.
# _partial<TrueClass, FalseClass>:: Whether or not the file to look up is a partial
def _determine_template(options)
if options.key?(:text)
options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text)
elsif options.key?(:inline)
handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
template = ActionView::Template.new(options[:inline], "inline template", handler, {})
options[:_template] = template
elsif options.key?(:template)
options[:_template_name] = options[:template]
elsif options.key?(:file)
options[:_template_name] = options[:file]
end
name = (options[:_template_name] || options[:action] || action_name).to_s
options[:_prefix] ||= _prefix if (options.keys & [:partial, :file, :template]).empty?
details = _normalize_details(options)
template_lookup.details = details
options[:_template] ||= find_template(name, options)
end
def details_for_render
{ }
end
def _normalize_details(options)
details = template_lookup.details
details[:formats] = Array(options[:format]) if options[:format]
details[:locale] = Array(options[:locale]) if options[:locale]
details
end
def find_template(name, options)
template_lookup.find(name, options[:_prefix], options[:_partial])
end
def template_exists?(name, options)
template_lookup.exists?(name, options[:_prefix], options[:_partial])
end
def format_for_text
Mime[:text]
def find_template(*args)
template_lookup.find(*args)
end
module ClassMethods
@ -245,4 +90,123 @@ def view_paths=(paths)
end
end
end
module Rendering
extend ActiveSupport::Concern
include AbstractController::ViewPaths
# An instance of a view class. The default view class is ActionView::Base
#
# The view class must have the following methods:
# View.for_controller[controller]
# Create a new ActionView instance for a controller
# View#render_template[options]
# Returns String with the rendered template
#
# Override this method in a module to change the default behavior.
def view_context
@_view_context ||= ActionView::Base.for_controller(self)
end
# Mostly abstracts the fact that calling render twice is a DoubleRenderError.
# Delegates render_to_body and sticks the result in self.response_body.
def render(*args, &block)
options = _normalize_args(*args, &block)
_normalize_options(options)
self.response_body = render_to_body(options)
end
# Raw rendering of a template to a Rack-compatible body.
# :api: plugin
def render_to_body(options = {})
_process_options(options)
_render_template(options)
end
# Raw rendering of a template to a string. Just convert the results of
# render_to_body into a String.
# :api: plugin
def render_to_string(options={})
_normalize_options(options)
AbstractController::Rendering.body_to_s(render_to_body(options))
end
# Find and renders a template based on the options given.
def _render_template(options)
view_context.render_template(options) { |template| _with_template_hook(template) }
end
# The prefix used in render "foo" shortcuts.
def _prefix
controller_path
end
# Return a string representation of a Rack-compatible response body.
def self.body_to_s(body)
if body.respond_to?(:to_str)
body
else
strings = []
body.each { |part| strings << part.to_s }
body.close if body.respond_to?(:close)
strings.join
end
end
private
# Normalize options, by converting render "foo" to render :template => "prefix/foo"
# and render "/foo" to render :file => "/foo".
def _normalize_args(action=nil, options={})
case action
when NilClass
when Hash
options, action = action, nil
when String, Symbol
action = action.to_s
case action.index("/")
when NilClass
options[:action] = action
when 0
options[:file] = action
else
options[:template] = action
end
else
options.merge!(:partial => action)
end
options
end
def _normalize_options(options)
if options[:partial] == true
options[:partial] = action_name
end
if (options.keys & [:partial, :file, :template]).empty?
options[:_prefix] ||= _prefix
end
options[:template] ||= (options[:action] || action_name).to_s
details = _normalize_details(options)
template_lookup.details = details
options
end
def _normalize_details(options)
details = template_lookup.details
details[:formats] = Array(options[:format]) if options[:format]
details[:locale] = Array(options[:locale]) if options[:locale]
details
end
def _process_options(options)
end
def _with_template_hook(template)
self.formats = template.details[:formats]
end
end
end

@ -49,14 +49,19 @@ def process_action(*)
super
end
def render_to_body(options)
if options.is_a?(Hash) && options.key?(:template)
options[:template].sub!(/^\//, '')
def _normalize_options(options)
# TODO Deprecate this. Rails 2.x allowed to give a template as action.
if options[:action] && options[:action].to_s.include?(?/)
options[:template] = options.delete(:action)
end
options[:text] = nil if options.delete(:nothing) == true
options[:text] = " " if options.key?(:text) && options[:text].nil?
super
end
def render_to_body(options)
options[:template].sub!(/^\//, '') if options.key?(:template)
super || " "
end

@ -12,7 +12,7 @@ def default_render
def method_for_action(action_name)
super || begin
if template_exists?(action_name.to_s, :_prefix => controller_path)
if template_exists?(action_name.to_s, _prefix)
"default_render"
end
end

@ -6,46 +6,20 @@ module Rendering
include AbstractController::Rendering
def process(*)
self.formats = request.formats.map {|x| x.to_sym }
self.formats = request.formats.map { |x| x.to_sym }
super
end
def render(*args)
raise ::AbstractController::DoubleRenderError if response_body
args << {} unless args.last.is_a?(Hash)
super(*args)
self.content_type ||= args.last[:_template].mime_type.to_s
response_body
end
def render_to_body(options)
_process_options(options)
super
response_body
end
private
def _render_partial(options)
options[:partial] = action_name if options[:partial] == true
options[:_details] = details_for_render
super
end
def format_for_text
formats.first
end
def _normalize_args(action=nil, options={}, &blk)
case action
when NilClass
when Hash
options = super(action.delete(:action), action)
when String, Symbol
options = super
else
options.merge!(:partial => action)
end
options = super
options[:update] = blk if block_given?
options
end
@ -64,9 +38,18 @@ def _normalize_options(options)
def _process_options(options)
status, content_type, location = options.values_at(:status, :content_type, :location)
self.status = status if status
self.content_type = content_type if content_type
self.headers["Location"] = url_for(location) if location
super
end
def _with_template_hook(template)
super
self.content_type ||= template.mime_type.to_s
end
end
end

@ -24,18 +24,8 @@ def render(options = {}, locals = {}, &block) #:nodoc:
return _render_partial(options)
end
template = if options[:file]
find(options[:file])
elsif options[:inline]
handler = Template.handler_class_for_extension(options[:type] || "erb")
Template.new(options[:inline], "inline template", handler, {})
elsif options[:text]
Template::Text.new(options[:text])
end
if template
_render_template(template, layout, :locals => options[:locals])
end
template = _determine_template(options)
_render_template(template, layout, :locals => options[:locals]) if template
when :update
update_page(&block)
else
@ -87,8 +77,28 @@ def _layout_for(name = nil, &block)
# _layout:: The layout, if any, to wrap the Template in
def render_template(options)
_evaluate_assigns_and_ivars
template, layout = options.values_at(:_template, :layout)
_render_template(template, layout, options)
if options.key?(:partial)
_render_partial(options)
else
template = _determine_template(options)
yield template if block_given?
_render_template(template, options[:layout], options)
end
end
def _determine_template(options)
if options.key?(:inline)
handler = Template.handler_class_for_extension(options[:type] || "erb")
Template.new(options[:inline], "inline template", handler, {})
elsif options.key?(:text)
Template::Text.new(options[:text], self.formats.try(:first))
elsif options.key?(:_template)
options[:_template]
elsif options.key?(:file)
find(options[:file], options[:_prefix])
elsif options.key?(:template)
find(options[:template], options[:_prefix])
end
end
def _find_layout(layout)
@ -102,8 +112,6 @@ def _find_layout(layout)
end
def _render_template(template, layout = nil, options = {})
self.formats = template.details[:formats]
locals = options[:locals] || {}
layout = _find_layout(layout) if layout

@ -1,11 +1,10 @@
module ActionView #:nodoc:
class Template
class Text < String #:nodoc:
HTML = Mime[:html]
def initialize(string, content_type = HTML)
def initialize(string, content_type = nil)
super(string.to_s)
@content_type = Mime[content_type] || content_type
@content_type = Mime[content_type] || content_type if content_type
@content_type ||= Mime::TEXT
end
def details

@ -59,11 +59,11 @@ def naked_render
end
def rendering_to_body
self.response_body = render_to_body :_template_name => "naked_render.erb"
self.response_body = render_to_body :template => "naked_render.erb"
end
def rendering_to_string
self.response_body = render_to_string :_template_name => "naked_render.erb"
self.response_body = render_to_string :template => "naked_render.erb"
end
end