1
0
Fork 0
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:
José Valim 2010-03-07 19:41:58 +01:00
parent 6e0443fd43
commit c7564d74e8
18 changed files with 118 additions and 239 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -13,6 +13,7 @@ module ActionView
autoload :Handler
autoload :Handlers
autoload :Text
autoload :Lookup
end
extend Template::Handlers

View 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

View file

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

View file

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

View file

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