From e91a1e010f4def9ca395af7f7cb89fec3b192a18 Mon Sep 17 00:00:00 2001 From: Holger Just Date: Mon, 16 Jan 2012 17:08:52 +0100 Subject: [PATCH] [#778] Properly cache Liquid markup This commit moves the markup caching into Liquid rendering. As Liquid allows to return different results depending on the environment (variables, logged user, ...) we only cache the page if it contains no active content. Unfortunetely, active content currently also includes the TOC in wiki pages. --- lib/chili_project/liquid/liquid_ext/block.rb | 10 ++++-- .../liquid/liquid_ext/context.rb | 8 +++++ lib/chili_project/liquid/template.rb | 31 +++++++++++++++++-- lib/redmine/wiki_formatting.rb | 22 +------------ 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/lib/chili_project/liquid/liquid_ext/block.rb b/lib/chili_project/liquid/liquid_ext/block.rb index 5c54c5f3..cf0b693e 100644 --- a/lib/chili_project/liquid/liquid_ext/block.rb +++ b/lib/chili_project/liquid/liquid_ext/block.rb @@ -19,15 +19,19 @@ module ChiliProject def self.included(base) base.send(:include, InstanceMethods) base.class_eval do - alias_method_chain :render_all, :cleaned_whitespace + alias_method_chain :render_all, :cleaned_whitespace_and_cache end end module InstanceMethods - def render_all_with_cleaned_whitespace(list, context) + def render_all_with_cleaned_whitespace_and_cache(list, context) # Remove the leading newline in a block's content list[0].sub!(/\A\r?\n/, "") if list[0].is_a?(String) - render_all_without_cleaned_whitespace(list, context) + + # prevent caching if there are any potentially active elements + context.not_cachable! if list.any? { |token| token.respond_to?(:render) } + + render_all_without_cleaned_whitespace_and_cache(list, context) end end end diff --git a/lib/chili_project/liquid/liquid_ext/context.rb b/lib/chili_project/liquid/liquid_ext/context.rb index 54f19604..d5123cac 100644 --- a/lib/chili_project/liquid/liquid_ext/context.rb +++ b/lib/chili_project/liquid/liquid_ext/context.rb @@ -47,6 +47,14 @@ module ChiliProject def html_results registers[:html_results] ||= {} end + + def cacheable? + registers.has_key?(:cachable) ? !!registers[:cachable] : true + end + + def not_cachable! + registers[:cachable] = false + end end end end diff --git a/lib/chili_project/liquid/template.rb b/lib/chili_project/liquid/template.rb index ab59cc45..2c2fd2eb 100644 --- a/lib/chili_project/liquid/template.rb +++ b/lib/chili_project/liquid/template.rb @@ -74,6 +74,26 @@ module ChiliProject context = context_from_render_options(*args) context.registers[:html_results] ||= {} + obj = context.registers[:object] + attribute = context.registers[:attribute] + + if Setting.cache_formatted_text? && cache_key = cache_key_for(Setting.text_formatting, obj, attribute) + # Text retrieved from the cache store may be frozen + # We need to dup it so we can do in-place substitutions with gsub! + result = Rails.cache.fetch(cache_key) + result ||= begin + result = render_context(context) + Rails.cache.write(cache_key, result) if context.cacheable? + result + end.dup + else + render_context(context) + end + end + + def render_context(context) + return '' if @root.nil? + # ENTER THE RENDERING STAGE # 1. Render the input as Liquid @@ -88,8 +108,8 @@ module ChiliProject # 2. Perform the Wiki markup transformation (e.g. Textile) obj = context.registers[:object] - attr = context.registers[:attribute] - result = Redmine::WikiFormatting.to_html(Setting.text_formatting, result, :object => obj, :attribute => attr) + attribute = context.registers[:attribute] + result = Redmine::WikiFormatting.to_html(Setting.text_formatting, result, :object => obj, :attribute => attribute) # 3. Now finally, replace the captured raw HTML bits in the final content length = nil @@ -105,6 +125,13 @@ module ChiliProject result end + + private + def cache_key_for(format, object, attribute) + if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank? + "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}" + end + end end end end diff --git a/lib/redmine/wiki_formatting.rb b/lib/redmine/wiki_formatting.rb index a3176b72..7a78fbcb 100644 --- a/lib/redmine/wiki_formatting.rb +++ b/lib/redmine/wiki_formatting.rb @@ -41,27 +41,7 @@ module Redmine end def to_html(format, text, options = {}, &block) - if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute]) - # Text retrieved from the cache store may be frozen - # We need to dup it so we can do in-place substitutions with gsub! - cache_store.fetch cache_key do - formatter_for(format).new(text).to_html - end.dup - else - formatter_for(format).new(text).to_html - end - end - - # Returns a cache key for the given text +format+, +object+ and +attribute+ or nil if no caching should be done - def cache_key_for(format, object, attribute) - if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank? - "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}" - end - end - - # Returns the cache store used to cache HTML output - def cache_store - ActionController::Base.cache_store + formatter_for(format).new(text).to_html end end end