erubi 1.6.1 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d7f083e903297ea1af2aa0f9fffb787a7fcd8cdd
4
- data.tar.gz: 5f2ee10913c29dde0cd731781e8c696872f71733
2
+ SHA256:
3
+ metadata.gz: 3f504eb8d1ae0f89e098de29336a69d0251cd8659a1b6fe1ab86c7f23ed44d67
4
+ data.tar.gz: e54396a6eb2cf0c193089c9b2c3d5fa0f92daf5cf4f462fe09d0aea6dca49419
5
5
  SHA512:
6
- metadata.gz: 78b7ecae18bdc2e524cf2f778ef3552419a0df2b61e63f1f027ee1083375e9395306c3444bcc5ceb69ba7fe2a9e0da7aaf1535267b3db59d6ff8c634cf42a955
7
- data.tar.gz: 58f2d7eb1f3930061415f010815d9e097fa3939d52491d60038ccf58283f4cf04d77ddabf808582cdae1a5fd6d9e3b0d9c8918c27cf2e02dc3a73c9ba367a05a
6
+ metadata.gz: 0a789b8a528548760ac4898867588477e03c3f1410bd930538c849e167c68a346202f8c168a2a71107485c961bb41a91d549f687a4d24ebf3614821bc15b4e73
7
+ data.tar.gz: 14e74f5fb8c452c0cbc15afbe51b8a86be572d8d585416f09f07d9227e06ad8d8c14d8681424dc53f9b7eaba1a1297ed72e8e7dc0c36afe6e2da1926bec742d0
data/CHANGELOG CHANGED
@@ -1,3 +1,27 @@
1
+ === 1.10.0 (2020-11-13)
2
+
3
+ * Improve template parsing, mostly by reducing allocations (jeremyevans)
4
+
5
+ * Do not ship tests in the gem, reducing gem size about 20% (jeremyevans)
6
+
7
+ * Support :literal_prefix and :literal_postfix options for how to output literal tags (e.g. <%% code %>) (jaredcwhite) (#26, #27)
8
+
9
+ === 1.9.0 (2019-09-25)
10
+
11
+ * Change default :bufvar from 'String.new' to '::String.new' to work with BasicObject (jeremyevans)
12
+
13
+ === 1.8.0 (2018-12-18)
14
+
15
+ * Support :yield_returns_buffer option in capture_end for always returning the (potentially modified) buffer in <%|= tags (evanleck) (#15)
16
+
17
+ === 1.7.1 (2018-03-05)
18
+
19
+ * Make whitespace handling for <%# %> tags more compatible with Erubis (jeremyevans) (#14)
20
+
21
+ === 1.7.0 (2017-10-09)
22
+
23
+ * Fix escaping in erubi/capture_end, the setting was previously inverted (jeremyevans) (#10)
24
+
1
25
  === 1.6.1 (2017-06-27)
2
26
 
3
27
  * Fix usage on newer versions of JRuby 9.1 (jeremyevans)
@@ -1,5 +1,5 @@
1
1
  copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
2
- copyright(c) 2016-2017 Jeremy Evans
2
+ copyright(c) 2016-2020 Jeremy Evans
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining
5
5
  a copy of this software and associated documentation files (the
@@ -5,15 +5,14 @@ the same basic algorithm, with the following differences:
5
5
 
6
6
  * Handles postfix conditionals when using escaping (e.g. <tt><%= foo if bar %></tt>)
7
7
  * Supports frozen_string_literal: true in templates via :freeze option
8
- * Works with ruby's --enable-frozen-string-literal option
8
+ * Works with ruby's <tt>--enable-frozen-string-literal</tt> option
9
9
  * Automatically freezes strings for template text when ruby optimizes it (on ruby 2.1+)
10
- * Escapes ' (apostrophe) when escaping for better XSS protection
10
+ * Escapes <tt>'</tt> (apostrophe) when escaping for better XSS protection
11
11
  * Has 6x faster escaping on ruby 2.3+ by using cgi/escape
12
12
  * Has 86% smaller memory footprint
13
13
  * Does no monkey patching (Erubis adds a method to Kernel)
14
14
  * Uses an immutable design (all options passed to the constructor, which returns a frozen object)
15
15
  * Has simpler internals (1 file, <150 lines of code)
16
- * Has an open development model (Erubis doesn't have a public source control repository or bug tracker)
17
16
  * Is not dead (Erubis hasn't been updated since 2011)
18
17
 
19
18
  It is not designed with Erubis API compatibility in mind, though most Erubis
@@ -37,15 +36,15 @@ file:
37
36
  require 'erubi'
38
37
  eval(Erubi::Engine.new(File.read('filename.erb')).src)
39
38
 
40
- Most users are will probably use Erubi via Rails or Tilt. Erubi is the default
41
- erb template handler in Tilt 2.0.6+ and will be the default template handler in
42
- Rails 5.1+.
39
+ Most users will probably use Erubi via Rails or Tilt. Erubi is the default
40
+ erb template handler in Tilt 2.0.6+ and Rails 5.1+.
43
41
 
44
42
  == Capturing
45
43
 
46
44
  Erubi does not support capturing block output into the template by default.
47
45
  However, it comes with an +erubi/capture_end+ file that supports capturing
48
- via <tt><%|=</tt>, <tt><%|==</tt>, <tt><%|</tt> tags:
46
+ via <tt><%|=</tt> and <tt><%|==</tt> tags which are closed with a
47
+ <tt><%|</tt> tag:
49
48
 
50
49
  <%|= form do %>
51
50
  <input>
@@ -64,6 +63,38 @@ To use the capture_end support with tilt:
64
63
  require 'erubi/capture_end'
65
64
  Tilt.new("filename.erb", :engine_class=>Erubi::CaptureEndEngine).render
66
65
 
66
+ When using the capture_end support, any methods (such as +form+ in the example
67
+ above) should return the (potentially modified) buffer. Since the buffer
68
+ variable is a local variable and not an instance variable by default, you'll
69
+ probably want to set the +:bufvar+ variable when using the capture_end
70
+ support to an instance variable, and have any methods used access that
71
+ instance variable. Example:
72
+
73
+ def form
74
+ @_buf << "<form>"
75
+ yield
76
+ @_buf << "</form>"
77
+ @_buf
78
+ end
79
+
80
+ puts eval(Erubi::CaptureEndEngine.new(<<-END, :bufvar=>:@_buf).src)
81
+ before
82
+ <%|= form do %>
83
+ inside
84
+ <%| end %>
85
+ after
86
+ END
87
+
88
+ # Output:
89
+ # before
90
+ # <form>
91
+ # inside
92
+ # </form>
93
+ # after
94
+
95
+ Alternatively, passing the option <tt>:yield_returns_buffer => true</tt> will return the
96
+ buffer captured by the block instead of the last expression in the block.
97
+
67
98
  = Reporting Bugs
68
99
 
69
100
  The bug tracker is located at https://github.com/jeremyevans/erubi/issues
data/Rakefile CHANGED
@@ -60,9 +60,7 @@ end
60
60
 
61
61
  desc "Run specs with -w, some warnings filtered"
62
62
  task "spec_w" do
63
- ENV['RUBYOPT'] ? (ENV['RUBYOPT'] += " -w") : (ENV['RUBYOPT'] = '-w')
64
- rake = ENV['RAKE'] || "#{FileUtils::RUBY} -S rake"
65
- sh %{#{rake} 2>&1 | egrep -v \": warning: instance variable @.* not initialized|: warning: method redefined; discarding old|: warning: previous definition of|: warning: statement not reached"}
63
+ sh "#{FileUtils::RUBY} test/test_w.rb"
66
64
  end
67
65
 
68
66
  ### Other
@@ -1,34 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Erubi
4
- VERSION = '1.6.1'
4
+ VERSION = '1.10.0'
5
5
  RANGE_ALL = 0..-1
6
6
 
7
+ # :nocov:
7
8
  if RUBY_VERSION >= '1.9'
8
9
  RANGE_FIRST = 0
9
10
  RANGE_LAST = -1
10
- TEXT_END = RUBY_VERSION >= '2.1' ? "'.freeze;" : "';"
11
11
  else
12
- # :nocov:
13
12
  RANGE_FIRST = 0..0
14
13
  RANGE_LAST = -1..-1
15
- TEXT_END = "';"
16
14
  end
17
15
 
16
+ TEXT_END = RUBY_VERSION >= '2.1' ? "'.freeze;" : "';"
17
+ MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
18
+ # :nocov:
19
+
18
20
  begin
19
21
  require 'cgi/escape'
22
+ # :nocov:
20
23
  unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
21
24
  CGI = Object.new
22
25
  CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
23
26
  end
27
+ # :nocov:
28
+ # Escape characters with their HTML/XML equivalents.
24
29
  def self.h(value)
25
30
  CGI.escapeHTML(value.to_s)
26
31
  end
27
32
  rescue LoadError
33
+ # :nocov:
28
34
  ESCAPE_TABLE = {'&' => '&amp;'.freeze, '<' => '&lt;'.freeze, '>' => '&gt;'.freeze, '"' => '&quot;'.freeze, "'" => '&#39;'.freeze}.freeze
29
35
  if RUBY_VERSION >= '1.9'
30
- # Escape the following characters with their HTML/XML
31
- # equivalents.
32
36
  def self.h(value)
33
37
  value.to_s.gsub(/[&<>"']/, ESCAPE_TABLE)
34
38
  end
@@ -37,6 +41,7 @@ module Erubi
37
41
  value.to_s.gsub(/[&<>"']/){|s| ESCAPE_TABLE[s]}
38
42
  end
39
43
  end
44
+ # :nocov:
40
45
  end
41
46
 
42
47
  class Engine
@@ -50,27 +55,31 @@ module Erubi
50
55
  attr_reader :bufvar
51
56
 
52
57
  # Initialize a new Erubi::Engine. Options:
53
- # :bufval :: The value to use for the buffer variable, as a string.
54
- # :bufvar :: The variable name to use for the buffer variable, as a string.
55
- # :ensure :: Wrap the template in a begin/ensure block restoring the previous value of bufvar.
56
- # :escapefunc :: The function to use for escaping, as a string (default: ::Erubi.h).
57
- # :escape :: Whether to make <%= escape by default, and <%== not escape by default.
58
- # :escape_html :: Same as :escape, with lower priority.
59
- # :filename :: The filename for the template.
60
- # :freeze :: Whether to enable frozen string literals in the resulting source code.
61
- # :outvar :: Same as bufvar, with lower priority.
62
- # :postamble :: The postamble for the template, by default returns the resulting source code.
63
- # :preamble :: The preamble for the template, by default initializes up the buffer variable.
64
- # :regexp :: The regexp to use for scanning.
65
- # :src :: The initial value to use for the source code
66
- # :trim :: Whether to trim leading and trailing whitespace, true by default.
58
+ # +:bufval+ :: The value to use for the buffer variable, as a string (default <tt>'::String.new'</tt>).
59
+ # +:bufvar+ :: The variable name to use for the buffer variable, as a string.
60
+ # +:ensure+ :: Wrap the template in a begin/ensure block restoring the previous value of bufvar.
61
+ # +:escapefunc+ :: The function to use for escaping, as a string (default: <tt>'::Erubi.h'</tt>).
62
+ # +:escape+ :: Whether to make <tt><%=</tt> escape by default, and <tt><%==</tt> not escape by default.
63
+ # +:escape_html+ :: Same as +:escape+, with lower priority.
64
+ # +:filename+ :: The filename for the template.
65
+ # +:freeze+ :: Whether to enable frozen string literals in the resulting source code.
66
+ # +:literal_prefix+ :: The prefix to output when using escaped tag delimiters (default <tt>'<%'</tt>).
67
+ # +:literal_postfix+ :: The postfix to output when using escaped tag delimiters (default <tt>'%>'</tt>).
68
+ # +:outvar+ :: Same as +:bufvar+, with lower priority.
69
+ # +:postamble+ :: The postamble for the template, by default returns the resulting source code.
70
+ # +:preamble+ :: The preamble for the template, by default initializes the buffer variable.
71
+ # +:regexp+ :: The regexp to use for scanning.
72
+ # +:src+ :: The initial value to use for the source code, an empty string by default.
73
+ # +:trim+ :: Whether to trim leading and trailing whitespace, true by default.
67
74
  def initialize(input, properties={})
68
75
  @escape = escape = properties.fetch(:escape){properties.fetch(:escape_html, false)}
69
76
  trim = properties[:trim] != false
70
77
  @filename = properties[:filename]
71
78
  @bufvar = bufvar = properties[:bufvar] || properties[:outvar] || "_buf"
72
- bufval = properties[:bufval] || 'String.new'
79
+ bufval = properties[:bufval] || '::String.new'
73
80
  regexp = properties[:regexp] || /<%(={1,2}|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m
81
+ literal_prefix = properties[:literal_prefix] || '<%'
82
+ literal_postfix = properties[:literal_postfix] || '%>'
74
83
  preamble = properties[:preamble] || "#{bufvar} = #{bufval};"
75
84
  postamble = properties[:postamble] || "#{bufvar}.to_s\n"
76
85
 
@@ -110,46 +119,45 @@ module Erubi
110
119
  if rindex
111
120
  range = rindex+1..-1
112
121
  s = text[range]
113
- if s =~ /\A[ \t]*\z/
122
+ if /\A[ \t]*\z/.send(MATCH_METHOD, s)
114
123
  lspace = s
115
124
  text[range] = ''
116
125
  end
117
126
  else
118
- if is_bol && text =~ /\A[ \t]*\z/
119
- lspace = text.dup
120
- text[RANGE_ALL] = ''
127
+ if is_bol && /\A[ \t]*\z/.send(MATCH_METHOD, text)
128
+ lspace = text
129
+ text = ''
121
130
  end
122
131
  end
123
132
  end
124
133
  end
125
134
 
126
- is_bol = rspace ? true : false
127
- add_text(text) if text && !text.empty?
135
+ is_bol = rspace
136
+ add_text(text)
128
137
  case ch
129
138
  when '='
130
139
  rspace = nil if tailch && !tailch.empty?
131
- add_text(lspace) if lspace
132
140
  add_expression(indicator, code)
133
141
  add_text(rspace) if rspace
134
- when '#'
135
- n = code.count("\n") + (rspace ? 1 : 0)
136
- if trim
137
- add_code("\n" * n)
142
+ when nil, '-'
143
+ if trim && lspace && rspace
144
+ add_code("#{lspace}#{code}#{rspace}")
138
145
  else
139
146
  add_text(lspace) if lspace
140
- add_code("\n" * n)
147
+ add_code(code)
141
148
  add_text(rspace) if rspace
142
149
  end
143
- when '%'
144
- add_text("#{lspace}#{prefix||='<%'}#{code}#{tailch}#{postfix||='%>'}#{rspace}")
145
- when nil, '-'
150
+ when '#'
151
+ n = code.count("\n") + (rspace ? 1 : 0)
146
152
  if trim && lspace && rspace
147
- add_code("#{lspace}#{code}#{rspace}")
153
+ add_code("\n" * n)
148
154
  else
149
155
  add_text(lspace) if lspace
150
- add_code(code)
156
+ add_code("\n" * n)
151
157
  add_text(rspace) if rspace
152
158
  end
159
+ when '%'
160
+ add_text("#{lspace}#{literal_prefix}#{code}#{tailch}#{literal_postfix}#{rspace}")
153
161
  else
154
162
  handle(indicator, code, tailch, rspace, lspace)
155
163
  end
@@ -159,16 +167,24 @@ module Erubi
159
167
 
160
168
  src << "\n" unless src[RANGE_LAST] == "\n"
161
169
  add_postamble(postamble)
162
- src << "; ensure\n #{bufvar} = __original_outvar\nend\n" if properties[:ensure]
170
+ src << "; ensure\n " << bufvar << " = __original_outvar\nend\n" if properties[:ensure]
163
171
  src.freeze
164
172
  freeze
165
173
  end
166
174
 
167
175
  private
168
176
 
169
- # Add raw text to the template
177
+ # Add raw text to the template. Modifies argument if argument is mutable as a memory optimization.
178
+ # Must be called with a string, cannot be called with nil (Rails's subclass depends on it).
170
179
  def add_text(text)
171
- @src << " #{@bufvar} << '" << text.gsub(/['\\]/, '\\\\\&') << TEXT_END unless text.empty?
180
+ return if text.empty?
181
+
182
+ if text.frozen?
183
+ text = text.gsub(/['\\]/, '\\\\\&')
184
+ else
185
+ text.gsub!(/['\\]/, '\\\\\&')
186
+ end
187
+ @src << " " << @bufvar << " << '" << text << TEXT_END
172
188
  end
173
189
 
174
190
  # Add ruby code to the template
@@ -189,12 +205,12 @@ module Erubi
189
205
 
190
206
  # Add the result of Ruby expression to the template
191
207
  def add_expression_result(code)
192
- @src << " #{@bufvar} << (" << code << ').to_s;'
208
+ @src << ' ' << @bufvar << ' << (' << code << ').to_s;'
193
209
  end
194
210
 
195
211
  # Add the escaped result of Ruby expression to the template
196
212
  def add_expression_result_escaped(code)
197
- @src << " #{@bufvar} << #{@escapefunc}((" << code << '));'
213
+ @src << ' ' << @bufvar << ' << ' << @escapefunc << '((' << code << '));'
198
214
  end
199
215
 
200
216
  # Add the given postamble to the src. Can be overridden in subclasses
@@ -3,18 +3,25 @@
3
3
  require 'erubi'
4
4
 
5
5
  module Erubi
6
- # An engine class that supports capturing blocks via the <%|= and <%|== tags,
7
- # explicitly ending the captures using <%| end %> blocks.
6
+ # An engine class that supports capturing blocks via the <tt><%|=</tt> and <tt><%|==</tt> tags,
7
+ # explicitly ending the captures using <tt><%|</tt> end <tt>%></tt> blocks.
8
8
  class CaptureEndEngine < Engine
9
9
  # Initializes the engine. Accepts the same arguments as ::Erubi::Engine, and these
10
10
  # additional options:
11
- # :escape_capture :: Whether to make <%|= escape by default, and <%|== not escape by default,
11
+ # :escape_capture :: Whether to make <tt><%|=</tt> escape by default, and <tt><%|==</tt> not escape by default,
12
12
  # defaults to the same value as :escape.
13
+ # :yield_returns_buffer :: Whether to have <tt><%|</tt> tags insert the buffer as an expression, so that
14
+ # <tt><%| end %></tt> tags will have the buffer be the last expression inside
15
+ # the block, and therefore have the buffer be returned by the yield
16
+ # expression. Normally the buffer will be returned anyway, but there
17
+ # are cases where the last expression will not be the buffer,
18
+ # and therefore a different object will be returned.
13
19
  def initialize(input, properties={})
14
20
  properties = Hash[properties]
15
21
  escape = properties.fetch(:escape){properties.fetch(:escape_html, false)}
16
22
  @escape_capture = properties.fetch(:escape_capture, escape)
17
- @bufval = properties[:bufval] ||= 'String.new'
23
+ @yield_returns_buffer = properties.fetch(:yield_returns_buffer, false)
24
+ @bufval = properties[:bufval] ||= '::String.new'
18
25
  @bufstack = '__erubi_stack'
19
26
  properties[:regexp] ||= /<%(\|?={1,2}|-|\#|%|\|)?(.*?)([-=])?%>([ \t]*\r?\n)?/m
20
27
  super
@@ -28,13 +35,14 @@ module Erubi
28
35
  when '|=', '|=='
29
36
  rspace = nil if tailch && !tailch.empty?
30
37
  add_text(lspace) if lspace
31
- escape_capture = ((indicator == '|=') ^ @escape_capture)
38
+ escape_capture = !((indicator == '|=') ^ @escape_capture)
32
39
  src << "begin; (#{@bufstack} ||= []) << #{@bufvar}; #{@bufvar} = #{@bufval}; #{@bufstack}.last << #{@escapefunc if escape_capture}((" << code
33
40
  add_text(rspace) if rspace
34
41
  when '|'
35
42
  rspace = nil if tailch && !tailch.empty?
36
43
  add_text(lspace) if lspace
37
- src << code << ")).to_s; ensure; #{@bufvar} = #{@bufstack}.pop; end;"
44
+ result = @yield_returns_buffer ? " #{@bufvar}; " : ""
45
+ src << result << code << ")).to_s; ensure; #{@bufvar} = #{@bufstack}.pop; end;"
38
46
  add_text(rspace) if rspace
39
47
  else
40
48
  super
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erubi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-06-27 00:00:00.000000000 Z
12
+ date: 2020-11-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: minitest-global_expectations
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
28
42
  description: Erubi is a ERB template engine for ruby. It is a simplified fork of Erubis
29
43
  email: code@jeremyevans.net
30
44
  executables: []
@@ -40,11 +54,13 @@ files:
40
54
  - Rakefile
41
55
  - lib/erubi.rb
42
56
  - lib/erubi/capture_end.rb
43
- - test/test.rb
44
57
  homepage: https://github.com/jeremyevans/erubi
45
58
  licenses:
46
59
  - MIT
47
- metadata: {}
60
+ metadata:
61
+ bug_tracker_uri: https://github.com/jeremyevans/erubi/issues
62
+ changelog_uri: https://github.com/jeremyevans/erubi/blob/master/CHANGELOG
63
+ source_code_uri: https://github.com/jeremyevans/erubi
48
64
  post_install_message:
49
65
  rdoc_options:
50
66
  - "--quiet"
@@ -67,8 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
83
  - !ruby/object:Gem::Version
68
84
  version: '0'
69
85
  requirements: []
70
- rubyforge_project:
71
- rubygems_version: 2.6.11
86
+ rubygems_version: 3.1.4
72
87
  signing_key:
73
88
  specification_version: 4
74
89
  summary: Small ERB Implementation
@@ -1,541 +0,0 @@
1
- require 'rubygems'
2
-
3
- unless defined?(TESTDIR)
4
- TESTDIR = File.dirname(__FILE__)
5
- LIBDIR = TESTDIR == '.' ? '../lib' : File.dirname(TESTDIR) + '/lib'
6
- $: << TESTDIR
7
- $: << LIBDIR
8
- end
9
-
10
- if ENV['COVERAGE']
11
- require 'coverage'
12
- require 'simplecov'
13
-
14
- ENV.delete('COVERAGE')
15
- SimpleCov.instance_eval do
16
- start do
17
- add_filter "/test/"
18
- add_group('Missing'){|src| src.covered_percent < 100}
19
- add_group('Covered'){|src| src.covered_percent == 100}
20
- end
21
- end
22
- end
23
-
24
- require 'erubi'
25
- require 'erubi/capture_end'
26
- require 'minitest/spec'
27
- require 'minitest/autorun'
28
-
29
- describe Erubi::Engine do
30
- before do
31
- @options = {}
32
- end
33
-
34
- def check_output(input, src, result, &block)
35
- t = (@options[:engine] || Erubi::Engine).new(input, @options)
36
- tsrc = t.src
37
- eval(tsrc, block.binding).must_equal result
38
- tsrc = tsrc.gsub("'.freeze;", "';") if RUBY_VERSION >= '2.1'
39
- tsrc.must_equal src
40
- end
41
-
42
- def setup_foo
43
- @foo = Object.new
44
- @foo.instance_variable_set(:@t, self)
45
- def self.a; @a; end
46
- def @foo.bar
47
- @t.a << "a"
48
- yield
49
- @t.a << 'b'
50
- @t.a.buffer.upcase!
51
- end
52
- end
53
-
54
- def setup_bar
55
- def self.bar
56
- @a << "a"
57
- yield
58
- @a << 'b'
59
- @a.upcase
60
- end
61
- def self.baz
62
- @a << "c"
63
- yield
64
- @a << 'd'
65
- @a * 2
66
- end
67
- end
68
-
69
- it "should handle no options" do
70
- list = ['&\'<>"2']
71
- check_output(<<END1, <<END2, <<END3){}
72
- <table>
73
- <tbody>
74
- <% i = 0
75
- list.each_with_index do |item, i| %>
76
- <tr>
77
- <td><%= i+1 %></td>
78
- <td><%== item %></td>
79
- </tr>
80
- <% end %>
81
- </tbody>
82
- </table>
83
- <%== i+1 %>
84
- END1
85
- _buf = String.new; _buf << '<table>
86
- <tbody>
87
- '; i = 0
88
- list.each_with_index do |item, i|
89
- _buf << ' <tr>
90
- <td>'; _buf << ( i+1 ).to_s; _buf << '</td>
91
- <td>'; _buf << ::Erubi.h(( item )); _buf << '</td>
92
- </tr>
93
- '; end
94
- _buf << ' </tbody>
95
- </table>
96
- '; _buf << ::Erubi.h(( i+1 )); _buf << '
97
- ';
98
- _buf.to_s
99
- END2
100
- <table>
101
- <tbody>
102
- <tr>
103
- <td>1</td>
104
- <td>&amp;&#39;&lt;&gt;&quot;2</td>
105
- </tr>
106
- </tbody>
107
- </table>
108
- 1
109
- END3
110
- end
111
-
112
- it "should handle ensure option" do
113
- list = ['&\'<>"2']
114
- @options[:ensure] = true
115
- @options[:bufvar] = '@a'
116
- @a = 'bar'
117
- check_output(<<END1, <<END2, <<END3){}
118
- <table>
119
- <tbody>
120
- <% i = 0
121
- list.each_with_index do |item, i| %>
122
- <tr>
123
- <td><%= i+1 %></td>
124
- <td><%== item %></td>
125
- </tr>
126
- <% end %>
127
- </tbody>
128
- </table>
129
- <%== i+1 %>
130
- END1
131
- begin; __original_outvar = @a if defined?(@a); @a = String.new; @a << '<table>
132
- <tbody>
133
- '; i = 0
134
- list.each_with_index do |item, i|
135
- @a << ' <tr>
136
- <td>'; @a << ( i+1 ).to_s; @a << '</td>
137
- <td>'; @a << ::Erubi.h(( item )); @a << '</td>
138
- </tr>
139
- '; end
140
- @a << ' </tbody>
141
- </table>
142
- '; @a << ::Erubi.h(( i+1 )); @a << '
143
- ';
144
- @a.to_s
145
- ; ensure
146
- @a = __original_outvar
147
- end
148
- END2
149
- <table>
150
- <tbody>
151
- <tr>
152
- <td>1</td>
153
- <td>&amp;&#39;&lt;&gt;&quot;2</td>
154
- </tr>
155
- </tbody>
156
- </table>
157
- 1
158
- END3
159
- @a.must_equal 'bar'
160
- end
161
-
162
- [['', false], ['=', true]].each do |ind, escape|
163
- it "should allow <%|=#{ind} and <%| for capturing with CaptureEndEngine with :escape_capture => #{escape} and :escape => #{!escape}" do
164
- @options[:bufvar] = '@a'
165
- @options[:capture] = true
166
- @options[:escape_capture] = escape
167
- @options[:escape] = !escape
168
- @options[:engine] = ::Erubi::CaptureEndEngine
169
- setup_bar
170
- check_output(<<END1, <<END2, <<END3){}
171
- <table>
172
- <tbody>
173
- <%|=#{ind} bar do %>
174
- <b><%=#{ind} '&' %></b>
175
- <%| end %>
176
- </tbody>
177
- </table>
178
- END1
179
- #{'__erubi = ::Erubi;' unless escape}@a = String.new; @a << '<table>
180
- <tbody>
181
- '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = String.new; __erubi_stack.last << #{!escape ? '__erubi' : '::Erubi'}.h(( bar do @a << '
182
- '; @a << ' <b>'; @a << #{!escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << '</b>
183
- '; @a << ' '; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << '
184
- '; @a << ' </tbody>
185
- </table>
186
- ';
187
- @a.to_s
188
- END2
189
- <table>
190
- <tbody>
191
- A
192
- &lt;B&gt;&amp;AMP;&lt;/B&gt;
193
- B
194
- </tbody>
195
- </table>
196
- END3
197
- end
198
- end
199
-
200
- [['', true], ['=', false]].each do |ind, escape|
201
- it "should allow <%|=#{ind} and <%| for capturing with CaptureEndEngine when with :escape => #{escape}" do
202
- @options[:bufvar] = '@a'
203
- @options[:escape] = escape
204
- @options[:engine] = ::Erubi::CaptureEndEngine
205
- setup_bar
206
- check_output(<<END1, <<END2, <<END3){}
207
- <table>
208
- <tbody>
209
- <%|=#{ind} bar do %>
210
- <b><%=#{ind} '&' %></b>
211
- <%| end %>
212
- </tbody>
213
- </table>
214
- END1
215
- #{'__erubi = ::Erubi;' if escape}@a = String.new; @a << '<table>
216
- <tbody>
217
- '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = String.new; __erubi_stack.last << (( bar do @a << '
218
- '; @a << ' <b>'; @a << #{escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << '</b>
219
- '; @a << ' '; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << '
220
- '; @a << ' </tbody>
221
- </table>
222
- ';
223
- @a.to_s
224
- END2
225
- <table>
226
- <tbody>
227
- A
228
- <B>&AMP;</B>
229
- B
230
- </tbody>
231
- </table>
232
- END3
233
- end
234
-
235
- it "should allow <%|=#{ind} and <%| for nested capturing with CaptureEndEngine when with :escape => #{escape}" do
236
- @options[:bufvar] = '@a'
237
- @options[:escape] = escape
238
- @options[:engine] = ::Erubi::CaptureEndEngine
239
- setup_bar
240
- check_output(<<END1, <<END2, <<END3){}
241
- <table>
242
- <tbody>
243
- <%|=#{ind} bar do %>
244
- <b><%=#{ind} '&' %></b>
245
- <%|=#{ind} baz do %>e<%| end %>
246
- <%| end %>
247
- </tbody>
248
- </table>
249
- END1
250
- #{'__erubi = ::Erubi;' if escape}@a = String.new; @a << '<table>
251
- <tbody>
252
- '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = String.new; __erubi_stack.last << (( bar do @a << '
253
- '; @a << ' <b>'; @a << #{escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << '</b>
254
- '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = String.new; __erubi_stack.last << (( baz do @a << 'e'; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << '
255
- '; @a << ' '; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << '
256
- '; @a << ' </tbody>
257
- </table>
258
- ';
259
- @a.to_s
260
- END2
261
- <table>
262
- <tbody>
263
- A
264
- <B>&AMP;</B>
265
- CEDCED
266
- B
267
- </tbody>
268
- </table>
269
- END3
270
- end
271
- end
272
-
273
- [:outvar, :bufvar].each do |var|
274
- it "should handle :#{var} and :freeze options" do
275
- @options[var] = "@_out_buf"
276
- @options[:freeze] = true
277
- @items = [2]
278
- i = 0
279
- check_output(<<END1, <<END2, <<END3){}
280
- <table>
281
- <% for item in @items %>
282
- <tr>
283
- <td><%= i+1 %></td>
284
- <td><%== item %></td>
285
- </tr>
286
- <% end %>
287
- </table>
288
- END1
289
- # frozen_string_literal: true
290
- @_out_buf = String.new; @_out_buf << '<table>
291
- '; for item in @items
292
- @_out_buf << ' <tr>
293
- <td>'; @_out_buf << ( i+1 ).to_s; @_out_buf << '</td>
294
- <td>'; @_out_buf << ::Erubi.h(( item )); @_out_buf << '</td>
295
- </tr>
296
- '; end
297
- @_out_buf << '</table>
298
- ';
299
- @_out_buf.to_s
300
- END2
301
- <table>
302
- <tr>
303
- <td>1</td>
304
- <td>2</td>
305
- </tr>
306
- </table>
307
- END3
308
- end
309
- end
310
-
311
- it "should handle <%% and <%# syntax" do
312
- @items = [2]
313
- i = 0
314
- check_output(<<END1, <<END2, <<END3){}
315
- <table>
316
- <%% for item in @items %>
317
- <tr>
318
- <td><%# i+1 %></td>
319
- <td><%# item %></td>
320
- </tr>
321
- <%% end %>
322
- </table>
323
- END1
324
- _buf = String.new; _buf << '<table>
325
- '; _buf << '<% for item in @items %>
326
- '; _buf << ' <tr>
327
- <td>';; _buf << '</td>
328
- <td>';; _buf << '</td>
329
- </tr>
330
- '; _buf << ' <% end %>
331
- '; _buf << '</table>
332
- ';
333
- _buf.to_s
334
- END2
335
- <table>
336
- <% for item in @items %>
337
- <tr>
338
- <td></td>
339
- <td></td>
340
- </tr>
341
- <% end %>
342
- </table>
343
- END3
344
- end
345
-
346
- it "should handle :trim => false option" do
347
- @options[:trim] = false
348
- @items = [2]
349
- i = 0
350
- check_output(<<END1, <<END2, <<END3){}
351
- <table>
352
- <% for item in @items %>
353
- <tr>
354
- <td><%#
355
- i+1
356
- %></td>
357
- <td><%== item %></td>
358
- </tr>
359
- <% end %><%#%>
360
- <% i %>a
361
- <% i %>
362
- </table>
363
- END1
364
- _buf = String.new; _buf << '<table>
365
- '; _buf << ' '; for item in @items ; _buf << '
366
- '; _buf << ' <tr>
367
- <td>';
368
-
369
- _buf << '</td>
370
- <td>'; _buf << ::Erubi.h(( item )); _buf << '</td>
371
- </tr>
372
- '; _buf << ' '; end ;
373
- _buf << '
374
- '; _buf << ' '; i ; _buf << 'a
375
- '; _buf << ' '; i ; _buf << '
376
- '; _buf << '</table>
377
- ';
378
- _buf.to_s
379
- END2
380
- <table>
381
-
382
- <tr>
383
- <td></td>
384
- <td>2</td>
385
- </tr>
386
-
387
- a
388
-
389
- </table>
390
- END3
391
- end
392
-
393
- [:escape, :escape_html].each do |opt|
394
- it "should handle :#{opt} and :escapefunc options" do
395
- @options[opt] = true
396
- @options[:escapefunc] = 'h.call'
397
- h = proc{|s| s.to_s*2}
398
- list = ['2']
399
- check_output(<<END1, <<END2, <<END3){}
400
- <table>
401
- <tbody>
402
- <% i = 0
403
- list.each_with_index do |item, i| %>
404
- <tr>
405
- <td><%= i+1 %></td>
406
- <td><%== item %></td>
407
- </tr>
408
- <% end %>
409
- </tbody>
410
- </table>
411
- <%== i+1 %>
412
- END1
413
- _buf = String.new; _buf << '<table>
414
- <tbody>
415
- '; i = 0
416
- list.each_with_index do |item, i|
417
- _buf << ' <tr>
418
- <td>'; _buf << h.call(( i+1 )); _buf << '</td>
419
- <td>'; _buf << ( item ).to_s; _buf << '</td>
420
- </tr>
421
- '; end
422
- _buf << ' </tbody>
423
- </table>
424
- '; _buf << ( i+1 ).to_s; _buf << '
425
- ';
426
- _buf.to_s
427
- END2
428
- <table>
429
- <tbody>
430
- <tr>
431
- <td>11</td>
432
- <td>2</td>
433
- </tr>
434
- </tbody>
435
- </table>
436
- 1
437
- END3
438
- end
439
- end
440
-
441
- it "should handle :escape option without :escapefunc option" do
442
- @options[:escape] = true
443
- list = ['&\'<>"2']
444
- check_output(<<END1, <<END2, <<END3){}
445
- <table>
446
- <tbody>
447
- <% i = 0
448
- list.each_with_index do |item, i| %>
449
- <tr>
450
- <td><%== i+1 %></td>
451
- <td><%= item %></td>
452
- </tr>
453
- <% end %>
454
- </tbody>
455
- </table>
456
- END1
457
- __erubi = ::Erubi;_buf = String.new; _buf << '<table>
458
- <tbody>
459
- '; i = 0
460
- list.each_with_index do |item, i|
461
- _buf << ' <tr>
462
- <td>'; _buf << ( i+1 ).to_s; _buf << '</td>
463
- <td>'; _buf << __erubi.h(( item )); _buf << '</td>
464
- </tr>
465
- '; end
466
- _buf << ' </tbody>
467
- </table>
468
- ';
469
- _buf.to_s
470
- END2
471
- <table>
472
- <tbody>
473
- <tr>
474
- <td>1</td>
475
- <td>&amp;&#39;&lt;&gt;&quot;2</td>
476
- </tr>
477
- </tbody>
478
- </table>
479
- END3
480
- end
481
-
482
- it "should handle :preamble and :postamble options" do
483
- @options[:preamble] = '_buf = String.new("1");'
484
- @options[:postamble] = "_buf[0...18]\n"
485
- list = ['2']
486
- check_output(<<END1, <<END2, <<END3){}
487
- <table>
488
- <tbody>
489
- <% i = 0
490
- list.each_with_index do |item, i| %>
491
- <tr>
492
- <td><%= i+1 %></td>
493
- <td><%== item %></td>
494
- </tr>
495
- <% end %>
496
- </tbody>
497
- </table>
498
- <%== i+1 %>
499
- END1
500
- _buf = String.new("1"); _buf << '<table>
501
- <tbody>
502
- '; i = 0
503
- list.each_with_index do |item, i|
504
- _buf << ' <tr>
505
- <td>'; _buf << ( i+1 ).to_s; _buf << '</td>
506
- <td>'; _buf << ::Erubi.h(( item )); _buf << '</td>
507
- </tr>
508
- '; end
509
- _buf << ' </tbody>
510
- </table>
511
- '; _buf << ::Erubi.h(( i+1 )); _buf << '
512
- ';
513
- _buf[0...18]
514
- END2
515
- 1<table>
516
- <tbody>
517
- END3
518
- end
519
-
520
- it "should have working filename accessor" do
521
- Erubi::Engine.new('', :filename=>'foo.rb').filename.must_equal 'foo.rb'
522
- end
523
-
524
- it "should have working bufvar accessor" do
525
- Erubi::Engine.new('', :bufvar=>'foo').bufvar.must_equal 'foo'
526
- Erubi::Engine.new('', :outvar=>'foo').bufvar.must_equal 'foo'
527
- end
528
-
529
- it "should return frozen object" do
530
- Erubi::Engine.new('').frozen?.must_equal true
531
- end
532
-
533
- it "should have frozen src" do
534
- Erubi::Engine.new('').src.frozen?.must_equal true
535
- end
536
-
537
- it "should raise an error if a tag is not handled when a custom regexp is used" do
538
- proc{Erubi::Engine.new('<%] %>', :regexp =>/<%(={1,2}|\]|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m)}.must_raise ArgumentError
539
- proc{Erubi::CaptureEndEngine.new('<%] %>', :regexp =>/<%(={1,2}|\]|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m)}.must_raise ArgumentError
540
- end
541
- end