mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Added template lookup responsible to hold all information used in template lookup.
This commit is contained in:
parent
6e0443fd43
commit
c7564d74e8
18 changed files with 118 additions and 239 deletions
|
@ -267,7 +267,6 @@ module ActionMailer #:nodoc:
|
|||
|
||||
include AbstractController::Logger
|
||||
include AbstractController::Rendering
|
||||
include AbstractController::DetailsCache
|
||||
include AbstractController::Layouts
|
||||
include AbstractController::Helpers
|
||||
include AbstractController::Translation
|
||||
|
|
|
@ -11,7 +11,7 @@ module AbstractController
|
|||
def _default_layout(details, require_layout = false)
|
||||
super
|
||||
rescue ActionView::MissingTemplate
|
||||
_find_layout(_layout({}), {})
|
||||
_layout_for_name(_layout({}), {})
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
module AbstractController
|
||||
class HashKey
|
||||
@hash_keys = Hash.new {|h,k| h[k] = {} }
|
||||
|
||||
def self.get(klass, details)
|
||||
@hash_keys[klass][details] ||= new(klass, details)
|
||||
end
|
||||
|
||||
attr_reader :hash
|
||||
alias_method :eql?, :equal?
|
||||
|
||||
def initialize(klass, details)
|
||||
@details, @hash = details, details.hash
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<HashKey -- details: #{@details.inspect}>"
|
||||
end
|
||||
end
|
||||
|
||||
module DetailsCache
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
def clear_template_caches!
|
||||
ActionView::Partials::PartialRenderer::TEMPLATES.clear
|
||||
template_cache.clear
|
||||
super
|
||||
end
|
||||
|
||||
def template_cache
|
||||
@template_cache ||= Hash.new {|h,k| h[k] = {} }
|
||||
end
|
||||
end
|
||||
|
||||
def render_to_body(*args)
|
||||
Thread.current[:format_locale_key] = HashKey.get(self.class, details_for_render)
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_template_cache(name, details)
|
||||
self.class.template_cache[HashKey.get(self.class, details)][name] ||= super
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -170,27 +170,7 @@ module AbstractController
|
|||
module ClassMethods
|
||||
def inherited(klass)
|
||||
super
|
||||
klass.class_eval do
|
||||
_write_layout_method
|
||||
@found_layouts = {}
|
||||
end
|
||||
end
|
||||
|
||||
def clear_template_caches!
|
||||
@found_layouts.clear if defined? @found_layouts
|
||||
super
|
||||
end
|
||||
|
||||
def cache_layout(details)
|
||||
layout = @found_layouts
|
||||
key = Thread.current[:format_locale_key]
|
||||
|
||||
# Cache nil
|
||||
if layout.key?(key)
|
||||
return layout[key]
|
||||
else
|
||||
layout[key] = yield
|
||||
end
|
||||
klass._write_layout_method
|
||||
end
|
||||
|
||||
# This module is mixed in if layout conditions are provided. This means
|
||||
|
@ -282,14 +262,12 @@ module AbstractController
|
|||
if name
|
||||
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def _layout(details)
|
||||
self.class.cache_layout(details) do
|
||||
if template_exists?("#{_implied_layout_name}", details, :_prefix => "layouts")
|
||||
if template_exists?("#{_implied_layout_name}", :_prefix => "layouts")
|
||||
"#{_implied_layout_name}"
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
@ -372,9 +350,10 @@ module AbstractController
|
|||
# ==== Returns
|
||||
# Template:: A template object matching the name and details
|
||||
def _find_layout(name, details)
|
||||
# TODO: Make prefix actually part of details in ViewPath#find_by_parts
|
||||
prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts"
|
||||
find_template(name, details, :_prefix => prefix)
|
||||
# TODO This should happen automatically
|
||||
template_lookup.details = template_lookup.details.merge(:formats => details[:formats])
|
||||
find_template(name, :_prefix => prefix)
|
||||
end
|
||||
|
||||
# Returns the default layout for this controller and a given set of details.
|
||||
|
|
|
@ -90,10 +90,22 @@ module AbstractController
|
|||
view_context.render_partial(options)
|
||||
end
|
||||
|
||||
def template_lookup
|
||||
@template_lookup ||= ActionView::Template::Lookup.new(_view_paths, details_for_render)
|
||||
end
|
||||
|
||||
# The list of view paths for this controller. See ActionView::ViewPathSet for
|
||||
# more details about writing custom view paths.
|
||||
def view_paths
|
||||
_view_paths
|
||||
template_lookup.view_paths
|
||||
end
|
||||
|
||||
def append_view_path(path)
|
||||
template_lookup.view_paths.push(*path)
|
||||
end
|
||||
|
||||
def prepend_view_path(path)
|
||||
template_lookup.view_paths.unshift(*path)
|
||||
end
|
||||
|
||||
# The prefix used in render "foo" shortcuts.
|
||||
|
@ -162,10 +174,9 @@ module AbstractController
|
|||
options[:_prefix] ||= _prefix if (options.keys & [:partial, :file, :template]).empty?
|
||||
|
||||
details = _normalize_details(options)
|
||||
template_lookup.details = details
|
||||
|
||||
options[:_template] ||= with_template_cache(name, details) do
|
||||
find_template(name, details, options)
|
||||
end
|
||||
options[:_template] ||= find_template(name, options)
|
||||
end
|
||||
|
||||
def details_for_render
|
||||
|
@ -179,16 +190,12 @@ module AbstractController
|
|||
details
|
||||
end
|
||||
|
||||
def find_template(name, details, options)
|
||||
view_paths.find(name, details, options[:_prefix], options[:_partial])
|
||||
def find_template(name, options)
|
||||
template_lookup.find(name, options[:_prefix], options[:_partial])
|
||||
end
|
||||
|
||||
def template_exists?(name, details, options)
|
||||
view_paths.exists?(name, details, options[:_prefix], options[:_partial])
|
||||
end
|
||||
|
||||
def with_template_cache(name, details)
|
||||
yield
|
||||
def template_exists?(name, options)
|
||||
template_lookup.exists?(name, options[:_prefix], options[:_partial])
|
||||
end
|
||||
|
||||
def format_for_text
|
||||
|
@ -196,9 +203,6 @@ module AbstractController
|
|||
end
|
||||
|
||||
module ClassMethods
|
||||
def clear_template_caches!
|
||||
end
|
||||
|
||||
# Append a path to the list of view paths for this controller.
|
||||
#
|
||||
# ==== Parameters
|
||||
|
@ -206,7 +210,7 @@ module AbstractController
|
|||
# the default view path. You may also provide a custom view path
|
||||
# (see ActionView::ViewPathSet for more information)
|
||||
def append_view_path(path)
|
||||
self.view_paths = view_paths.dup + Array.wrap(path)
|
||||
self.view_paths = view_paths.dup + Array(path)
|
||||
end
|
||||
|
||||
# Prepend a path to the list of view paths for this controller.
|
||||
|
@ -216,8 +220,7 @@ module AbstractController
|
|||
# the default view path. You may also provide a custom view path
|
||||
# (see ActionView::ViewPathSet for more information)
|
||||
def prepend_view_path(path)
|
||||
clear_template_caches!
|
||||
self.view_paths = Array.wrap(path) + view_paths.dup
|
||||
self.view_paths = Array(path) + view_paths.dup
|
||||
end
|
||||
|
||||
# A list of all of the default view paths for this controller.
|
||||
|
@ -231,9 +234,8 @@ module AbstractController
|
|||
# paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
|
||||
# otherwise, process the parameter into a ViewPathSet.
|
||||
def view_paths=(paths)
|
||||
clear_template_caches!
|
||||
self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths)
|
||||
_view_paths.freeze
|
||||
self._view_paths.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -73,18 +73,5 @@ module ActionController
|
|||
def performed?
|
||||
response_body
|
||||
end
|
||||
|
||||
# ==== Request only view path switching ====
|
||||
def append_view_path(path)
|
||||
view_paths.push(*path)
|
||||
end
|
||||
|
||||
def prepend_view_path(path)
|
||||
view_paths.unshift(*path)
|
||||
end
|
||||
|
||||
def view_paths
|
||||
view_context.view_paths
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,7 @@ module ActionController
|
|||
|
||||
def method_for_action(action_name)
|
||||
super || begin
|
||||
# TODO This should use template lookup
|
||||
if view_paths.exists?(action_name.to_s, details_for_render, controller_path)
|
||||
"default_render"
|
||||
end
|
||||
|
|
|
@ -4,7 +4,6 @@ module ActionController
|
|||
|
||||
include ActionController::RackDelegation
|
||||
include AbstractController::Rendering
|
||||
include AbstractController::DetailsCache
|
||||
|
||||
def process_action(*)
|
||||
self.formats = request.formats.map {|x| x.to_sym }
|
||||
|
|
|
@ -180,64 +180,21 @@ module ActionView #:nodoc:
|
|||
attr_accessor :base_path, :assigns, :template_extension
|
||||
attr_internal :captures
|
||||
|
||||
def reset_formats(formats)
|
||||
old_formats, self.formats = self.formats, formats
|
||||
reset_hash_key
|
||||
yield if block_given?
|
||||
ensure
|
||||
if block_given?
|
||||
self.formats = old_formats
|
||||
reset_hash_key
|
||||
end
|
||||
end
|
||||
|
||||
def reset_hash_key
|
||||
if defined?(AbstractController::HashKey)
|
||||
# This is expensive, but we need to reset this when the format is updated,
|
||||
# which currently only happens
|
||||
Thread.current[:format_locale_key] =
|
||||
AbstractController::HashKey.get(self.class, :formats => formats, :locale => [I18n.locale])
|
||||
end
|
||||
end
|
||||
|
||||
def formats
|
||||
controller ? controller.formats : @formats
|
||||
end
|
||||
|
||||
def formats=(val)
|
||||
if controller
|
||||
controller.formats = val
|
||||
else
|
||||
@formats = val
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
|
||||
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
|
||||
end
|
||||
|
||||
@@debug_rjs = false
|
||||
##
|
||||
# :singleton-method:
|
||||
# Specify whether RJS responses should be wrapped in a try/catch block
|
||||
# that alert()s the caught exception (and then re-raises it).
|
||||
cattr_accessor :debug_rjs
|
||||
|
||||
# Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
|
||||
# Automatically reloading templates are not thread safe and should only be used in development mode.
|
||||
@@cache_template_loading = nil
|
||||
cattr_accessor :cache_template_loading
|
||||
@@debug_rjs = false
|
||||
|
||||
# :nodoc:
|
||||
def self.xss_safe?
|
||||
true
|
||||
end
|
||||
|
||||
def self.cache_template_loading?
|
||||
ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading)
|
||||
end
|
||||
|
||||
attr_internal :request, :layout
|
||||
|
||||
def controller_path
|
||||
|
@ -249,8 +206,6 @@ module ActionView #:nodoc:
|
|||
|
||||
delegate :logger, :to => :controller, :allow_nil => true
|
||||
|
||||
delegate :find, :to => :view_paths
|
||||
|
||||
include Context
|
||||
|
||||
def self.process_view_paths(value)
|
||||
|
@ -287,10 +242,10 @@ module ActionView #:nodoc:
|
|||
klass = self
|
||||
end
|
||||
|
||||
klass.new(controller.class.view_paths, {}, controller)
|
||||
klass.new(controller.template_lookup, {}, controller)
|
||||
end
|
||||
|
||||
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
|
||||
def initialize(template_lookup = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
|
||||
@config = nil
|
||||
@formats = formats
|
||||
@assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
|
||||
|
@ -300,14 +255,31 @@ module ActionView #:nodoc:
|
|||
@_config = ActiveSupport::InheritableOptions.new(controller.config) if controller
|
||||
@_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
|
||||
@_virtual_path = nil
|
||||
self.view_paths = view_paths
|
||||
|
||||
@template_lookup = template_lookup.is_a?(ActionView::Template::Lookup) ?
|
||||
template_lookup : ActionView::Template::Lookup.new(template_lookup)
|
||||
end
|
||||
|
||||
attr_internal :controller, :template, :config
|
||||
attr_reader :view_paths
|
||||
|
||||
def view_paths=(paths)
|
||||
@view_paths = self.class.process_view_paths(paths)
|
||||
attr_reader :template_lookup
|
||||
delegate :find, :view_paths, :view_paths=, :to => :template_lookup
|
||||
|
||||
def formats=(formats)
|
||||
update_details(:formats => Array(formats))
|
||||
end
|
||||
|
||||
def update_details(details)
|
||||
old_details = template_lookup.details
|
||||
template_lookup.details = old_details.merge(details)
|
||||
|
||||
if block_given?
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
template_lookup.details = old_details
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def punctuate_body!(part)
|
||||
|
|
|
@ -182,7 +182,7 @@ module ActionView
|
|||
def initialize(context, &block) #:nodoc:
|
||||
context._evaluate_assigns_and_ivars
|
||||
@context, @lines = context, []
|
||||
@context.reset_formats([:js, :html]) do
|
||||
@context.update_details(:formats => [:js, :html]) do
|
||||
include_helpers_from_context
|
||||
@context.with_output_buffer(@lines) do
|
||||
@context.instance_exec(self, &block)
|
||||
|
@ -583,7 +583,7 @@ module ActionView
|
|||
end
|
||||
|
||||
def with_formats(*args)
|
||||
@context ? @context.reset_formats(args) { yield } : yield
|
||||
@context ? @context.update_details(:formats => args) { yield } : yield
|
||||
end
|
||||
|
||||
def javascript_object_for(object)
|
||||
|
|
|
@ -9,7 +9,7 @@ module ActionView #:nodoc:
|
|||
METHOD
|
||||
end
|
||||
|
||||
def find(path, details = {}, prefix = nil, partial = false)
|
||||
def find(path, details = {}, prefix = nil, partial = false, key=nil)
|
||||
each do |resolver|
|
||||
if template = resolver.find(path, details, prefix, partial)
|
||||
return template
|
||||
|
@ -19,7 +19,7 @@ module ActionView #:nodoc:
|
|||
raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path}", details, partial)
|
||||
end
|
||||
|
||||
def exists?(path, details = {}, prefix = nil, partial = false)
|
||||
def exists?(path, details = {}, prefix = nil, partial = false, key=nil)
|
||||
each do |resolver|
|
||||
if resolver.find(path, details, prefix, partial)
|
||||
return true
|
||||
|
|
|
@ -309,7 +309,7 @@ module ActionView
|
|||
prefix = controller.controller_path unless path.include?(?/)
|
||||
end
|
||||
|
||||
@view.find(path, {:formats => @view.formats}, prefix, true)
|
||||
@view.find(path, prefix, true)
|
||||
end
|
||||
|
||||
def partial_path(object = @object)
|
||||
|
@ -329,7 +329,7 @@ module ActionView
|
|||
|
||||
details = options[:_details]
|
||||
|
||||
# Is this needed
|
||||
# TODO This should happen automatically as well
|
||||
self.formats = details[:formats] if details
|
||||
renderer = PartialRenderer.new(self, options, nil)
|
||||
text = renderer.render
|
||||
|
|
|
@ -25,7 +25,7 @@ module ActionView
|
|||
end
|
||||
|
||||
template = if options[:file]
|
||||
find(options[:file], details_for_render)
|
||||
find(options[:file])
|
||||
elsif options[:inline]
|
||||
handler = Template.handler_class_for_extension(options[:type] || "erb")
|
||||
Template.new(options[:inline], "inline template", handler, {})
|
||||
|
@ -34,7 +34,7 @@ module ActionView
|
|||
end
|
||||
|
||||
if template
|
||||
layout = find(layout, details_for_render) if layout
|
||||
layout = find(layout) if layout
|
||||
_render_template(template, layout, :locals => options[:locals])
|
||||
end
|
||||
when :update
|
||||
|
@ -44,10 +44,6 @@ module ActionView
|
|||
end
|
||||
end
|
||||
|
||||
def details_for_render
|
||||
controller.try(:details_for_render) || {:formats => formats}
|
||||
end
|
||||
|
||||
# You can think of a layout as a method that is called with a block. _layout_for
|
||||
# returns the contents that are yielded to the layout. If the user calls yield
|
||||
# :some_name, the block, by default, returns content_for(:some_name). If the user
|
||||
|
|
|
@ -13,6 +13,7 @@ module ActionView
|
|||
autoload :Handler
|
||||
autoload :Handlers
|
||||
autoload :Text
|
||||
autoload :Lookup
|
||||
end
|
||||
|
||||
extend Template::Handlers
|
||||
|
|
48
actionpack/lib/action_view/template/lookup.rb
Normal file
48
actionpack/lib/action_view/template/lookup.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
module ActionView
|
||||
class Template
|
||||
class Lookup
|
||||
attr_reader :details, :view_paths
|
||||
|
||||
class DetailsKey
|
||||
attr_reader :details
|
||||
alias :eql? :equal?
|
||||
|
||||
@details_keys = Hash.new
|
||||
|
||||
def self.get(details)
|
||||
@details_keys[details] ||= new(details)
|
||||
end
|
||||
|
||||
def initialize(details)
|
||||
@details, @hash = details, details.hash
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(view_paths, details = {})
|
||||
@details = details
|
||||
self.view_paths = view_paths
|
||||
end
|
||||
|
||||
def view_paths=(paths)
|
||||
@view_paths = ActionView::Base.process_view_paths(paths)
|
||||
end
|
||||
|
||||
def details=(details)
|
||||
@details = details
|
||||
@details_key = nil if @details_key && @details_key.details != details
|
||||
end
|
||||
|
||||
def details_key
|
||||
@details_key ||= DetailsKey.get(details) unless details.empty?
|
||||
end
|
||||
|
||||
def find(name, prefix = nil, partial = false)
|
||||
@view_paths.find(name, details, prefix, partial || false, details_key)
|
||||
end
|
||||
|
||||
def exists?(name, prefix = nil, partial = false)
|
||||
@view_paths.exists?(name, details, prefix, partial || false, details_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
module AbstractController
|
||||
module Testing
|
||||
|
||||
class CachedController < AbstractController::Base
|
||||
include AbstractController::Rendering
|
||||
include AbstractController::DetailsCache
|
||||
|
||||
self.view_paths = [ActionView::FixtureResolver.new(
|
||||
"default.erb" => "With Default",
|
||||
"template.erb" => "With Template",
|
||||
"some/file.erb" => "With File",
|
||||
"template_name.erb" => "With Template Name"
|
||||
)]
|
||||
end
|
||||
|
||||
class TestLocalizedCache < ActiveSupport::TestCase
|
||||
|
||||
def setup
|
||||
@controller = CachedController.new
|
||||
CachedController.clear_template_caches!
|
||||
end
|
||||
|
||||
def test_templates_are_cached
|
||||
@controller.render :template => "default.erb"
|
||||
assert_equal "With Default", @controller.response_body
|
||||
|
||||
cached = @controller.class.template_cache
|
||||
assert_equal 1, cached.size
|
||||
assert_kind_of ActionView::Template, cached.values.first["default.erb"]
|
||||
end
|
||||
|
||||
def test_cache_is_used
|
||||
CachedController.new.render :template => "default.erb"
|
||||
|
||||
@controller.expects(:find_template).never
|
||||
@controller.render :template => "default.erb"
|
||||
|
||||
assert_equal 1, @controller.class.template_cache.size
|
||||
end
|
||||
|
||||
def test_cache_changes_with_locale
|
||||
CachedController.new.render :template => "default.erb"
|
||||
|
||||
I18n.locale = :es
|
||||
@controller.render :template => "default.erb"
|
||||
|
||||
assert_equal 2, @controller.class.template_cache.size
|
||||
ensure
|
||||
I18n.locale = :en
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -7,8 +7,8 @@ class JavaScriptHelperTest < ActionView::TestCase
|
|||
|
||||
attr_accessor :formats, :output_buffer
|
||||
|
||||
def reset_formats(format)
|
||||
@format = format
|
||||
def update_details(details)
|
||||
@details = details
|
||||
yield if block_given?
|
||||
end
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ class Author::Nested < Author; end
|
|||
class PrototypeHelperBaseTest < ActionView::TestCase
|
||||
attr_accessor :formats, :output_buffer
|
||||
|
||||
def reset_formats(format)
|
||||
@format = format
|
||||
def update_details(details)
|
||||
@details = details
|
||||
yield if block_given?
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue