Adds a setting to cache textile rendering (off by default).

* it uses ActionController cache store which is MemoryStore by default and can be configured with config.action_controller.cache_store
* macro processing was moved out of textile rendering so that it doesn't get cached
* no noticeable improvement is expected for small portions of text, so only texts larger than 2KB are cached

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3372 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang
2010-02-06 10:40:21 +00:00
parent 6c8b87fbc8
commit 2ad8242ae7
44 changed files with 100 additions and 33 deletions
+52 -1
View File
@@ -44,7 +44,58 @@ module Redmine
end
def to_html(format, text, options = {}, &block)
formatter_for(format).new(text).to_html(&block)
text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache && 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.fetch cache_key do
formatter_for(format).new(text).to_html
end.dup
else
formatter_for(format).new(text).to_html
end
if block_given?
execute_macros(text, block)
end
text
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
ActionController::Base.cache_store
end
MACROS_RE = /
(!)? # escaping
(
\{\{ # opening tag
([\w]+) # macro name
(\(([^\}]*)\))? # optional arguments
\}\} # closing tag
)
/x unless const_defined?(:MACROS_RE)
# Macros substitution
def execute_macros(text, macros_runner)
text.gsub!(MACROS_RE) do
esc, all, macro = $1, $2, $3.downcase
args = ($5 || '').split(',').each(&:strip)
if esc.nil?
begin
macros_runner.call(macro, args)
rescue => e
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
end || all
else
all
end
end
end
end
@@ -24,7 +24,7 @@ module Redmine
class Formatter < RedCloth3
# auto_link rule after textile rules so that it doesn't break !image_url! tags
RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc]
def initialize(*args)
super
@@ -33,9 +33,8 @@ module Redmine
self.filter_styles=true
end
def to_html(*rules, &block)
def to_html(*rules)
@toc = []
@macros_runner = block
super(*RULES).to_s
end
@@ -102,32 +101,6 @@ module Redmine
end
end
MACROS_RE = /
(!)? # escaping
(
\{\{ # opening tag
([\w]+) # macro name
(\(([^\}]*)\))? # optional arguments
\}\} # closing tag
)
/x unless const_defined?(:MACROS_RE)
def inline_macros(text)
text.gsub!(MACROS_RE) do
esc, all, macro = $1, $2, $3.downcase
args = ($5 || '').split(',').each(&:strip)
if esc.nil?
begin
@macros_runner.call(macro, args)
rescue => e
"<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
end || all
else
all
end
end
end
AUTO_LINK_RE = %r{
( # leading text
<\w+.*?>| # leading HTML tag, or