Ensure the template format is always passed to the template finder. Now we can cleanup some nasty stuff.

This commit is contained in:
Joshua Peek 2008-12-21 17:23:53 -06:00
parent f5b7f0911b
commit 858a420ce1
9 changed files with 102 additions and 108 deletions

View File

@ -570,7 +570,9 @@ module ActionMailer #:nodoc:
end
def candidate_for_layout?(options)
!@template.send(:_exempt_from_layout?, default_template_name)
!self.view_paths.find_template(default_template_name, default_template_format).exempt_from_layout?
rescue ActionView::MissingTemplate
return true
end
def template_root

View File

@ -10,11 +10,14 @@ require 'action_mailer/test_case'
ActiveSupport::Deprecation.debug = true
# Bogus template processors
ActionView::Template.register_template_handler :haml, lambda { |template| "Look its HAML!" }
ActionView::Template.register_template_handler :bak, lambda { |template| "Lame backup" }
ActionView::Template.register_template_handler :haml, lambda { |template| "Look its HAML!".inspect }
ActionView::Template.register_template_handler :bak, lambda { |template| "Lame backup".inspect }
$:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers"
ActionMailer::Base.template_root = "#{File.dirname(__FILE__)}/fixtures"
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
ActionMailer::Base.template_root = FIXTURE_LOAD_PATH
ActionMailer::Base.template_root.load
class MockSMTP
def self.deliveries

View File

@ -502,7 +502,7 @@ module ActionController #:nodoc:
protected :filter_parameters
end
delegate :exempt_from_layout, :to => 'ActionView::Base'
delegate :exempt_from_layout, :to => 'ActionView::Template'
end
public
@ -860,7 +860,7 @@ module ActionController #:nodoc:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
if options.nil?
return render(:file => default_template_name, :layout => true)
return render(:file => default_template, :layout => true)
elsif !extra_options.is_a?(Hash)
raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
else
@ -898,7 +898,7 @@ module ActionController #:nodoc:
render_for_text(@template.render(options.merge(:layout => layout)), options[:status])
elsif action_name = options[:action]
render_for_file(default_template_name(action_name.to_s), options[:status], layout)
render_for_file(default_template(action_name.to_s), options[:status], layout)
elsif xml = options[:xml]
response.content_type ||= Mime::XML
@ -933,7 +933,7 @@ module ActionController #:nodoc:
render_for_text(nil, options[:status])
else
render_for_file(default_template_name, options[:status], layout)
render_for_file(default_template, options[:status], layout)
end
end
end
@ -1164,7 +1164,8 @@ module ActionController #:nodoc:
private
def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc:
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
path = template_path.respond_to?(:path_without_format_and_extension) ? template_path.path_without_format_and_extension : template_path
logger.info("Rendering #{path}" + (status ? " (#{status})" : '')) if logger
render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status
end
@ -1241,10 +1242,17 @@ module ActionController #:nodoc:
elsif respond_to? :method_missing
method_missing action_name
default_render unless performed?
elsif template_exists?
default_render
else
raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
begin
default_render
rescue ActionView::MissingTemplate => e
# Was the implicit template missing, or was it another template?
if e.path == default_template_name
raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
else
raise e
end
end
end
end
@ -1290,10 +1298,8 @@ module ActionController #:nodoc:
@_session.close if @_session && @_session.respond_to?(:close)
end
def template_exists?(template_name = default_template_name)
@template.send(:_pick_template, template_name) ? true : false
rescue ActionView::MissingTemplate
false
def default_template(action_name = self.action_name)
self.view_paths.find_template(default_template_name(action_name), default_template_format)
end
def default_template_name(action_name = self.action_name)

View File

@ -178,9 +178,15 @@ module ActionController #:nodoc:
find_layout(layout, format)
end
def layout_list #:nodoc:
Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
end
def find_layout(layout, *formats) #:nodoc:
return layout if layout.respond_to?(:render)
view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats)
rescue ActionView::MissingTemplate
nil
end
private
@ -188,7 +194,7 @@ module ActionController #:nodoc:
inherited_without_layout(child)
unless child.name.blank?
layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
child.layout(layout_match, {}, true) if child.find_layout(layout_match, :all)
child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
end
end
@ -225,8 +231,16 @@ module ActionController #:nodoc:
private
def candidate_for_layout?(options)
options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty? &&
!@template.__send__(:_exempt_from_layout?, options[:template] || default_template_name(options[:action]))
template = options[:template] || default_template(options[:action])
if options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty?
begin
!self.view_paths.find_template(template, default_template_format).exempt_from_layout?
rescue ActionView::MissingTemplate
true
end
end
rescue ActionView::MissingTemplate
false
end
def pick_layout(options)
@ -235,7 +249,7 @@ module ActionController #:nodoc:
when FalseClass
nil
when NilClass, TrueClass
active_layout if action_has_layout? && !@template.__send__(:_exempt_from_layout?, default_template_name)
active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name)
else
active_layout(layout)
end

View File

@ -3,7 +3,10 @@ module ActionView #:nodoc:
end
class MissingTemplate < ActionViewError #:nodoc:
attr_reader :path
def initialize(paths, path, template_format = nil)
@path = path
full_template_path = path.include?('.') ? path : "#{path}.erb"
display_paths = paths.compact.join(":")
template_type = (path =~ /layouts/i) ? 'layout' : 'template'
@ -172,17 +175,6 @@ module ActionView #:nodoc:
delegate :logger, :to => 'ActionController::Base'
end
# Templates that are exempt from layouts
@@exempt_from_layout = Set.new([/\.rjs$/])
# Don't render layouts for templates with the given extensions.
def self.exempt_from_layout(*extensions)
regexps = extensions.collect do |extension|
extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
end
@@exempt_from_layout.merge(regexps)
end
@@debug_rjs = false
##
# :singleton-method:
@ -190,12 +182,6 @@ module ActionView #:nodoc:
# that alert()s the caught exception (and then re-raises it).
cattr_accessor :debug_rjs
@@warn_cache_misses = false
##
# :singleton-method:
# A warning will be displayed whenever an action results in a cache miss on your view paths.
cattr_accessor :warn_cache_misses
attr_internal :request
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
@ -257,7 +243,8 @@ module ActionView #:nodoc:
if options[:layout]
_render_with_layout(options, local_assigns, &block)
elsif options[:file]
_pick_template(options[:file]).render_template(self, options[:locals])
tempalte = self.view_paths.find_template(options[:file], template_format)
tempalte.render_template(self, options[:locals])
elsif options[:partial]
render_partial(options)
elsif options[:inline]
@ -315,45 +302,6 @@ module ActionView #:nodoc:
end
end
def _pick_template(template_path)
return template_path if template_path.respond_to?(:render)
path = template_path.sub(/^\//, '')
if m = path.match(/(.*)\.(\w+)$/)
template_file_name, template_file_extension = m[1], m[2]
else
template_file_name = path
end
# OPTIMIZE: Checks to lookup template in view path
if template = self.view_paths.find_template(template_file_name, template_format)
template
elsif (first_render = @_render_stack.first) && first_render.respond_to?(:format_and_extension) &&
(template = self.view_paths["#{template_file_name}.#{first_render.format_and_extension}"])
template
else
template = Template.new(template_path, view_paths)
if self.class.warn_cache_misses && logger
logger.debug "[PERFORMANCE] Rendering a template that was " +
"not found in view path. Templates outside the view path are " +
"not cached and result in expensive disk operations. Move this " +
"file into #{view_paths.join(':')} or add the folder to your " +
"view path list"
end
template
end
end
memoize :_pick_template
def _exempt_from_layout?(template_path) #:nodoc:
template = _pick_template(template_path).to_s
@@exempt_from_layout.any? { |ext| template =~ ext }
rescue ActionView::MissingTemplate
return false
end
def _render_with_layout(options, local_assigns, &block) #:nodoc:
partial_layout = options.delete(:layout)

View File

@ -228,7 +228,7 @@ module ActionView
path = "_#{partial_path}"
end
_pick_template(path)
self.view_paths.find_template(path, self.template_format)
end
memoize :_pick_partial_template
end

View File

@ -2,13 +2,6 @@ module ActionView #:nodoc:
class PathSet < Array #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
if Base.warn_cache_misses && defined?(Rails) && Rails.initialized?
Base.logger.debug "[PERFORMANCE] Processing view path during a " +
"request. This an expense disk operation that should be done at " +
"boot. You can manually process this view path with " +
"ActionView::Base.process_view_paths(#{obj.inspect}) and set it " +
"as your view path"
end
Path.new(obj)
else
obj
@ -92,7 +85,7 @@ module ActionView #:nodoc:
else
Dir.glob("#{@path}/#{path}*").each do |file|
template = create_template(file)
if path == template.path_without_extension || path == template.path
if template.accessible_paths.include?(path)
return template
end
end
@ -115,8 +108,9 @@ module ActionView #:nodoc:
templates_in_path do |template|
template.load!
@paths[template.path] = template
@paths[template.path_without_extension] ||= template
template.accessible_paths.each do |path|
@paths[path] = template
end
end
@paths.freeze
@ -143,28 +137,19 @@ module ActionView #:nodoc:
each { |path| path.reload! }
end
def [](template_path)
each do |path|
if template = path[template_path]
return template
end
end
nil
end
def find_template(original_template_path, format = nil)
return original_template_path if original_template_path.respond_to?(:render)
template_path = original_template_path.sub(/^\//, '')
def find_template(path, *formats)
if formats && formats.first == :all
formats = Mime::EXTENSION_LOOKUP.values.map(&:to_sym)
end
formats.each do |format|
if template = self["#{path}.#{format}"]
each do |load_path|
if format && (template = load_path["#{template_path}.#{format}"])
return template
elsif template = load_path[template_path]
return template
end
end
if template = self[path]
return template
end
nil
Template.new(original_template_path, self)
end
end
end

View File

@ -22,6 +22,11 @@ module ActionView
end
memoize :compiled_source
def method_name_without_locals
['_run', extension, method_segment].compact.join('_')
end
memoize :method_name_without_locals
def render(view, local_assigns = {})
compile(local_assigns)
@ -46,9 +51,12 @@ module ActionView
def method_name(local_assigns)
if local_assigns && local_assigns.any?
local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
method_name = method_name_without_locals.dup
method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
else
method_name = method_name_without_locals
end
['_run', extension, method_segment, local_assigns_keys].compact.join('_').to_sym
method_name.to_sym
end
private

View File

@ -4,6 +4,17 @@ module ActionView #:nodoc:
extend ActiveSupport::Memoizable
include Renderable
# Templates that are exempt from layouts
@@exempt_from_layout = Set.new([/\.rjs$/])
# Don't render layouts for templates with the given extensions.
def self.exempt_from_layout(*extensions)
regexps = extensions.collect do |extension|
extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
end
@@exempt_from_layout.merge(regexps)
end
attr_accessor :filename, :load_path, :base_path, :name, :format, :extension
delegate :to_s, :to => :path
@ -17,6 +28,18 @@ module ActionView #:nodoc:
extend RenderablePartial if @name =~ /^_/
end
def accessible_paths
paths = []
paths << path
paths << path_without_extension
if multipart?
formats = format.split(".")
paths << "#{path_without_format_and_extension}.#{formats.first}"
paths << "#{path_without_format_and_extension}.#{formats.second}"
end
paths
end
def format_and_extension
(extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
end
@ -57,6 +80,10 @@ module ActionView #:nodoc:
end
memoize :relative_path
def exempt_from_layout?
@@exempt_from_layout.any? { |exempted| path =~ exempted }
end
def mtime
File.mtime(filename)
end
@ -94,6 +121,7 @@ module ActionView #:nodoc:
def load!
@loaded = true
compile({})
freeze
end