[#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.
This commit is contained in:
Holger Just 2012-01-16 17:08:52 +01:00
parent 637ca24aed
commit e91a1e010f
4 changed files with 45 additions and 26 deletions

View File

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

View File

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

View File

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

View File

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