erubi 1.10.0 → 1.13.1

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
2
  SHA256:
3
- metadata.gz: 3f504eb8d1ae0f89e098de29336a69d0251cd8659a1b6fe1ab86c7f23ed44d67
4
- data.tar.gz: e54396a6eb2cf0c193089c9b2c3d5fa0f92daf5cf4f462fe09d0aea6dca49419
3
+ metadata.gz: 547aa741f72fd87ac7747c597614f457b3880d01d3b3aa161c24e62c93f00cc7
4
+ data.tar.gz: d465d4b57e9abe24de9cf5859773691dfee9198ca0c4d05a5ca8f0903b2acefd
5
5
  SHA512:
6
- metadata.gz: 0a789b8a528548760ac4898867588477e03c3f1410bd930538c849e167c68a346202f8c168a2a71107485c961bb41a91d549f687a4d24ebf3614821bc15b4e73
7
- data.tar.gz: 14e74f5fb8c452c0cbc15afbe51b8a86be572d8d585416f09f07d9227e06ad8d8c14d8681424dc53f9b7eaba1a1297ed72e8e7dc0c36afe6e2da1926bec742d0
6
+ metadata.gz: 109432844ed8df5daab8e5b6e6aa2404c314009f968129ec02c920f6c75094f4a484207751d34cb2255d6c3d5f286e14c369ba54dc398e06266d22e84e2f4e24
7
+ data.tar.gz: 5eab140a49714dd65c67b9eb7e196a86f25c9a3a59348b04dc3d7b57db8c0cbccbca28f94dae2ae69c04382e5ab6c4350c7f8dd99db2dbbd0121ba0759bd0ca1
data/CHANGELOG CHANGED
@@ -1,3 +1,27 @@
1
+ === 1.13.1 (2024-12-19)
2
+
3
+ * Avoid spurious frozen string literal warnings for chilled strings when using Ruby 3.4 (jeremyevans)
4
+
5
+ === 1.13.0 (2024-06-13)
6
+
7
+ * Define Erubi.h as a module function (jeremyevans)
8
+
9
+ * Add erubi/capture_block, supporting capturing block output via standard <%= and <%== tags (jeremyevans)
10
+
11
+ === 1.12.0 (2022-12-22)
12
+
13
+ * Use erb/escape for faster html escaping if available (jeremyevans)
14
+
15
+ * Default :freeze_template_literals option to false if running with --enable-frozen-string-literal (casperisfine) (#35)
16
+
17
+ === 1.11.0 (2022-08-02)
18
+
19
+ * Support :freeze_template_literals option for configuring whether to add .freeze to template literal strings (casperisfine) (#33)
20
+
21
+ * Support :chain_appends option for chaining appends to the buffer variable (casperisfine, jeremyevans) (#32)
22
+
23
+ * Avoid unnecessary defined? usage on Ruby 3+ when using the :ensure option (jeremyevans)
24
+
1
25
  === 1.10.0 (2020-11-13)
2
26
 
3
27
  * Improve template parsing, mostly by reducing allocations (jeremyevans)
data/MIT-LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
2
- copyright(c) 2016-2020 Jeremy Evans
2
+ copyright(c) 2016-2021 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
data/README.rdoc CHANGED
@@ -8,8 +8,8 @@ the same basic algorithm, with the following differences:
8
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
10
  * Escapes <tt>'</tt> (apostrophe) when escaping for better XSS protection
11
- * Has 6x faster escaping on ruby 2.3+ by using cgi/escape
12
- * Has 86% smaller memory footprint
11
+ * Has 15x-6x faster escaping by using erb/escape or cgi/escape
12
+ * Has 81% smaller memory footprint (calculated using +ObjectSpace.memsize_of_all+)
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)
@@ -42,33 +42,75 @@ erb template handler in Tilt 2.0.6+ and Rails 5.1+.
42
42
  == Capturing
43
43
 
44
44
  Erubi does not support capturing block output into the template by default.
45
- However, it comes with an +erubi/capture_end+ file that supports capturing
46
- via <tt><%|=</tt> and <tt><%|==</tt> tags which are closed with a
47
- <tt><%|</tt> tag:
45
+ It currently ships with two implementations that allow it.
48
46
 
49
- <%|= form do %>
47
+ === Erubi::CaptureBlockEngine
48
+
49
+ The recommended implementation can be required via +erubi/capture_block+,
50
+ which allows capturing to work with normal <tt><%=</tt> and <tt><%==</tt>
51
+ tags.
52
+
53
+ <%= form do %>
50
54
  <input>
51
- <%| end %>
55
+ <% end %>
56
+
57
+ When using the capture_block support, capture methods should just return
58
+ the text it emit into the template, and call +capture+ on the buffer value.
59
+ Since the buffer variable is a local variable and not an instance variable
60
+ by default, you'll probably want to set the +:bufvar+ variable when using
61
+ the capture_block support to an instance variable, and have any methods
62
+ used call capture on that instance variable. Example:
63
+
64
+ def form(&block)
65
+ "<form>#{@_buf.capture(&block)}</form>"
66
+ end
52
67
 
53
- This offers similar functionality to that offered by Rails' <tt><%=</tt>
54
- tags, but without the corner cases with that approach (which are due to
55
- attempting to parse ruby code via a regexp). Similar to the <tt><%=</tt>
56
- and <tt><%==</tt> tags, <tt><%|=</tt> captures by default and
57
- <tt><%|==</tt> captures and escapes by default, but this can be reversed
58
- via the +:escape_capture+ or +:escape+ options.
68
+ puts eval(Erubi::CaptureBlockEngine.new(<<-END, bufvar: '@_buf', trim: false).src)
69
+ before
70
+ <%= form do %>
71
+ inside
72
+ <% end %>
73
+ after
74
+ END
75
+
76
+ # Output:
77
+ # before
78
+ # <form>
79
+ # inside
80
+ # </form>
81
+ # after
59
82
 
60
- To use the capture_end support with tilt:
83
+ To use the capture_block support with tilt:
61
84
 
62
85
  require 'tilt'
63
- require 'erubi/capture_end'
64
- Tilt.new("filename.erb", :engine_class=>Erubi::CaptureEndEngine).render
86
+ require 'erubi/capture_block'
87
+ Tilt.new("filename.erb", :engine_class=>Erubi::CaptureBlockEngine).render
88
+
89
+ Note that the capture_block support, while very compatible with the default
90
+ support, is not 100% compatible. One area where behavior differs is when
91
+ using multiple statements inside <tt><%=</tt> and <tt><%==</tt> tags:
92
+
93
+ <%= 1; 2 %>
94
+
95
+ The default support will output 2, but the capture_block support will output
96
+ 1.
97
+
98
+ === Erubi::CaptureEndEngine
99
+
100
+ An alternative capture implementation can be required via +erubi/capture_end+,
101
+ which supports it via <tt><%|=</tt> and <tt><%|==</tt> tags which are
102
+ closed with a <tt><%|</tt> tag:
103
+
104
+ <%|= form do %>
105
+ <input>
106
+ <%| end %>
107
+
108
+ It is only recommended to use +erubi/capture_end+ for backwards
109
+ compatibilty.
65
110
 
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:
111
+ When using the capture_end support, capture methods (such as +form+ in the example
112
+ above) should return the (potentially modified) buffer. Similar to the
113
+ capture_block support, using an instance variable is recommended. Example:
72
114
 
73
115
  def form
74
116
  @_buf << "<form>"
@@ -77,7 +119,7 @@ instance variable. Example:
77
119
  @_buf
78
120
  end
79
121
 
80
- puts eval(Erubi::CaptureEndEngine.new(<<-END, :bufvar=>:@_buf).src)
122
+ puts eval(Erubi::CaptureEndEngine.new(<<-END, bufvar: '@_buf').src)
81
123
  before
82
124
  <%|= form do %>
83
125
  inside
data/Rakefile CHANGED
@@ -16,7 +16,7 @@ end
16
16
  RDOC_DEFAULT_OPTS = ["--line-numbers", "--inline-source", '--title', 'Erubi: Small ERB Implementation']
17
17
 
18
18
  begin
19
- gem 'hanna-nouveau'
19
+ gem 'hanna'
20
20
  RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
21
21
  rescue Gem::LoadError
22
22
  end
@@ -42,7 +42,7 @@ end
42
42
 
43
43
  spec = proc do |env|
44
44
  env.each{|k,v| ENV[k] = v}
45
- sh "#{FileUtils::RUBY} test/test.rb"
45
+ sh "#{FileUtils::RUBY} #{'-w' if RUBY_VERSION >= '3'} #{'-W:strict_unused_block' if RUBY_VERSION >= '3.4'} test/test.rb"
46
46
  env.each{|k,v| ENV.delete(k)}
47
47
  end
48
48
 
@@ -57,11 +57,6 @@ desc "Run specs with coverage"
57
57
  task "spec_cov" do
58
58
  spec.call('COVERAGE'=>'1')
59
59
  end
60
-
61
- desc "Run specs with -w, some warnings filtered"
62
- task "spec_w" do
63
- sh "#{FileUtils::RUBY} test/test_w.rb"
64
- end
65
60
 
66
61
  ### Other
67
62
 
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erubi'
4
+
5
+ module Erubi
6
+ # An engine class that supports capturing blocks via the <tt><%=</tt> and <tt><%==</tt> tags:
7
+ #
8
+ # <%= upcase_form do %>
9
+ # <%= 'foo' %>
10
+ # <% end %>
11
+ #
12
+ # Where +upcase_form+ is defined like:
13
+ #
14
+ # def upcase_form(&block)
15
+ # "<form>#{@bufvar.capture(&block).upcase}</form>"
16
+ # end
17
+ #
18
+ # With output being:
19
+ #
20
+ # <form>
21
+ # FOO
22
+ # </form>
23
+ #
24
+ # This requires using a string subclass as the buffer value, provided by the
25
+ # CaptureBlockEngine::Buffer class.
26
+ #
27
+ # This engine does not support the :escapefunc option. To change the escaping function,
28
+ # use a subclass of CaptureBlockEngine::Buffer and override the #| method.
29
+ #
30
+ # This engine does not support the :chain_appends option, and ignores it if present.
31
+ class CaptureBlockEngine < Engine
32
+ class Buffer < ::String
33
+
34
+ # Convert argument to string when concatening
35
+ def <<(v)
36
+ concat(v.to_s)
37
+ end
38
+
39
+ # Escape argument using Erubi.h then then concatenate it to the receiver.
40
+ def |(v)
41
+ concat(h(v))
42
+ end
43
+
44
+ # Temporarily clear the receiver before yielding to the block, yield the
45
+ # given args to the block, return any data captured by the receiver, and
46
+ # restore the original data the receiver contained before returning.
47
+ def capture(*args)
48
+ prev = dup
49
+ replace("") # 1.8 support!
50
+ yield(*args)
51
+ dup
52
+ ensure
53
+ replace(prev)
54
+ end
55
+
56
+ private
57
+
58
+ if RUBY_VERSION >= '2'
59
+ define_method(:h, ::Erubi.instance_method(:h))
60
+ # :nocov:
61
+ else
62
+ def h(v)
63
+ ::Erubi.h(v)
64
+ end
65
+ end
66
+ # :nocov:
67
+ end
68
+
69
+ def initialize(input, properties={})
70
+ properties = Hash[properties]
71
+ properties[:bufval] ||= '::Erubi::CaptureBlockEngine::Buffer.new'
72
+ properties[:chain_appends] = false
73
+ super
74
+ end
75
+
76
+ private
77
+
78
+ def add_expression_result(code)
79
+ add_expression_op(' <<= ', code)
80
+ end
81
+
82
+ def add_expression_result_escaped(code)
83
+ add_expression_op(' |= ', code)
84
+ end
85
+
86
+ def add_expression_op(op, code)
87
+ check = /\A\s*\z/.send(MATCH_METHOD, code) ? "''" : ''
88
+ with_buffer{@src << op << check << code}
89
+ end
90
+ end
91
+ end
@@ -36,13 +36,19 @@ module Erubi
36
36
  rspace = nil if tailch && !tailch.empty?
37
37
  add_text(lspace) if lspace
38
38
  escape_capture = !((indicator == '|=') ^ @escape_capture)
39
- src << "begin; (#{@bufstack} ||= []) << #{@bufvar}; #{@bufvar} = #{@bufval}; #{@bufstack}.last << #{@escapefunc if escape_capture}((" << code
39
+ terminate_expression
40
+ @src << "begin; (#{@bufstack} ||= []) << #{@bufvar}; #{@bufvar} = #{@bufval}; #{@bufstack}.last << #{@escapefunc if escape_capture}((" << code
41
+ @buffer_on_stack = false
40
42
  add_text(rspace) if rspace
41
43
  when '|'
42
44
  rspace = nil if tailch && !tailch.empty?
43
45
  add_text(lspace) if lspace
44
- result = @yield_returns_buffer ? " #{@bufvar}; " : ""
45
- src << result << code << ")).to_s; ensure; #{@bufvar} = #{@bufstack}.pop; end;"
46
+ if @yield_returns_buffer
47
+ terminate_expression
48
+ @src << " #{@bufvar}; "
49
+ end
50
+ @src << code << ")).to_s; ensure; #{@bufvar} = #{@bufstack}.pop; end;"
51
+ @buffer_on_stack = false
46
52
  add_text(rspace) if rspace
47
53
  else
48
54
  super
data/lib/erubi.rb CHANGED
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Erubi
4
- VERSION = '1.10.0'
5
- RANGE_ALL = 0..-1
4
+ VERSION = '1.13.1'
6
5
 
7
6
  # :nocov:
8
7
  if RUBY_VERSION >= '1.9'
@@ -13,38 +12,46 @@ module Erubi
13
12
  RANGE_LAST = -1..-1
14
13
  end
15
14
 
16
- TEXT_END = RUBY_VERSION >= '2.1' ? "'.freeze;" : "';"
17
15
  MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
16
+ SKIP_DEFINED_FOR_INSTANCE_VARIABLE = RUBY_VERSION > '3'
17
+ FREEZE_TEMPLATE_LITERALS = !eval("''").frozen? && RUBY_VERSION >= '2.1'
18
18
  # :nocov:
19
19
 
20
20
  begin
21
- require 'cgi/escape'
22
- # :nocov:
23
- unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
24
- CGI = Object.new
25
- CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
26
- end
27
- # :nocov:
28
- # Escape characters with their HTML/XML equivalents.
29
- def self.h(value)
30
- CGI.escapeHTML(value.to_s)
31
- end
21
+ require 'erb/escape'
22
+ define_method(:h, ERB::Escape.instance_method(:html_escape))
23
+ # :nocov:
32
24
  rescue LoadError
33
- # :nocov:
34
- ESCAPE_TABLE = {'&' => '&amp;'.freeze, '<' => '&lt;'.freeze, '>' => '&gt;'.freeze, '"' => '&quot;'.freeze, "'" => '&#39;'.freeze}.freeze
35
- if RUBY_VERSION >= '1.9'
36
- def self.h(value)
37
- value.to_s.gsub(/[&<>"']/, ESCAPE_TABLE)
25
+ begin
26
+ require 'cgi/escape'
27
+ unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
28
+ CGI = Object.new
29
+ CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
38
30
  end
39
- else
40
- def self.h(value)
41
- value.to_s.gsub(/[&<>"']/){|s| ESCAPE_TABLE[s]}
31
+ # Escape characters with their HTML/XML equivalents.
32
+ def h(value)
33
+ CGI.escapeHTML(value.to_s)
34
+ end
35
+ rescue LoadError
36
+ ESCAPE_TABLE = {'&' => '&amp;'.freeze, '<' => '&lt;'.freeze, '>' => '&gt;'.freeze, '"' => '&quot;'.freeze, "'" => '&#39;'.freeze}.freeze
37
+ if RUBY_VERSION >= '1.9'
38
+ def h(value)
39
+ value.to_s.gsub(/[&<>"']/, ESCAPE_TABLE)
40
+ end
41
+ else
42
+ def h(value)
43
+ value.to_s.gsub(/[&<>"']/){|s| ESCAPE_TABLE[s]}
44
+ end
42
45
  end
43
46
  end
44
- # :nocov:
45
47
  end
48
+ # :nocov:
49
+ module_function :h
46
50
 
47
51
  class Engine
52
+ # The default regular expression used for scanning.
53
+ DEFAULT_REGEXP = /<%(={1,2}|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m
54
+
48
55
  # The frozen ruby source code generated from the template, which can be evaled.
49
56
  attr_reader :src
50
57
 
@@ -57,12 +64,22 @@ module Erubi
57
64
  # Initialize a new Erubi::Engine. Options:
58
65
  # +:bufval+ :: The value to use for the buffer variable, as a string (default <tt>'::String.new'</tt>).
59
66
  # +:bufvar+ :: The variable name to use for the buffer variable, as a string.
67
+ # +:chain_appends+ :: Whether to chain <tt><<</t> calls to the buffer variable. Offers better
68
+ # performance, but can cause issues when the buffer variable is reassigned during
69
+ # template rendering (default +false+).
60
70
  # +:ensure+ :: Wrap the template in a begin/ensure block restoring the previous value of bufvar.
61
71
  # +:escapefunc+ :: The function to use for escaping, as a string (default: <tt>'::Erubi.h'</tt>).
62
72
  # +:escape+ :: Whether to make <tt><%=</tt> escape by default, and <tt><%==</tt> not escape by default.
63
73
  # +:escape_html+ :: Same as +:escape+, with lower priority.
64
74
  # +:filename+ :: The filename for the template.
65
- # +:freeze+ :: Whether to enable frozen string literals in the resulting source code.
75
+ # +:freeze+ :: Whether to enable add a <tt>frozen_string_literal: true</tt> magic comment at the top of
76
+ # the resulting source code. Note this may cause problems if you are wrapping the resulting
77
+ # source code in other code, because the magic comment only has an effect at the beginning of
78
+ # the file, and having the magic comment later in the file can trigger warnings.
79
+ # +:freeze_template_literals+ :: Whether to suffix all literal strings for template code with <tt>.freeze</tt>
80
+ # (default: +true+ on Ruby 2.1+, +false+ on Ruby 2.0 and older).
81
+ # Can be set to +false+ on Ruby 2.3+ when frozen string literals are enabled
82
+ # in order to improve performance.
66
83
  # +:literal_prefix+ :: The prefix to output when using escaped tag delimiters (default <tt>'<%'</tt>).
67
84
  # +:literal_postfix+ :: The postfix to output when using escaped tag delimiters (default <tt>'%>'</tt>).
68
85
  # +:outvar+ :: Same as +:bufvar+, with lower priority.
@@ -77,20 +94,34 @@ module Erubi
77
94
  @filename = properties[:filename]
78
95
  @bufvar = bufvar = properties[:bufvar] || properties[:outvar] || "_buf"
79
96
  bufval = properties[:bufval] || '::String.new'
80
- regexp = properties[:regexp] || /<%(={1,2}|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m
97
+ regexp = properties[:regexp] || DEFAULT_REGEXP
81
98
  literal_prefix = properties[:literal_prefix] || '<%'
82
99
  literal_postfix = properties[:literal_postfix] || '%>'
83
100
  preamble = properties[:preamble] || "#{bufvar} = #{bufval};"
84
101
  postamble = properties[:postamble] || "#{bufvar}.to_s\n"
102
+ @chain_appends = properties[:chain_appends]
103
+ @text_end = if properties.fetch(:freeze_template_literals, FREEZE_TEMPLATE_LITERALS)
104
+ "'.freeze"
105
+ else
106
+ "'"
107
+ end
85
108
 
109
+ @buffer_on_stack = false
86
110
  @src = src = properties[:src] || String.new
87
111
  src << "# frozen_string_literal: true\n" if properties[:freeze]
88
- src << "begin; __original_outvar = #{bufvar} if defined?(#{bufvar}); " if properties[:ensure]
112
+ if properties[:ensure]
113
+ src << "begin; __original_outvar = #{bufvar}"
114
+ if SKIP_DEFINED_FOR_INSTANCE_VARIABLE && /\A@[^@]/ =~ bufvar
115
+ src << "; "
116
+ else
117
+ src << " if defined?(#{bufvar}); "
118
+ end
119
+ end
89
120
 
90
121
  unless @escapefunc = properties[:escapefunc]
91
122
  if escape
92
123
  @escapefunc = '__erubi.h'
93
- src << "__erubi = ::Erubi;"
124
+ src << "__erubi = ::Erubi; "
94
125
  else
95
126
  @escapefunc = '::Erubi.h'
96
127
  end
@@ -174,23 +205,35 @@ module Erubi
174
205
 
175
206
  private
176
207
 
208
+ if RUBY_VERSION >= '2.3'
209
+ def _dup_string_if_frozen(string)
210
+ +string
211
+ end
212
+ # :nocov:
213
+ else
214
+ def _dup_string_if_frozen(string)
215
+ string.frozen? ? string.dup : string
216
+ end
217
+ end
218
+ # :nocov:
219
+
177
220
  # Add raw text to the template. Modifies argument if argument is mutable as a memory optimization.
178
221
  # Must be called with a string, cannot be called with nil (Rails's subclass depends on it).
179
222
  def add_text(text)
180
223
  return if text.empty?
181
224
 
182
- if text.frozen?
183
- text = text.gsub(/['\\]/, '\\\\\&')
184
- else
185
- text.gsub!(/['\\]/, '\\\\\&')
186
- end
187
- @src << " " << @bufvar << " << '" << text << TEXT_END
225
+ text = _dup_string_if_frozen(text)
226
+ text.gsub!(/['\\]/, '\\\\\&')
227
+
228
+ with_buffer{@src << " << '" << text << @text_end}
188
229
  end
189
230
 
190
231
  # Add ruby code to the template
191
232
  def add_code(code)
233
+ terminate_expression
192
234
  @src << code
193
235
  @src << ';' unless code[RANGE_LAST] == "\n"
236
+ @buffer_on_stack = false
194
237
  end
195
238
 
196
239
  # Add the given ruby expression result to the template,
@@ -205,23 +248,52 @@ module Erubi
205
248
 
206
249
  # Add the result of Ruby expression to the template
207
250
  def add_expression_result(code)
208
- @src << ' ' << @bufvar << ' << (' << code << ').to_s;'
251
+ with_buffer{@src << ' << (' << code << ').to_s'}
209
252
  end
210
253
 
211
254
  # Add the escaped result of Ruby expression to the template
212
255
  def add_expression_result_escaped(code)
213
- @src << ' ' << @bufvar << ' << ' << @escapefunc << '((' << code << '));'
256
+ with_buffer{@src << ' << ' << @escapefunc << '((' << code << '))'}
214
257
  end
215
258
 
216
259
  # Add the given postamble to the src. Can be overridden in subclasses
217
260
  # to make additional changes to src that depend on the current state.
218
261
  def add_postamble(postamble)
219
- src << postamble
262
+ terminate_expression
263
+ @src << postamble
220
264
  end
221
265
 
222
266
  # Raise an exception, as the base engine class does not support handling other indicators.
223
267
  def handle(indicator, code, tailch, rspace, lspace)
224
268
  raise ArgumentError, "Invalid indicator: #{indicator}"
225
269
  end
270
+
271
+ # Make sure the buffer variable is the target of the next append
272
+ # before yielding to the block. Mark that the buffer is the target
273
+ # of the next append after the block executes.
274
+ #
275
+ # This method should only be called if the block will result in
276
+ # code where << will append to the bufvar.
277
+ def with_buffer
278
+ if @chain_appends
279
+ unless @buffer_on_stack
280
+ @src << '; ' << @bufvar
281
+ end
282
+ yield
283
+ @buffer_on_stack = true
284
+ else
285
+ @src << ' ' << @bufvar
286
+ yield
287
+ @src << ';'
288
+ end
289
+ end
290
+
291
+ # Make sure that any current expression has been terminated.
292
+ # The default is to terminate all expressions, but when
293
+ # the chain_appends option is used, expressions may not be
294
+ # terminated.
295
+ def terminate_expression
296
+ @src << '; ' if @chain_appends
297
+ end
226
298
  end
227
299
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erubi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  - kuwata-lab.com
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-11-13 00:00:00.000000000 Z
12
+ date: 2024-12-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -53,15 +53,17 @@ files:
53
53
  - README.rdoc
54
54
  - Rakefile
55
55
  - lib/erubi.rb
56
+ - lib/erubi/capture_block.rb
56
57
  - lib/erubi/capture_end.rb
57
58
  homepage: https://github.com/jeremyevans/erubi
58
59
  licenses:
59
60
  - MIT
60
61
  metadata:
61
62
  bug_tracker_uri: https://github.com/jeremyevans/erubi/issues
63
+ mailing_list_uri: https://github.com/jeremyevans/erubi/discussions
62
64
  changelog_uri: https://github.com/jeremyevans/erubi/blob/master/CHANGELOG
63
65
  source_code_uri: https://github.com/jeremyevans/erubi
64
- post_install_message:
66
+ post_install_message:
65
67
  rdoc_options:
66
68
  - "--quiet"
67
69
  - "--line-numbers"
@@ -83,8 +85,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
85
  - !ruby/object:Gem::Version
84
86
  version: '0'
85
87
  requirements: []
86
- rubygems_version: 3.1.4
87
- signing_key:
88
+ rubygems_version: 3.5.22
89
+ signing_key:
88
90
  specification_version: 4
89
91
  summary: Small ERB Implementation
90
92
  test_files: []