erubi 1.6.1 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
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