mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
5a41d00455
If a template includes `# Template Collection: ...` anywhere in its source, that name will be used as the cache name for the partial that is rendered for the collection. This allows users to enable collection caching even if the template doesn't start with `<% cache ... do %>`. Moreover, the `# Template Collection: ...` notation is recognized in all template types (and template types other than ERB can define a resource_cache_call_pattern method to allow the `cache ... do` pattern to be recognized too).
170 lines
5.3 KiB
Ruby
170 lines
5.3 KiB
Ruby
require 'erubis'
|
|
|
|
module ActionView
|
|
class Template
|
|
module Handlers
|
|
class Erubis < ::Erubis::Eruby
|
|
def add_preamble(src)
|
|
@newline_pending = 0
|
|
src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
|
|
end
|
|
|
|
def add_text(src, text)
|
|
return if text.empty?
|
|
|
|
if text == "\n"
|
|
@newline_pending += 1
|
|
else
|
|
src << "@output_buffer.safe_append='"
|
|
src << "\n" * @newline_pending if @newline_pending > 0
|
|
src << escape_text(text)
|
|
src << "'.freeze;"
|
|
|
|
@newline_pending = 0
|
|
end
|
|
end
|
|
|
|
# Erubis toggles <%= and <%== behavior when escaping is enabled.
|
|
# We override to always treat <%== as escaped.
|
|
def add_expr(src, code, indicator)
|
|
case indicator
|
|
when '=='
|
|
add_expr_escaped(src, code)
|
|
else
|
|
super
|
|
end
|
|
end
|
|
|
|
BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
|
|
|
def add_expr_literal(src, code)
|
|
flush_newline_if_pending(src)
|
|
if code =~ BLOCK_EXPR
|
|
src << '@output_buffer.append= ' << code
|
|
else
|
|
src << '@output_buffer.append=(' << code << ');'
|
|
end
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
flush_newline_if_pending(src)
|
|
if code =~ BLOCK_EXPR
|
|
src << "@output_buffer.safe_expr_append= " << code
|
|
else
|
|
src << "@output_buffer.safe_expr_append=(" << code << ");"
|
|
end
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
flush_newline_if_pending(src)
|
|
super
|
|
end
|
|
|
|
def add_postamble(src)
|
|
flush_newline_if_pending(src)
|
|
src << '@output_buffer.to_s'
|
|
end
|
|
|
|
def flush_newline_if_pending(src)
|
|
if @newline_pending > 0
|
|
src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
|
|
@newline_pending = 0
|
|
end
|
|
end
|
|
end
|
|
|
|
class ERB
|
|
# Specify trim mode for the ERB compiler. Defaults to '-'.
|
|
# See ERB documentation for suitable values.
|
|
class_attribute :erb_trim_mode
|
|
self.erb_trim_mode = '-'
|
|
|
|
# Default implementation used.
|
|
class_attribute :erb_implementation
|
|
self.erb_implementation = Erubis
|
|
|
|
# Do not escape templates of these mime types.
|
|
class_attribute :escape_whitelist
|
|
self.escape_whitelist = ["text/plain"]
|
|
|
|
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
|
|
|
|
def self.call(template)
|
|
new.call(template)
|
|
end
|
|
|
|
def supports_streaming?
|
|
true
|
|
end
|
|
|
|
def handles_encoding?
|
|
true
|
|
end
|
|
|
|
def call(template)
|
|
# First, convert to BINARY, so in case the encoding is
|
|
# wrong, we can still find an encoding tag
|
|
# (<%# encoding %>) inside the String using a regular
|
|
# expression
|
|
template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
|
|
|
|
erb = template_source.gsub(ENCODING_TAG, '')
|
|
encoding = $2
|
|
|
|
erb.force_encoding valid_encoding(template.source.dup, encoding)
|
|
|
|
# Always make sure we return a String in the default_internal
|
|
erb.encode!
|
|
|
|
self.class.erb_implementation.new(
|
|
erb,
|
|
:escape => (self.class.escape_whitelist.include? template.type),
|
|
:trim => (self.class.erb_trim_mode == "-")
|
|
).src
|
|
end
|
|
|
|
# Returns Regexp to extract a cached resource's name from a cache call at the
|
|
# first line of a template.
|
|
# The extracted cache name is captured as :resource_name.
|
|
#
|
|
# <% cache notification do %> # => notification
|
|
#
|
|
# The pattern should support templates with a beginning comment:
|
|
#
|
|
# <%# Still extractable even though there's a comment %>
|
|
# <% cache notification do %> # => notification
|
|
#
|
|
# But fail to extract a name if a resource association is cached.
|
|
#
|
|
# <% cache notification.event do %> # => nil
|
|
def resource_cache_call_pattern
|
|
/\A
|
|
(?:<%\#.*%>)* # optional initial comment
|
|
\s* # followed by optional spaces or newlines
|
|
<%\s*cache[\(\s] # followed by an ERB call to cache
|
|
\s* # followed by optional spaces or newlines
|
|
(?<resource_name>\w+) # capture the cache call argument as :resource_name
|
|
[\s\)] # followed by a space or close paren
|
|
/xm
|
|
end
|
|
|
|
private
|
|
|
|
def valid_encoding(string, encoding)
|
|
# If a magic encoding comment was found, tag the
|
|
# String with this encoding. This is for a case
|
|
# where the original String was assumed to be,
|
|
# for instance, UTF-8, but a magic comment
|
|
# proved otherwise
|
|
string.force_encoding(encoding) if encoding
|
|
|
|
# If the String is valid, return the encoding we found
|
|
return string.encoding if string.valid_encoding?
|
|
|
|
# Otherwise, raise an exception
|
|
raise WrongEncodingError.new(string, string.encoding)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|