New Upstream Snapshot - ruby-erubi
Ready changes
Summary
Merged new upstream version: 1.12.0+git20221226.1.7fedb8c (was: 1.9.0).
Resulting package
Built on 2023-01-19T20:58 (took 5m4s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-snapshots ruby-erubi
Lintian Result
Diff
diff --git a/.ci.gemfile b/.ci.gemfile
new file mode 100644
index 0000000..33c634d
--- /dev/null
+++ b/.ci.gemfile
@@ -0,0 +1,11 @@
+source 'https://rubygems.org'
+
+gem 'minitest-global_expectations'
+
+if RUBY_VERSION < '2.4.0'
+ # Until mintest 5.12.0 is fixed
+ gem 'minitest', '5.11.3'
+ gem 'rake', '<10.0.0'
+else
+ gem 'rake'
+end
diff --git a/CHANGELOG b/CHANGELOG
index f9b13bf..41c8815 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,25 @@
+=== 1.12.0 (2022-12-22)
+
+* Use erb/escape for faster html escaping if available (jeremyevans)
+
+* Default :freeze_template_literals option to false if running with --enable-frozen-string-literal (casperisfine) (#35)
+
+=== 1.11.0 (2022-08-02)
+
+* Support :freeze_template_literals option for configuring whether to add .freeze to template literal strings (casperisfine) (#33)
+
+* Support :chain_appends option for chaining appends to the buffer variable (casperisfine, jeremyevans) (#32)
+
+* Avoid unnecessary defined? usage on Ruby 3+ when using the :ensure option (jeremyevans)
+
+=== 1.10.0 (2020-11-13)
+
+* Improve template parsing, mostly by reducing allocations (jeremyevans)
+
+* Do not ship tests in the gem, reducing gem size about 20% (jeremyevans)
+
+* Support :literal_prefix and :literal_postfix options for how to output literal tags (e.g. <%% code %>) (jaredcwhite) (#26, #27)
+
=== 1.9.0 (2019-09-25)
* Change default :bufvar from 'String.new' to '::String.new' to work with BasicObject (jeremyevans)
diff --git a/MIT-LICENSE b/MIT-LICENSE
index 036808e..a8950e2 100644
--- a/MIT-LICENSE
+++ b/MIT-LICENSE
@@ -1,5 +1,5 @@
copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-copyright(c) 2016-2018 Jeremy Evans
+copyright(c) 2016-2021 Jeremy Evans
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/README.rdoc b/README.rdoc
index b87a685..1502aad 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -5,11 +5,11 @@ the same basic algorithm, with the following differences:
* Handles postfix conditionals when using escaping (e.g. <tt><%= foo if bar %></tt>)
* Supports frozen_string_literal: true in templates via :freeze option
-* Works with ruby's --enable-frozen-string-literal option
+* Works with ruby's <tt>--enable-frozen-string-literal</tt> option
* Automatically freezes strings for template text when ruby optimizes it (on ruby 2.1+)
-* Escapes ' (apostrophe) when escaping for better XSS protection
+* Escapes <tt>'</tt> (apostrophe) when escaping for better XSS protection
* Has 6x faster escaping on ruby 2.3+ by using cgi/escape
-* Has 86% smaller memory footprint
+* Has 81% smaller memory footprint (calculated using +ObjectSpace.memsize_of_all+)
* Does no monkey patching (Erubis adds a method to Kernel)
* Uses an immutable design (all options passed to the constructor, which returns a frozen object)
* Has simpler internals (1 file, <150 lines of code)
@@ -92,7 +92,7 @@ instance variable. Example:
# </form>
# after
-Alternatively, passing the option +:yield_returns_buffer => true+ will return the
+Alternatively, passing the option <tt>:yield_returns_buffer => true</tt> will return the
buffer captured by the block instead of the last expression in the block.
= Reporting Bugs
diff --git a/Rakefile b/Rakefile
index 753ff1b..4497485 100644
--- a/Rakefile
+++ b/Rakefile
@@ -42,7 +42,7 @@ end
spec = proc do |env|
env.each{|k,v| ENV[k] = v}
- sh "#{FileUtils::RUBY} test/test.rb"
+ sh "#{FileUtils::RUBY} #{'-w' if RUBY_VERSION >= '3'} test/test.rb"
env.each{|k,v| ENV.delete(k)}
end
@@ -57,13 +57,6 @@ desc "Run specs with coverage"
task "spec_cov" do
spec.call('COVERAGE'=>'1')
end
-
-desc "Run specs with -w, some warnings filtered"
-task "spec_w" do
- ENV['RUBYOPT'] ? (ENV['RUBYOPT'] += " -w") : (ENV['RUBYOPT'] = '-w')
- rake = ENV['RAKE'] || "#{FileUtils::RUBY} -S rake"
- sh %{#{rake} 2>&1 | egrep -v \": warning: instance variable @.* not initialized|: warning: method redefined; discarding old|: warning: previous definition of|: warning: statement not reached"}
-end
### Other
diff --git a/debian/changelog b/debian/changelog
index c68893e..c6507de 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ruby-erubi (1.12.0+git20221226.1.7fedb8c-1) UNRELEASED; urgency=low
+
+ * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk> Thu, 19 Jan 2023 20:55:33 -0000
+
ruby-erubi (1.9.0-2) unstable; urgency=medium
* Team Upload
diff --git a/erubi.gemspec b/erubi.gemspec
index def0102..ea548a2 100644
--- a/erubi.gemspec
+++ b/erubi.gemspec
@@ -1,39 +1,25 @@
-#########################################################
-# This file has been automatically generated by gem2tgz #
-#########################################################
-# -*- encoding: utf-8 -*-
-# stub: erubi 1.9.0 ruby lib
+# frozen_string_literal: true
+require File.expand_path("../lib/erubi", __FILE__)
Gem::Specification.new do |s|
- s.name = "erubi".freeze
- s.version = "1.9.0"
-
- s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
- s.require_paths = ["lib".freeze]
- s.authors = ["Jeremy Evans".freeze, "kuwata-lab.com".freeze]
- s.date = "2019-09-25"
- s.description = "Erubi is a ERB template engine for ruby. It is a simplified fork of Erubis".freeze
- s.email = "code@jeremyevans.net".freeze
- s.extra_rdoc_files = ["CHANGELOG".freeze, "MIT-LICENSE".freeze, "README.rdoc".freeze]
- s.files = ["CHANGELOG".freeze, "MIT-LICENSE".freeze, "README.rdoc".freeze, "Rakefile".freeze, "lib/erubi.rb".freeze, "lib/erubi/capture_end.rb".freeze, "test/test.rb".freeze]
- s.homepage = "https://github.com/jeremyevans/erubi".freeze
- s.licenses = ["MIT".freeze]
- s.rdoc_options = ["--quiet".freeze, "--line-numbers".freeze, "--inline-source".freeze, "--title".freeze, "Erubi: Small ERB Implementation".freeze, "--main".freeze, "README.rdoc".freeze]
- s.rubygems_version = "2.5.2.1".freeze
- s.summary = "Small ERB Implementation".freeze
-
- if s.respond_to? :specification_version then
- s.specification_version = 4
-
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
- s.add_development_dependency(%q<minitest>.freeze, [">= 0"])
- s.add_development_dependency(%q<minitest-global_expectations>.freeze, [">= 0"])
- else
- s.add_dependency(%q<minitest>.freeze, [">= 0"])
- s.add_dependency(%q<minitest-global_expectations>.freeze, [">= 0"])
- end
- else
- s.add_dependency(%q<minitest>.freeze, [">= 0"])
- s.add_dependency(%q<minitest-global_expectations>.freeze, [">= 0"])
- end
+ s.name = 'erubi'
+ s.version = Erubi::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.extra_rdoc_files = ["README.rdoc", "CHANGELOG", "MIT-LICENSE"]
+ s.rdoc_options += ["--quiet", "--line-numbers", "--inline-source", '--title', 'Erubi: Small ERB Implementation', '--main', 'README.rdoc']
+ s.license = "MIT"
+ s.summary = "Small ERB Implementation"
+ s.author = ["Jeremy Evans", 'kuwata-lab.com']
+ s.email = "code@jeremyevans.net"
+ s.homepage = "https://github.com/jeremyevans/erubi"
+ s.files = %w(MIT-LICENSE CHANGELOG README.rdoc Rakefile lib/erubi.rb lib/erubi/capture_end.rb)
+ s.description = "Erubi is a ERB template engine for ruby. It is a simplified fork of Erubis"
+ s.add_development_dependency "minitest"
+ s.add_development_dependency "minitest-global_expectations"
+ s.metadata = {
+ 'bug_tracker_uri' => 'https://github.com/jeremyevans/erubi/issues',
+ 'mailing_list_uri' => 'https://github.com/jeremyevans/erubi/discussions',
+ 'changelog_uri' => 'https://github.com/jeremyevans/erubi/blob/master/CHANGELOG',
+ 'source_code_uri' => 'https://github.com/jeremyevans/erubi',
+ }
end
diff --git a/lib/erubi.rb b/lib/erubi.rb
index 0d5e738..e419d36 100644
--- a/lib/erubi.rb
+++ b/lib/erubi.rb
@@ -1,45 +1,60 @@
# frozen_string_literal: true
module Erubi
- VERSION = '1.9.0'
- RANGE_ALL = 0..-1
+ VERSION = '1.12.0'
+ # :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
+ MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
+ SKIP_DEFINED_FOR_INSTANCE_VARIABLE = RUBY_VERSION > '3'
+ FREEZE_TEMPLATE_LITERALS = !eval("''").frozen? && RUBY_VERSION >= '2.1'
+ # :nocov:
+
begin
- require 'cgi/escape'
- unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
- CGI = Object.new
- CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
- end
- def self.h(value)
- CGI.escapeHTML(value.to_s)
- end
+ require 'erb/escape'
+ # :nocov:
+ define_singleton_method(:h, ERB::Escape.instance_method(:html_escape))
+ # :nocov:
rescue LoadError
- 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)
+ 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
- else
+ # :nocov:
+ # Escape characters with their HTML/XML equivalents.
def self.h(value)
- value.to_s.gsub(/[&<>"']/){|s| ESCAPE_TABLE[s]}
+ CGI.escapeHTML(value.to_s)
+ end
+ rescue LoadError
+ # :nocov:
+ ESCAPE_TABLE = {'&' => '&'.freeze, '<' => '<'.freeze, '>' => '>'.freeze, '"' => '"'.freeze, "'" => '''.freeze}.freeze
+ if RUBY_VERSION >= '1.9'
+ 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
end
class Engine
+ # The default regular expression used for scanning.
+ DEFAULT_REGEXP = /<%(={1,2}|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m
+
# The frozen ruby source code generated from the template, which can be evaled.
attr_reader :src
@@ -50,38 +65,66 @@ module Erubi
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.
+ # +:chain_appends+ :: Whether to chain <tt><<</t> calls to the buffer variable. Offers better
+ # performance, but can cause issues when the buffer variable is reassigned during
+ # template rendering (default +false+).
+ # +: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 add a <tt>frozen_string_literal: true</tt> magic comment at the top of
+ # the resulting source code. Note this may cause problems if you are wrapping the resulting
+ # source code in other code, because the magic comment only has an effect at the beginning of
+ # the file, and having the magic comment later in the file can trigger warnings.
+ # +:freeze_template_literals+ :: Whether to suffix all literal strings for template code with <tt>.freeze</tt>
+ # (default: +true+ on Ruby 2.1+, +false+ on Ruby 2.0 and older).
+ # Can be set to +false+ on Ruby 2.3+ when frozen string literals are enabled
+ # in order to improve performance.
+ # +: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
+ regexp = properties[:regexp] || DEFAULT_REGEXP
+ literal_prefix = properties[:literal_prefix] || '<%'
+ literal_postfix = properties[:literal_postfix] || '%>'
preamble = properties[:preamble] || "#{bufvar} = #{bufval};"
postamble = properties[:postamble] || "#{bufvar}.to_s\n"
+ @chain_appends = properties[:chain_appends]
+ @text_end = if properties.fetch(:freeze_template_literals, FREEZE_TEMPLATE_LITERALS)
+ "'.freeze"
+ else
+ "'"
+ end
+ @buffer_on_stack = false
@src = src = properties[:src] || String.new
src << "# frozen_string_literal: true\n" if properties[:freeze]
- src << "begin; __original_outvar = #{bufvar} if defined?(#{bufvar}); " if properties[:ensure]
+ if properties[:ensure]
+ src << "begin; __original_outvar = #{bufvar}"
+ if SKIP_DEFINED_FOR_INSTANCE_VARIABLE && /\A@[^@]/ =~ bufvar
+ src << "; "
+ else
+ src << " if defined?(#{bufvar}); "
+ end
+ end
unless @escapefunc = properties[:escapefunc]
if escape
@escapefunc = '__erubi.h'
- src << "__erubi = ::Erubi;"
+ src << "__erubi = ::Erubi; "
else
@escapefunc = '::Erubi.h'
end
@@ -110,46 +153,45 @@ module Erubi
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
@@ -159,22 +201,33 @@ module Erubi
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
+
+ with_buffer{@src << " << '" << text << @text_end}
end
# Add ruby code to the template
def add_code(code)
+ terminate_expression
@src << code
@src << ';' unless code[RANGE_LAST] == "\n"
+ @buffer_on_stack = false
end
# Add the given ruby expression result to the template,
@@ -189,23 +242,52 @@ module Erubi
# Add the result of Ruby expression to the template
def add_expression_result(code)
- @src << " #{@bufvar} << (" << code << ').to_s;'
+ with_buffer{@src << ' << (' << code << ').to_s'}
end
# Add the escaped result of Ruby expression to the template
def add_expression_result_escaped(code)
- @src << " #{@bufvar} << #{@escapefunc}((" << code << '));'
+ with_buffer{@src << ' << ' << @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)
- src << postamble
+ terminate_expression
+ @src << postamble
end
# Raise an exception, as the base engine class does not support handling other indicators.
def handle(indicator, code, tailch, rspace, lspace)
raise ArgumentError, "Invalid indicator: #{indicator}"
end
+
+ # Make sure the buffer variable is the target of the next append
+ # before yielding to the block. Mark that the buffer is the target
+ # of the next append after the block executes.
+ #
+ # This method should only be called if the block will result in
+ # code where << will append to the bufvar.
+ def with_buffer
+ if @chain_appends
+ unless @buffer_on_stack
+ @src << '; ' << @bufvar
+ end
+ yield
+ @buffer_on_stack = true
+ else
+ @src << ' ' << @bufvar
+ yield
+ @src << ';'
+ end
+ end
+
+ # Make sure that any current expression has been terminated.
+ # The default is to terminate all expressions, but when
+ # the chain_appends option is used, expressions may not be
+ # terminated.
+ def terminate_expression
+ @src << '; ' if @chain_appends
+ end
end
end
diff --git a/lib/erubi/capture_end.rb b/lib/erubi/capture_end.rb
index 5bc04f7..e381dfb 100644
--- a/lib/erubi/capture_end.rb
+++ b/lib/erubi/capture_end.rb
@@ -3,15 +3,15 @@
require 'erubi'
module Erubi
- # An engine class that supports capturing blocks via the <%|= and <%|== tags,
- # explicitly ending the captures using <%| end %> blocks.
+ # An engine class that supports capturing blocks via the <tt><%|=</tt> and <tt><%|==</tt> tags,
+ # explicitly ending the captures using <tt><%|</tt> end <tt>%></tt> blocks.
class CaptureEndEngine < Engine
# Initializes the engine. Accepts the same arguments as ::Erubi::Engine, and these
# additional options:
- # :escape_capture :: Whether to make <%|= escape by default, and <%|== not escape by default,
+ # :escape_capture :: Whether to make <tt><%|=</tt> escape by default, and <tt><%|==</tt> not escape by default,
# defaults to the same value as :escape.
- # :yield_returns_buffer :: Whether to have <%| tags insert the buffer as an expression, so that
- # <%| end %> tags will have the buffer be the last expression inside
+ # :yield_returns_buffer :: Whether to have <tt><%|</tt> tags insert the buffer as an expression, so that
+ # <tt><%| end %></tt> tags will have the buffer be the last expression inside
# the block, and therefore have the buffer be returned by the yield
# expression. Normally the buffer will be returned anyway, but there
# are cases where the last expression will not be the buffer,
@@ -36,13 +36,19 @@ module Erubi
rspace = nil if tailch && !tailch.empty?
add_text(lspace) if lspace
escape_capture = !((indicator == '|=') ^ @escape_capture)
- src << "begin; (#{@bufstack} ||= []) << #{@bufvar}; #{@bufvar} = #{@bufval}; #{@bufstack}.last << #{@escapefunc if escape_capture}((" << code
+ terminate_expression
+ @src << "begin; (#{@bufstack} ||= []) << #{@bufvar}; #{@bufvar} = #{@bufval}; #{@bufstack}.last << #{@escapefunc if escape_capture}((" << code
+ @buffer_on_stack = false
add_text(rspace) if rspace
when '|'
rspace = nil if tailch && !tailch.empty?
add_text(lspace) if lspace
- result = @yield_returns_buffer ? " #{@bufvar}; " : ""
- src << result << code << ")).to_s; ensure; #{@bufvar} = #{@bufstack}.pop; end;"
+ if @yield_returns_buffer
+ terminate_expression
+ @src << " #{@bufvar}; "
+ end
+ @src << code << ")).to_s; ensure; #{@bufvar} = #{@bufstack}.pop; end;"
+ @buffer_on_stack = false
add_text(rspace) if rspace
else
super
diff --git a/test/test.rb b/test/test.rb
index 940e5d7..7ec6ce8 100644
--- a/test/test.rb
+++ b/test/test.rb
@@ -13,6 +13,8 @@ if ENV['COVERAGE']
ENV.delete('COVERAGE')
SimpleCov.instance_eval do
+ enable_coverage :branch
+
start do
add_filter "/test/"
add_group('Missing'){|src| src.covered_percent < 100}
@@ -37,7 +39,8 @@ describe Erubi::Engine do
t = (@options[:engine] || Erubi::Engine).new(input, @options)
tsrc = t.src
eval(tsrc, block.binding).must_equal result
- tsrc = tsrc.gsub("'.freeze;", "';") if RUBY_VERSION >= '2.1'
+ strip_freeze = defined?(@strip_freeze) ? @strip_freeze : RUBY_VERSION >= '2.1'
+ tsrc = tsrc.gsub(/\.freeze/, '') if strip_freeze
tsrc.must_equal src
end
@@ -78,8 +81,20 @@ describe Erubi::Engine do
end
end
+ it "should handle no tags with frozen source" do
+ check_output(<<END1.freeze, <<END2, <<END3){}
+a
+END1
+_buf = ::String.new; _buf << 'a
+';
+_buf.to_s
+END2
+a
+END3
+ end
+
it "should handle no options" do
- list = ['&\'<>"2']
+ list = list = ['&\'<>"2']
check_output(<<END1, <<END2, <<END3){}
<table>
<tbody>
@@ -121,43 +136,89 @@ END2
END3
end
+ it "should escape all backslashes and apostrophes in text" do
+ list = list = ['&\'<>"2']
+ check_output(<<END1.chomp, <<END2, <<END3){}
+<table>
+ <tbody>' ' \\ \\
+ <% i = 0
+ list.each_with_index do |item, i| %>
+ <tr>
+ <td><%= i+1 -%>
+</td>
+ <td><%== item %></td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
+<%== i+1 %>
+<%
+%>
+END1
+_buf = ::String.new; _buf << '<table>
+ <tbody>\\' \\' \\\\ \\\\
+'; i = 0
+ list.each_with_index do |item, i|
+ _buf << ' <tr>
+ <td>'; _buf << ( i+1 ).to_s; _buf << '</td>
+ <td>'; _buf << ::Erubi.h(( item )); _buf << '</td>
+ </tr>
+'; end
+ _buf << ' </tbody>
+</table>
+'; _buf << ::Erubi.h(( i+1 )); _buf << '
+';
+_buf.to_s
+END2
+<table>
+ <tbody>' ' \\ \\
+ <tr>
+ <td>1</td>
+ <td>&'<>"2</td>
+ </tr>
+ </tbody>
+</table>
+1
+END3
+ end
+
it "should strip only whitespace for <%, <%- and <%# tags" do
check_output(<<END1, <<END2, <<END3){}
- <% 1 %>
+ <% a = 1 %>
a
- <%- 2 %>
+ <%- a = 2 %>
b
- <%# 3 %>
+ <%# a = 3 %>
c
- /<% 1 %>
+ /<% a = 1 %>
a
-/ <%- 2 %>
+/ <%- a = 2 %>
b
-//<%# 3 %>
+//<%# a = 3 %>
c
- <% 1 %> /
+ <% a = 1 %> /
a
- <%- 2 %>/
+ <%- a = 2 %>/
b
- <%# 3 %>//
+ <%# a = 3 %>//
c
END1
-_buf = ::String.new; 1
+_buf = ::String.new; a = 1
_buf << 'a
-'; 2
+'; a = 2
_buf << 'b
';
_buf << 'c
- /'; 1 ; _buf << '
+ /'; a = 1 ; _buf << '
'; _buf << 'a
-/ '; 2 ; _buf << '
+/ '; a = 2 ; _buf << '
'; _buf << 'b
//';
_buf << '
'; _buf << 'c
-'; _buf << ' '; 1 ; _buf << ' /
+'; _buf << ' '; a = 1 ; _buf << ' /
a
-'; _buf << ' '; 2 ; _buf << '/
+'; _buf << ' '; a = 2 ; _buf << '/
b
'; _buf << ' ';; _buf << '//
c
@@ -183,7 +244,7 @@ END3
end
it "should handle ensure option" do
- list = ['&\'<>"2']
+ list = list = ['&\'<>"2']
@options[:ensure] = true
@options[:bufvar] = '@a'
@a = 'bar'
@@ -201,7 +262,7 @@ END3
</table>
<%== i+1 %>
END1
-begin; __original_outvar = @a if defined?(@a); @a = ::String.new; @a << '<table>
+begin; __original_outvar = @a#{' if defined?(@a)' if RUBY_VERSION < '3'}; @a = ::String.new; @a << '<table>
<tbody>
'; i = 0
list.each_with_index do |item, i|
@@ -232,6 +293,195 @@ END3
@a.must_equal 'bar'
end
+ it "should handle chain_appends option" do
+ @options[:chain_appends] = true
+ list = list = ['&\'<>"2']
+ check_output(<<END1, <<END2, <<END3){}
+<table>
+ <tbody>
+ <% i = 0
+ list.each_with_index do |item, i| %>
+ <tr>
+ <td><%= i+1 %></td>
+ <td><%== item %></td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
+<%== i+1 %>
+END1
+_buf = ::String.new;; _buf << '<table>
+ <tbody>
+'; i = 0
+ list.each_with_index do |item, i|
+; _buf << ' <tr>
+ <td>' << ( i+1 ).to_s << '</td>
+ <td>' << ::Erubi.h(( item )) << '</td>
+ </tr>
+'; end
+; _buf << ' </tbody>
+</table>
+' << ::Erubi.h(( i+1 )) << '
+'
+; _buf.to_s
+END2
+<table>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>&'<>"2</td>
+ </tr>
+ </tbody>
+</table>
+1
+END3
+ end
+
+ it "should handle :freeze_template_literals => true option" do
+ @options[:freeze_template_literals] = true
+ list = list = ['&\'<>"2']
+ @strip_freeze = false
+ check_output(<<END1, <<END2, <<END3){}
+<table>
+ <tbody>
+ <% i = 0
+ list.each_with_index do |item, i| %>
+ <tr>
+ <td><%= i+1 %></td>
+ <td><%== item %></td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
+<%== i+1 %>
+END1
+_buf = ::String.new; _buf << '<table>
+ <tbody>
+'.freeze; i = 0
+ list.each_with_index do |item, i|
+ _buf << ' <tr>
+ <td>'.freeze; _buf << ( i+1 ).to_s; _buf << '</td>
+ <td>'.freeze; _buf << ::Erubi.h(( item )); _buf << '</td>
+ </tr>
+'.freeze; end
+ _buf << ' </tbody>
+</table>
+'.freeze; _buf << ::Erubi.h(( i+1 )); _buf << '
+'.freeze;
+_buf.to_s
+END2
+<table>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>&'<>"2</td>
+ </tr>
+ </tbody>
+</table>
+1
+END3
+ end
+
+ it "should handle :freeze_template_literals => false option" do
+ @options[:freeze_template_literals] = false
+ list = list = ['&\'<>"2']
+ @strip_freeze = false
+ check_output(<<END1, <<END2, <<END3){}
+<table>
+ <tbody>
+ <% i = 0
+ list.each_with_index do |item, i| %>
+ <tr>
+ <td><%= i+1 %></td>
+ <td><%== item %></td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
+<%== i+1 %>
+END1
+_buf = ::String.new; _buf << '<table>
+ <tbody>
+'; i = 0
+ list.each_with_index do |item, i|
+ _buf << ' <tr>
+ <td>'; _buf << ( i+1 ).to_s; _buf << '</td>
+ <td>'; _buf << ::Erubi.h(( item )); _buf << '</td>
+ </tr>
+'; end
+ _buf << ' </tbody>
+</table>
+'; _buf << ::Erubi.h(( i+1 )); _buf << '
+';
+_buf.to_s
+END2
+<table>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>&'<>"2</td>
+ </tr>
+ </tbody>
+</table>
+1
+END3
+ end
+
+ it "should handle ensure option with no bufvar" do
+ list = list = ['&\'<>"2']
+ @options[:ensure] = true
+ check_output(<<END1, <<END2, <<END3){}
+<table>
+ <tbody>
+ <% i = 0
+ list.each_with_index do |item, i| %>
+ <tr>
+ <td><%= i+1 %></td>
+ <td><%== item %></td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
+<%== i+1 %>
+END1
+begin; __original_outvar = _buf if defined?(_buf); _buf = ::String.new; _buf << '<table>
+ <tbody>
+'; i = 0
+ list.each_with_index do |item, i|
+ _buf << ' <tr>
+ <td>'; _buf << ( i+1 ).to_s; _buf << '</td>
+ <td>'; _buf << ::Erubi.h(( item )); _buf << '</td>
+ </tr>
+'; end
+ _buf << ' </tbody>
+</table>
+'; _buf << ::Erubi.h(( i+1 )); _buf << '
+';
+_buf.to_s
+; ensure
+ _buf = __original_outvar
+end
+END2
+<table>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>&'<>"2</td>
+ </tr>
+ </tbody>
+</table>
+1
+END3
+ end
+
+ it "should handle trailing rspace with - modifier in <%|= and <%|" do
+ eval(::Erubi::CaptureEndEngine.new("<%|= '&' -%>\n<%| -%>\n").src).must_equal '&'
+ end
+
+ it "should handle lspace in <%|=" do
+ eval(::Erubi::CaptureEndEngine.new("<%|= %><%| %><%|= %><%| %>").src).must_equal ''
+ end
+
it "should have <%|= with CaptureEndEngine not escape by default" do
eval(::Erubi::CaptureEndEngine.new('<%|= "&" %><%| %>').src).must_equal '&'
eval(::Erubi::CaptureEndEngine.new('<%|= "&" %><%| %>', :escape=>false).src).must_equal '&'
@@ -265,7 +515,7 @@ END3
</tbody>
</table>
END1
-#{'__erubi = ::Erubi;' unless escape}@a = ::String.new; @a << '<table>
+#{'__erubi = ::Erubi; ' unless escape}@a = ::String.new; @a << '<table>
<tbody>
'; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << (( bar do @a << '
'; @a << ' <b>'; @a << #{!escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << '</b>
@@ -301,7 +551,7 @@ END3
</tbody>
</table>
END1
-#{'__erubi = ::Erubi;' if escape}@a = ::String.new; @a << '<table>
+#{'__erubi = ::Erubi; ' if escape}@a = ::String.new; @a << '<table>
<tbody>
'; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << #{escape ? '__erubi' : '::Erubi'}.h(( bar do @a << '
'; @a << ' <b>'; @a << #{escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << '</b>
@@ -335,7 +585,7 @@ END3
</tbody>
</table>
END1
-#{'__erubi = ::Erubi;' if escape}@a = ::String.new; @a << '<table>
+#{'__erubi = ::Erubi; ' if escape}@a = ::String.new; @a << '<table>
<tbody>
'; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << #{escape ? '__erubi' : '::Erubi'}.h(( quux do |i| @a << '
'; @a << ' <b>'; @a << #{escape ? '__erubi' : '::Erubi'}.h(( "\#{i}&" )); @a << '</b>
@@ -374,7 +624,7 @@ END3
</tbody>
</table>
END1
-#{'__erubi = ::Erubi;' if escape}@a = ::String.new; @a << '<table>
+#{'__erubi = ::Erubi; ' if escape}@a = ::String.new; @a << '<table>
<tbody>
'; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << #{escape ? '__erubi' : '::Erubi'}.h(( bar do @a << '
'; @a << ' <b>'; @a << #{escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << '</b>
@@ -402,7 +652,7 @@ END3
@options[var] = "@_out_buf"
@options[:freeze] = true
@items = [2]
- i = 0
+ i = i = 0
check_output(<<END1, <<END2, <<END3){}
<table>
<% for item in @items %>
@@ -437,7 +687,7 @@ END3
it "should handle <%% and <%# syntax" do
@items = [2]
- i = 0
+ i = i = 0
check_output(<<END1, <<END2, <<END3){}
<table>
<%% for item in @items %>
@@ -470,10 +720,44 @@ END2
END3
end
+ it "should handle <%% with a different literal prefix/postfix" do
+ @options[:literal_prefix] = "{%"
+ @options[:literal_postfix] = "%}"
+ @items = [2]
+ i = i = 0
+ check_output(<<END1, <<END2, <<END3){}
+<table>
+ <%% for item in @items %>
+ <tr>
+ </tr>
+ <%% end %>
+ <%%= "literal" %>
+</table>
+END1
+_buf = ::String.new; _buf << '<table>
+'; _buf << ' {% for item in @items %}
+'; _buf << ' <tr>
+ </tr>
+'; _buf << ' {% end %}
+'; _buf << ' {%= "literal" %}
+'; _buf << '</table>
+';
+_buf.to_s
+END2
+<table>
+ {% for item in @items %}
+ <tr>
+ </tr>
+ {% end %}
+ {%= "literal" %}
+</table>
+END3
+ end
+
it "should handle :trim => false option" do
@options[:trim] = false
@items = [2]
- i = 0
+ i = i = 0
check_output(<<END1, <<END2, <<END3){}
<table>
<% for item in @items %>
@@ -484,8 +768,8 @@ END3
<td><%== item %></td>
</tr>
<% end %><%#%>
- <% i %>a
- <% i %>
+ <% i = 1 %>a
+ <% i = 1 %>
</table>
END1
_buf = ::String.new; _buf << '<table>
@@ -498,8 +782,8 @@ _buf = ::String.new; _buf << '<table>
</tr>
'; _buf << ' '; end ;
_buf << '
-'; _buf << ' '; i ; _buf << 'a
-'; _buf << ' '; i ; _buf << '
+'; _buf << ' '; i = 1 ; _buf << 'a
+'; _buf << ' '; i = 1 ; _buf << '
'; _buf << '</table>
';
_buf.to_s
@@ -521,8 +805,8 @@ END3
it "should handle :#{opt} and :escapefunc options" do
@options[opt] = true
@options[:escapefunc] = 'h.call'
- h = proc{|s| s.to_s*2}
- list = ['2']
+ h = h = proc{|s| s.to_s*2}
+ list = list = ['2']
check_output(<<END1, <<END2, <<END3){}
<table>
<tbody>
@@ -567,7 +851,7 @@ END3
it "should handle :escape option without :escapefunc option" do
@options[:escape] = true
- list = ['&\'<>"2']
+ list = list = ['&\'<>"2']
check_output(<<END1, <<END2, <<END3){}
<table>
<tbody>
@@ -581,7 +865,7 @@ END3
</tbody>
</table>
END1
-__erubi = ::Erubi;_buf = ::String.new; _buf << '<table>
+__erubi = ::Erubi; _buf = ::String.new; _buf << '<table>
<tbody>
'; i = 0
list.each_with_index do |item, i|
@@ -609,7 +893,7 @@ END3
it "should handle :preamble and :postamble options" do
@options[:preamble] = '_buf = String.new("1");'
@options[:postamble] = "_buf[0...18]\n"
- list = ['2']
+ list = list = ['2']
check_output(<<END1, <<END2, <<END3){}
<table>
<tbody>
@@ -707,11 +991,11 @@ END3
check_output(<<END1, <<END2, <<END3) {}
<%|= bar do |item| %>
Let's eat <%= item %>!
-<% nil %><%| end %>
+<% i = i = nil %><%| end %>
END1
@a = ::String.new;begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << (( bar do |item| @a << '
'; @a << 'Let\\'s eat '; @a << ( item ).to_s; @a << '!
-'; nil ; @a; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << '
+'; i = i = nil ; @a; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << '
';
@a.to_s
END2
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/share/rubygems-integration/all/gems/erubi-1.12.0/lib/erubi.rb -rw-r--r-- root/root /usr/share/rubygems-integration/all/gems/erubi-1.12.0/lib/erubi/capture_end.rb -rw-r--r-- root/root /usr/share/rubygems-integration/all/specifications/erubi-1.12.0.gemspec
Files in first set of .debs but not in second
-rw-r--r-- root/root /usr/share/rubygems-integration/all/gems/erubi-1.9.0/lib/erubi.rb -rw-r--r-- root/root /usr/share/rubygems-integration/all/gems/erubi-1.9.0/lib/erubi/capture_end.rb -rw-r--r-- root/root /usr/share/rubygems-integration/all/specifications/erubi-1.9.0.gemspec
No differences were encountered in the control files