lib/erubi.rb in erubi-1.9.0 vs lib/erubi.rb in erubi-1.10.0
- old
+ new
@@ -1,44 +1,49 @@
# frozen_string_literal: true
module Erubi
- VERSION = '1.9.0'
+ VERSION = '1.10.0'
RANGE_ALL = 0..-1
+ # :nocov:
if RUBY_VERSION >= '1.9'
RANGE_FIRST = 0
RANGE_LAST = -1
- TEXT_END = RUBY_VERSION >= '2.1' ? "'.freeze;" : "';"
else
- # :nocov:
RANGE_FIRST = 0..0
RANGE_LAST = -1..-1
- TEXT_END = "';"
end
+ TEXT_END = RUBY_VERSION >= '2.1' ? "'.freeze;" : "';"
+ MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
+ # :nocov:
+
begin
require 'cgi/escape'
+ # :nocov:
unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
CGI = Object.new
CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
end
+ # :nocov:
+ # Escape characters with their HTML/XML equivalents.
def self.h(value)
CGI.escapeHTML(value.to_s)
end
rescue LoadError
+ # :nocov:
ESCAPE_TABLE = {'&' => '&'.freeze, '<' => '<'.freeze, '>' => '>'.freeze, '"' => '"'.freeze, "'" => '''.freeze}.freeze
if RUBY_VERSION >= '1.9'
- # Escape the following characters with their HTML/XML
- # equivalents.
def self.h(value)
value.to_s.gsub(/[&<>"']/, ESCAPE_TABLE)
end
else
def self.h(value)
value.to_s.gsub(/[&<>"']/){|s| ESCAPE_TABLE[s]}
end
end
+ # :nocov:
end
class Engine
# The frozen ruby source code generated from the template, which can be evaled.
attr_reader :src
@@ -48,31 +53,35 @@
# The variable name used for the buffer variable.
attr_reader :bufvar
# Initialize a new Erubi::Engine. Options:
- # :bufval :: The value to use for the buffer variable, as a string.
- # :bufvar :: The variable name to use for the buffer variable, as a string (default '::String.new')
- # :ensure :: Wrap the template in a begin/ensure block restoring the previous value of bufvar.
- # :escapefunc :: The function to use for escaping, as a string (default: '::Erubi.h').
- # :escape :: Whether to make <%= escape by default, and <%== not escape by default.
- # :escape_html :: Same as :escape, with lower priority.
- # :filename :: The filename for the template.
- # :freeze :: Whether to enable frozen string literals in the resulting source code.
- # :outvar :: Same as bufvar, with lower priority.
- # :postamble :: The postamble for the template, by default returns the resulting source code.
- # :preamble :: The preamble for the template, by default initializes up the buffer variable.
- # :regexp :: The regexp to use for scanning.
- # :src :: The initial value to use for the source code
- # :trim :: Whether to trim leading and trailing whitespace, true by default.
+ # +:bufval+ :: The value to use for the buffer variable, as a string (default <tt>'::String.new'</tt>).
+ # +:bufvar+ :: The variable name to use for the buffer variable, as a string.
+ # +:ensure+ :: Wrap the template in a begin/ensure block restoring the previous value of bufvar.
+ # +:escapefunc+ :: The function to use for escaping, as a string (default: <tt>'::Erubi.h'</tt>).
+ # +:escape+ :: Whether to make <tt><%=</tt> escape by default, and <tt><%==</tt> not escape by default.
+ # +:escape_html+ :: Same as +:escape+, with lower priority.
+ # +:filename+ :: The filename for the template.
+ # +:freeze+ :: Whether to enable frozen string literals in the resulting source code.
+ # +:literal_prefix+ :: The prefix to output when using escaped tag delimiters (default <tt>'<%'</tt>).
+ # +:literal_postfix+ :: The postfix to output when using escaped tag delimiters (default <tt>'%>'</tt>).
+ # +:outvar+ :: Same as +:bufvar+, with lower priority.
+ # +:postamble+ :: The postamble for the template, by default returns the resulting source code.
+ # +:preamble+ :: The preamble for the template, by default initializes the buffer variable.
+ # +:regexp+ :: The regexp to use for scanning.
+ # +:src+ :: The initial value to use for the source code, an empty string by default.
+ # +:trim+ :: Whether to trim leading and trailing whitespace, true by default.
def initialize(input, properties={})
@escape = escape = properties.fetch(:escape){properties.fetch(:escape_html, false)}
trim = properties[:trim] != false
@filename = properties[:filename]
@bufvar = bufvar = properties[:bufvar] || properties[:outvar] || "_buf"
bufval = properties[:bufval] || '::String.new'
regexp = properties[:regexp] || /<%(={1,2}|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m
+ literal_prefix = properties[:literal_prefix] || '<%'
+ literal_postfix = properties[:literal_postfix] || '%>'
preamble = properties[:preamble] || "#{bufvar} = #{bufval};"
postamble = properties[:postamble] || "#{bufvar}.to_s\n"
@src = src = properties[:src] || String.new
src << "# frozen_string_literal: true\n" if properties[:freeze]
@@ -108,69 +117,76 @@
else
rindex = text.rindex("\n")
if rindex
range = rindex+1..-1
s = text[range]
- if s =~ /\A[ \t]*\z/
+ if /\A[ \t]*\z/.send(MATCH_METHOD, s)
lspace = s
text[range] = ''
end
else
- if is_bol && text =~ /\A[ \t]*\z/
- lspace = text.dup
- text[RANGE_ALL] = ''
+ if is_bol && /\A[ \t]*\z/.send(MATCH_METHOD, text)
+ lspace = text
+ text = ''
end
end
end
end
is_bol = rspace
- add_text(text) if text && !text.empty?
+ add_text(text)
case ch
when '='
rspace = nil if tailch && !tailch.empty?
- add_text(lspace) if lspace
add_expression(indicator, code)
add_text(rspace) if rspace
- when '#'
- n = code.count("\n") + (rspace ? 1 : 0)
+ when nil, '-'
if trim && lspace && rspace
- add_code("\n" * n)
+ add_code("#{lspace}#{code}#{rspace}")
else
add_text(lspace) if lspace
- add_code("\n" * n)
+ add_code(code)
add_text(rspace) if rspace
end
- when '%'
- add_text("#{lspace}#{prefix||='<%'}#{code}#{tailch}#{postfix||='%>'}#{rspace}")
- when nil, '-'
+ when '#'
+ n = code.count("\n") + (rspace ? 1 : 0)
if trim && lspace && rspace
- add_code("#{lspace}#{code}#{rspace}")
+ add_code("\n" * n)
else
add_text(lspace) if lspace
- add_code(code)
+ add_code("\n" * n)
add_text(rspace) if rspace
end
+ when '%'
+ add_text("#{lspace}#{literal_prefix}#{code}#{tailch}#{literal_postfix}#{rspace}")
else
handle(indicator, code, tailch, rspace, lspace)
end
end
rest = pos == 0 ? input : input[pos..-1]
add_text(rest)
src << "\n" unless src[RANGE_LAST] == "\n"
add_postamble(postamble)
- src << "; ensure\n #{bufvar} = __original_outvar\nend\n" if properties[:ensure]
+ src << "; ensure\n " << bufvar << " = __original_outvar\nend\n" if properties[:ensure]
src.freeze
freeze
end
private
- # Add raw text to the template
+ # Add raw text to the template. Modifies argument if argument is mutable as a memory optimization.
+ # Must be called with a string, cannot be called with nil (Rails's subclass depends on it).
def add_text(text)
- @src << " #{@bufvar} << '" << text.gsub(/['\\]/, '\\\\\&') << TEXT_END unless text.empty?
+ return if text.empty?
+
+ if text.frozen?
+ text = text.gsub(/['\\]/, '\\\\\&')
+ else
+ text.gsub!(/['\\]/, '\\\\\&')
+ end
+ @src << " " << @bufvar << " << '" << text << TEXT_END
end
# Add ruby code to the template
def add_code(code)
@src << code
@@ -187,15 +203,15 @@
end
end
# Add the result of Ruby expression to the template
def add_expression_result(code)
- @src << " #{@bufvar} << (" << code << ').to_s;'
+ @src << ' ' << @bufvar << ' << (' << code << ').to_s;'
end
# Add the escaped result of Ruby expression to the template
def add_expression_result_escaped(code)
- @src << " #{@bufvar} << #{@escapefunc}((" << code << '));'
+ @src << ' ' << @bufvar << ' << ' << @escapefunc << '((' << code << '));'
end
# Add the given postamble to the src. Can be overridden in subclasses
# to make additional changes to src that depend on the current state.
def add_postamble(postamble)