Permalink
Browse files

Caches and cache clearing seems to actually work, but the actual arch…

…itecture is kind of messy. Next: CLEAN UP.
  • Loading branch information...
1 parent 9f5cd01 commit 9b552fb300c4606fe517eadaa30708e9d75498a6 @wycats wycats committed Aug 14, 2009
@@ -19,15 +19,20 @@ def inherited(klass)
end
end
+ def clear_template_caches!
+ @found_layouts.clear if @found_layouts
+ super
+ end
+
def cache_layout(details)
layout = @found_layouts
- values = details.values_at(:formats, :locale)
+ key = Thread.current[:format_locale_key]
# Cache nil
- if layout.key?(values)
- return layout[values]
+ if layout.key?(key)
+ return layout[key]
else
- layout[values] = yield
+ layout[key] = yield
end
end
@@ -111,12 +111,21 @@ def self.body_to_s(body)
def _determine_template(options)
name = (options[:_template_name] || action_name).to_s
- options[:_template] ||= view_paths.find(
- name, { :formats => formats }, options[:_prefix], options[:_partial]
- )
+ options[:_template] ||= with_template_cache(name) do
+ view_paths.find(
+ name, { :formats => formats }, options[:_prefix], options[:_partial]
+ )
+ end
+ end
+
+ def with_template_cache(name)
+ yield
end
module ClassMethods
+ def clear_template_caches!
+ end
+
# Append a path to the list of view paths for this controller.
#
# ==== Parameters
@@ -134,6 +143,7 @@ def append_view_path(path)
# 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.unshift(path)
end
@@ -148,6 +158,7 @@ def view_paths
# 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)
end
@@ -1,11 +1,39 @@
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
extend ActiveSupport::Concern
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(*)
self.formats = request.formats.map {|x| x.to_sym}
+ Thread.current[:format_locale_key] = HashKey.get(self.class, formats, I18n.locale)
super
end
@@ -34,6 +62,10 @@ def _prefix
controller_path
end
+ def with_template_cache(name)
+ self.class.template_cache[Thread.current[:format_locale_key]][name] ||= super
+ end
+
def _determine_template(options)
if options.key?(:text)
options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first)
@@ -173,44 +173,50 @@ module Partials
extend ActiveSupport::Concern
class PartialRenderer
- def self.partial_names
- @partial_names ||= Hash.new {|h,k| h[k] = ActiveSupport::ConcurrentHash.new }
- end
+ PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
+ TEMPLATES = Hash.new {|h,k| h[k] = {} }
- def self.formats
- @formats ||= Hash.new {|h,k| h[k] = Hash.new{|h,k| h[k] = Hash.new {|h,k| h[k] = {}}}}
- end
+ attr_reader :template
def initialize(view_context, options, block)
- partial = options[:partial]
-
- @memo = {}
@view = view_context
- @options = options
- @locals = options[:locals] || {}
- @block = block
-
- # Set up some instance variables to speed up memoizing
- @partial_names = self.class.partial_names[@view.controller.class]
- @templates = self.class.formats
- @format = view_context.formats
-
- # Set up the object and path
- @object = partial.is_a?(String) ? options[:object] : partial
- @path = partial_path(partial)
+ @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]
+
+ @options = options
+ @locals = options[:locals] || {}
+ @block = block
+
+ if String === partial
+ @object = options[:object]
+ @path = partial
+ else
+ @object = partial
+ @path = partial_path(partial)
+ end
end
def render
- return render_collection if collection
-
- template = find_template
- render_template(template, @object || @locals[template.variable_name])
+ if @collection = collection
+ render_collection
+ else
+ @template = template = find_template
+ render_template(template, @object || @locals[template.variable_name])
+ end
end
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)
spacer = find_template(@options[:spacer_template]).render(@view, @locals)
@@ -228,12 +234,14 @@ def collection_with_template(template)
counter_name = template.counter_name
locals[counter_name] = -1
- collection.each do |object|
+ @collection.each do |object|
locals[counter_name] += 1
locals[as] = object
segments << template.render(@view, locals)
end
+
+ @template = template
segments
end
@@ -243,52 +251,56 @@ def collection_without_template
segments, locals, as = [], @locals, options[:as]
index, template = -1, nil
- collection.each do |object|
+ @collection.each do |object|
template = find_template(partial_path(object))
locals[template.counter_name] = (index += 1)
locals[template.variable_name] = object
segments << template.render(@view, locals)
end
- @options[:_template] = template
+ @template = template
segments
end
def render_template(template, object = @object)
- @options[:_template] ||= template
-
- # TODO: is locals[:object] really necessary?
- @locals[:object] = @locals[template.variable_name] = object
- @locals[@options[:as]] = object if @options[:as]
+ options, locals, view = @options, @locals, @view
+ locals[options[:as] || template.variable_name] = object
- content = template.render(@view, @locals) do |*name|
+ content = template.render(view, locals) do |*name|
@view._layout_for(*name, &@block)
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
def collection
- @collection ||= if @object.respond_to?(:to_ary)
+ if @object.respond_to?(:to_ary)
@object
elsif @options.key?(:collection)
@options[:collection] || []
end
end
def find_template(path = @path)
- return if !path
- @templates[path][@view.controller_path][@format][I18n.locale] ||= begin
- prefix = @view.controller.controller_path unless path.include?(?/)
- @view.find(path, {:formats => @view.formats}, prefix, true)
+ unless @templates
+ path && _find_template(path)
+ else
+ path && @templates[path] ||= _find_template(path)
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)
- return object if object.is_a?(String)
@partial_names[object.class] ||= begin
return nil unless object.respond_to?(:to_model)
@@ -302,13 +314,25 @@ def partial_path(object = @object)
def render_partial(options)
_evaluate_assigns_and_ivars
- # TODO: Handle other details here.
- self.formats = options[:_details][:formats] if options[:_details]
- _render_partial(options)
+
+ details = options[:_details]
+
+ # Is this needed
+ self.formats = details[:formats] if details
+ renderer = PartialRenderer.new(self, options, nil)
+ text = renderer.render
+ options[:_template] = renderer.template
+ text
end
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
@@ -97,7 +97,22 @@ def #{method_name}(local_assigns)
raise ActionView::TemplateError.new(self, {}, e)
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)
# TODO: is locals.keys.hash reliably the same?
@method_names[locals.keys.hash] ||=

0 comments on commit 9b552fb

Please sign in to comment.