1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Caches and cache clearing seems to actually work, but the actual architecture is kind of messy. Next: CLEAN UP.

This commit is contained in:
Yehuda Katz 2009-08-14 12:00:52 -07:00
parent 9f5cd0156a
commit 9b552fb300
5 changed files with 144 additions and 57 deletions

View file

@ -19,15 +19,20 @@ module AbstractController
end end
end end
def clear_template_caches!
@found_layouts.clear if @found_layouts
super
end
def cache_layout(details) def cache_layout(details)
layout = @found_layouts layout = @found_layouts
values = details.values_at(:formats, :locale) key = Thread.current[:format_locale_key]
# Cache nil # Cache nil
if layout.key?(values) if layout.key?(key)
return layout[values] return layout[key]
else else
layout[values] = yield layout[key] = yield
end end
end end

View file

@ -111,12 +111,21 @@ module AbstractController
def _determine_template(options) def _determine_template(options)
name = (options[:_template_name] || action_name).to_s name = (options[:_template_name] || action_name).to_s
options[:_template] ||= view_paths.find( options[:_template] ||= with_template_cache(name) do
name, { :formats => formats }, options[:_prefix], options[:_partial] view_paths.find(
) name, { :formats => formats }, options[:_prefix], options[:_partial]
)
end
end
def with_template_cache(name)
yield
end end
module ClassMethods module ClassMethods
def clear_template_caches!
end
# Append a path to the list of view paths for this controller. # Append a path to the list of view paths for this controller.
# #
# ==== Parameters # ==== Parameters
@ -134,6 +143,7 @@ module AbstractController
# the default view path. You may also provide a custom view path # the default view path. You may also provide a custom view path
# (see ActionView::ViewPathSet for more information) # (see ActionView::ViewPathSet for more information)
def prepend_view_path(path) def prepend_view_path(path)
clear_template_caches!
self.view_paths.unshift(path) self.view_paths.unshift(path)
end end
@ -148,6 +158,7 @@ module AbstractController
# paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that; # paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
# otherwise, process the parameter into a ViewPathSet. # otherwise, process the parameter into a ViewPathSet.
def view_paths=(paths) def view_paths=(paths)
clear_template_caches!
self._view_paths = paths.is_a?(ActionView::PathSet) ? self._view_paths = paths.is_a?(ActionView::PathSet) ?
paths : ActionView::Base.process_view_paths(paths) paths : ActionView::Base.process_view_paths(paths)
end end

View file

@ -1,11 +1,39 @@
module ActionController module ActionController
class HashKey
@hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } }
def self.get(klass, formats, locale)
@hash_keys[klass][formats][locale] ||= new(klass, formats, locale)
end
attr_accessor :hash
def initialize(klass, formats, locale)
@hash = [formats, locale].hash
end
alias_method :eql?, :equal?
end
module RenderingController module RenderingController
extend ActiveSupport::Concern extend ActiveSupport::Concern
include AbstractController::RenderingController include AbstractController::RenderingController
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 process_action(*) def process_action(*)
self.formats = request.formats.map {|x| x.to_sym} self.formats = request.formats.map {|x| x.to_sym}
Thread.current[:format_locale_key] = HashKey.get(self.class, formats, I18n.locale)
super super
end end
@ -34,6 +62,10 @@ module ActionController
controller_path controller_path
end end
def with_template_cache(name)
self.class.template_cache[Thread.current[:format_locale_key]][name] ||= super
end
def _determine_template(options) def _determine_template(options)
if options.key?(:text) if options.key?(:text)
options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first) options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first)

View file

@ -173,44 +173,50 @@ module ActionView
extend ActiveSupport::Concern extend ActiveSupport::Concern
class PartialRenderer class PartialRenderer
def self.partial_names PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
@partial_names ||= Hash.new {|h,k| h[k] = ActiveSupport::ConcurrentHash.new } TEMPLATES = Hash.new {|h,k| h[k] = {} }
end
def self.formats attr_reader :template
@formats ||= Hash.new {|h,k| h[k] = Hash.new{|h,k| h[k] = Hash.new {|h,k| h[k] = {}}}}
end
def initialize(view_context, options, block) def initialize(view_context, options, block)
@view = view_context
@partial_names = PARTIAL_NAMES[@view.controller.class]
key = Thread.current[:format_locale_key]
@templates = TEMPLATES[key] if key
setup(options, block)
end
def setup(options, block)
partial = options[:partial] partial = options[:partial]
@memo = {} @options = options
@view = view_context @locals = options[:locals] || {}
@options = options @block = block
@locals = options[:locals] || {}
@block = block
# Set up some instance variables to speed up memoizing if String === partial
@partial_names = self.class.partial_names[@view.controller.class] @object = options[:object]
@templates = self.class.formats @path = partial
@format = view_context.formats else
@object = partial
# Set up the object and path @path = partial_path(partial)
@object = partial.is_a?(String) ? options[:object] : partial end
@path = partial_path(partial)
end end
def render def render
return render_collection if collection if @collection = collection
render_collection
template = find_template else
render_template(template, @object || @locals[template.variable_name]) @template = template = find_template
render_template(template, @object || @locals[template.variable_name])
end
end end
def render_collection def render_collection
@options[:_template] = template = find_template @template = template = find_template
return nil if collection.blank? return nil if @collection.blank?
if @options.key?(:spacer_template) if @options.key?(:spacer_template)
spacer = find_template(@options[:spacer_template]).render(@view, @locals) spacer = find_template(@options[:spacer_template]).render(@view, @locals)
@ -228,12 +234,14 @@ module ActionView
counter_name = template.counter_name counter_name = template.counter_name
locals[counter_name] = -1 locals[counter_name] = -1
collection.each do |object| @collection.each do |object|
locals[counter_name] += 1 locals[counter_name] += 1
locals[as] = object locals[as] = object
segments << template.render(@view, locals) segments << template.render(@view, locals)
end end
@template = template
segments segments
end end
@ -243,7 +251,7 @@ module ActionView
segments, locals, as = [], @locals, options[:as] segments, locals, as = [], @locals, options[:as]
index, template = -1, nil index, template = -1, nil
collection.each do |object| @collection.each do |object|
template = find_template(partial_path(object)) template = find_template(partial_path(object))
locals[template.counter_name] = (index += 1) locals[template.counter_name] = (index += 1)
locals[template.variable_name] = object locals[template.variable_name] = object
@ -251,28 +259,28 @@ module ActionView
segments << template.render(@view, locals) segments << template.render(@view, locals)
end end
@options[:_template] = template @template = template
segments segments
end end
def render_template(template, object = @object) def render_template(template, object = @object)
@options[:_template] ||= template options, locals, view = @options, @locals, @view
locals[options[:as] || template.variable_name] = object
# TODO: is locals[:object] really necessary? content = template.render(view, locals) do |*name|
@locals[:object] = @locals[template.variable_name] = object
@locals[@options[:as]] = object if @options[:as]
content = template.render(@view, @locals) do |*name|
@view._layout_for(*name, &@block) @view._layout_for(*name, &@block)
end end
return content if @block || !@options[:layout]
find_template(@options[:layout]).render(@view, @locals) { content }
end
if @block || !options[:layout]
content
else
find_template(options[:layout]).render(@view, @locals) { content }
end
end
private private
def collection def collection
@collection ||= if @object.respond_to?(:to_ary) if @object.respond_to?(:to_ary)
@object @object
elsif @options.key?(:collection) elsif @options.key?(:collection)
@options[:collection] || [] @options[:collection] || []
@ -280,15 +288,19 @@ module ActionView
end end
def find_template(path = @path) def find_template(path = @path)
return if !path unless @templates
@templates[path][@view.controller_path][@format][I18n.locale] ||= begin path && _find_template(path)
prefix = @view.controller.controller_path unless path.include?(?/) else
@view.find(path, {:formats => @view.formats}, prefix, true) path && @templates[path] ||= _find_template(path)
end end
end end
def _find_template(path)
prefix = @view.controller.controller_path unless path.include?(?/)
@view.find(path, {:formats => @view.formats}, prefix, true)
end
def partial_path(object = @object) def partial_path(object = @object)
return object if object.is_a?(String)
@partial_names[object.class] ||= begin @partial_names[object.class] ||= begin
return nil unless object.respond_to?(:to_model) return nil unless object.respond_to?(:to_model)
@ -302,13 +314,25 @@ module ActionView
def render_partial(options) def render_partial(options)
_evaluate_assigns_and_ivars _evaluate_assigns_and_ivars
# TODO: Handle other details here.
self.formats = options[:_details][:formats] if options[:_details] details = options[:_details]
_render_partial(options)
# Is this needed
self.formats = details[:formats] if details
renderer = PartialRenderer.new(self, options, nil)
text = renderer.render
options[:_template] = renderer.template
text
end end
def _render_partial(options, &block) #:nodoc: def _render_partial(options, &block) #:nodoc:
PartialRenderer.new(self, options, block).render if @renderer
@renderer.setup(options, block)
else
@renderer = PartialRenderer.new(self, options, block)
end
@renderer.render
end end
end end

View file

@ -98,6 +98,21 @@ module ActionView
end end
end end
class LocalsKey
@hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } }
def self.get(*locals)
@hash_keys[*locals] ||= new(klass, format, locale)
end
attr_accessor :hash
def initialize(klass, format, locale)
@hash = locals.hash
end
alias_method :eql?, :equal?
end
def build_method_name(locals) def build_method_name(locals)
# TODO: is locals.keys.hash reliably the same? # TODO: is locals.keys.hash reliably the same?
@method_names[locals.keys.hash] ||= @method_names[locals.keys.hash] ||=