pax_global_header00006660000000000000000000000064147310512660014517gustar00rootroot0000000000000052 comment=f1a0fb5bfb2a2604c0cf850d3c960bfc458421b6 erubi-1.13.1/000077500000000000000000000000001473105126600127105ustar00rootroot00000000000000erubi-1.13.1/.ci.gemfile000066400000000000000000000003101473105126600147050ustar00rootroot00000000000000source '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 erubi-1.13.1/.github/000077500000000000000000000000001473105126600142505ustar00rootroot00000000000000erubi-1.13.1/.github/workflows/000077500000000000000000000000001473105126600163055ustar00rootroot00000000000000erubi-1.13.1/.github/workflows/ci.yml000066400000000000000000000011511473105126600174210ustar00rootroot00000000000000name: CI on: push: branches: [ master ] pull_request: branches: [ master ] permissions: contents: read jobs: tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby: [ "1.9.3", "2.0.0", 2.1, 2.3, 2.4, 2.5, 2.6, 2.7, "3.0", 3.1, 3.2, 3.3, jruby-9.1, jruby-9.2, jruby-9.3, jruby-9.4, truffleruby-head ] name: ${{ matrix.ruby }} env: BUNDLE_GEMFILE: .ci.gemfile steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - run: bundle exec rake erubi-1.13.1/.gitignore000066400000000000000000000000371473105126600147000ustar00rootroot00000000000000/erubi-*.gem /rdoc/ /coverage/ erubi-1.13.1/CHANGELOG000066400000000000000000000074731473105126600141350ustar00rootroot00000000000000=== 1.13.1 (2024-12-19) * Avoid spurious frozen string literal warnings for chilled strings when using Ruby 3.4 (jeremyevans) === 1.13.0 (2024-06-13) * Define Erubi.h as a module function (jeremyevans) * Add erubi/capture_block, supporting capturing block output via standard <%= and <%== tags (jeremyevans) === 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) === 1.8.0 (2018-12-18) * Support :yield_returns_buffer option in capture_end for always returning the (potentially modified) buffer in <%|= tags (evanleck) (#15) === 1.7.1 (2018-03-05) * Make whitespace handling for <%# %> tags more compatible with Erubis (jeremyevans) (#14) === 1.7.0 (2017-10-09) * Fix escaping in erubi/capture_end, the setting was previously inverted (jeremyevans) (#10) === 1.6.1 (2017-06-27) * Fix usage on newer versions of JRuby 9.1 (jeremyevans) === 1.6.0 (2017-02-27) * Use cgi/escape if available for 6x faster HTML escaping (k0kubun, jeremyevans) (#4) === 1.5.0 (2017-01-26) * Drop tilt/erubi file, as tilt now ships with Erubi support (jeremyevans) * Drop erubi/capture file, Erubi::CaptureEngine support (jeremyevans) === 1.4.0 (2017-01-20) * Allow postambles to depend on internal state of engine (jeremyevans) * Allow overriding of behavior for <%= and <%== tags to depend on which indicator was used (jeremyevans) * Make whitespace handling for <% %> tags more compatible with Erubis for subclasses overriding add_text (jeremyevans) === 1.3.0 (2016-12-29) * Support :capture=>:explicit option in tilt support to use Erubi::CaptureEndEngine (jeremyevans) * Add erubi/capture_end containing Erubi::CaptureEndEngine, allowing <%|= and <%|== for opening capture tags, and <%| for closing capture tags (jeremyevans) === 1.2.1 (2016-11-21) * Don't automatically freeze template text strings on ruby 1.9 or 2.0 (jeremyevans) === 1.2.0 (2016-11-21) * Engine#src now returns a frozen string (jeremyevans) * Automatically freeze template text strings on ruby 2.1+, reducing garbage generated (jeremyevans) * Allow overriding of behavior for <%= and <%== tags (ujifgc) (#1) === 1.1.0 (2016-11-14) * Add :ensure option to supporting restoring bufvar to original value (jeremyevans) * Don't have tilt support require erb (jeremyevans) * Support :engine_class option in tilt support to override engine class used (jeremyevans) * Support :capture option in tilt support to use Erubi::CaptureEngine (jeremyevans) * Add erubi/capture file containing Erubi::CaptureEngine, allowing <%|= and <%|== for capture (and escaping) blocks in templates (jeremyevans) * Raise ArgumentError if template source code contains indicators matched by regexp but not handled (jeremyevans) * Add :bufval option to support arbitrary buffer values (jeremyevans) * Add :regexp option to specify regexp used for scanning (jeremyevans) * Add :src option to specify initial template source (jeremyevans) === 1.0.0 (2016-11-10) * Initial Public Release erubi-1.13.1/MIT-LICENSE000066400000000000000000000021371473105126600143470ustar00rootroot00000000000000copyright(c) 2006-2011 kuwata-lab.com all rights reserved. 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 "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. erubi-1.13.1/README.rdoc000066400000000000000000000103241473105126600145160ustar00rootroot00000000000000= Erubi Erubi is a ERB template engine for ruby. It is a simplified fork of Erubis, using the same basic algorithm, with the following differences: * Handles postfix conditionals when using escaping (e.g. <%= foo if bar %>) * Supports frozen_string_literal: true in templates via :freeze option * Works with ruby's --enable-frozen-string-literal option * Automatically freezes strings for template text when ruby optimizes it (on ruby 2.1+) * Escapes ' (apostrophe) when escaping for better XSS protection * Has 15x-6x faster escaping by using erb/escape or cgi/escape * 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) * Is not dead (Erubis hasn't been updated since 2011) It is not designed with Erubis API compatibility in mind, though most Erubis ERB syntax works, with the following exceptions: * No support for <%=== for debug output = Installation gem install erubi = Source Code Source code is available on GitHub at https://github.com/jeremyevans/erubi = Usage Erubi only has built in support for retrieving the generated source for a file: require 'erubi' eval(Erubi::Engine.new(File.read('filename.erb')).src) Most users will probably use Erubi via Rails or Tilt. Erubi is the default erb template handler in Tilt 2.0.6+ and Rails 5.1+. == Capturing Erubi does not support capturing block output into the template by default. It currently ships with two implementations that allow it. === Erubi::CaptureBlockEngine The recommended implementation can be required via +erubi/capture_block+, which allows capturing to work with normal <%= and <%== tags. <%= form do %> <% end %> When using the capture_block support, capture methods should just return the text it emit into the template, and call +capture+ on the buffer value. Since the buffer variable is a local variable and not an instance variable by default, you'll probably want to set the +:bufvar+ variable when using the capture_block support to an instance variable, and have any methods used call capture on that instance variable. Example: def form(&block) "
#{@_buf.capture(&block)}
" end puts eval(Erubi::CaptureBlockEngine.new(<<-END, bufvar: '@_buf', trim: false).src) before <%= form do %> inside <% end %> after END # Output: # before #
# inside #
# after To use the capture_block support with tilt: require 'tilt' require 'erubi/capture_block' Tilt.new("filename.erb", :engine_class=>Erubi::CaptureBlockEngine).render Note that the capture_block support, while very compatible with the default support, is not 100% compatible. One area where behavior differs is when using multiple statements inside <%= and <%== tags: <%= 1; 2 %> The default support will output 2, but the capture_block support will output 1. === Erubi::CaptureEndEngine An alternative capture implementation can be required via +erubi/capture_end+, which supports it via <%|= and <%|== tags which are closed with a <%| tag: <%|= form do %> <%| end %> It is only recommended to use +erubi/capture_end+ for backwards compatibilty. When using the capture_end support, capture methods (such as +form+ in the example above) should return the (potentially modified) buffer. Similar to the capture_block support, using an instance variable is recommended. Example: def form @_buf << "
" yield @_buf << "
" @_buf end puts eval(Erubi::CaptureEndEngine.new(<<-END, bufvar: '@_buf').src) before <%|= form do %> inside <%| end %> after END # Output: # before #
# inside #
# after Alternatively, passing the option :yield_returns_buffer => true will return the buffer captured by the block instead of the last expression in the block. = Reporting Bugs The bug tracker is located at https://github.com/jeremyevans/erubi/issues = License MIT = Authors Jeremy Evans kuwata-lab.com erubi-1.13.1/Rakefile000066400000000000000000000027501473105126600143610ustar00rootroot00000000000000require "rake" require "rake/clean" NAME = 'erubi' CLEAN.include ["#{NAME}-*.gem", "rdoc", "coverage"] # Gem Packaging and Release desc "Packages #{NAME}" task :package=>[:clean] do |p| sh %{gem build #{NAME}.gemspec} end ### RDoc RDOC_DEFAULT_OPTS = ["--line-numbers", "--inline-source", '--title', 'Erubi: Small ERB Implementation'] begin gem 'hanna' RDOC_DEFAULT_OPTS.concat(['-f', 'hanna']) rescue Gem::LoadError end rdoc_task_class = begin require "rdoc/task" RDoc::Task rescue LoadError require "rake/rdoctask" Rake::RDocTask end RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc'] RDOC_FILES = %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb" rdoc_task_class.new do |rdoc| rdoc.rdoc_dir = "rdoc" rdoc.options += RDOC_OPTS rdoc.rdoc_files.add RDOC_FILES end ### Specs spec = proc do |env| env.each{|k,v| ENV[k] = v} sh "#{FileUtils::RUBY} #{'-w' if RUBY_VERSION >= '3'} #{'-W:strict_unused_block' if RUBY_VERSION >= '3.4'} test/test.rb" env.each{|k,v| ENV.delete(k)} end desc "Run specs" task "spec" do spec.call({}) end task :default=>:spec desc "Run specs with coverage" task "spec_cov" do spec.call('COVERAGE'=>'1') end ### Other desc "Start an IRB shell using the extension" task :irb do require 'rbconfig' ruby = ENV['RUBY'] || File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) irb = ENV['IRB'] || File.join(RbConfig::CONFIG['bindir'], File.basename(ruby).sub('ruby', 'irb')) sh %{#{irb} -I lib -r #{NAME}} end erubi-1.13.1/erubi.gemspec000066400000000000000000000023511473105126600153640ustar00rootroot00000000000000# frozen_string_literal: true require File.expand_path("../lib/erubi", __FILE__) Gem::Specification.new do |s| 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 lib/erubi/capture_block.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 erubi-1.13.1/lib/000077500000000000000000000000001473105126600134565ustar00rootroot00000000000000erubi-1.13.1/lib/erubi.rb000066400000000000000000000246151473105126600151210ustar00rootroot00000000000000# frozen_string_literal: true module Erubi VERSION = '1.13.1' # :nocov: if RUBY_VERSION >= '1.9' RANGE_FIRST = 0 RANGE_LAST = -1 else RANGE_FIRST = 0..0 RANGE_LAST = -1..-1 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 'erb/escape' define_method(:h, ERB::Escape.instance_method(:html_escape)) # :nocov: rescue LoadError 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 # Escape characters with their HTML/XML equivalents. def h(value) CGI.escapeHTML(value.to_s) end rescue LoadError ESCAPE_TABLE = {'&' => '&'.freeze, '<' => '<'.freeze, '>' => '>'.freeze, '"' => '"'.freeze, "'" => '''.freeze}.freeze if RUBY_VERSION >= '1.9' def h(value) value.to_s.gsub(/[&<>"']/, ESCAPE_TABLE) end else def h(value) value.to_s.gsub(/[&<>"']/){|s| ESCAPE_TABLE[s]} end end end end # :nocov: module_function :h 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 # The filename of the template, if one was given. attr_reader :filename # The variable name used for the buffer variable. attr_reader :bufvar # Initialize a new Erubi::Engine. Options: # +:bufval+ :: The value to use for the buffer variable, as a string (default '::String.new'). # +:bufvar+ :: The variable name to use for the buffer variable, as a string. # +:chain_appends+ :: Whether to chain << 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: '::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 add a frozen_string_literal: true 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 .freeze # (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 '<%'). # +:literal_postfix+ :: The postfix to output when using escaped tag delimiters (default '%>'). # +: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] || 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] 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; " else @escapefunc = '::Erubi.h' end end src << preamble pos = 0 is_bol = true input.scan(regexp) do |indicator, code, tailch, rspace| match = Regexp.last_match len = match.begin(0) - pos text = input[pos, len] pos = match.end(0) ch = indicator ? indicator[RANGE_FIRST] : nil lspace = nil unless ch == '=' if text.empty? lspace = "" if is_bol elsif text[RANGE_LAST] == "\n" lspace = "" else rindex = text.rindex("\n") if rindex range = rindex+1..-1 s = text[range] if /\A[ \t]*\z/.send(MATCH_METHOD, s) lspace = s text[range] = '' end else if is_bol && /\A[ \t]*\z/.send(MATCH_METHOD, text) lspace = text text = '' end end end end is_bol = rspace add_text(text) case ch when '=' rspace = nil if tailch && !tailch.empty? add_expression(indicator, code) add_text(rspace) if rspace when nil, '-' if trim && lspace && rspace add_code("#{lspace}#{code}#{rspace}") else add_text(lspace) if lspace add_code(code) add_text(rspace) if rspace end when '#' n = code.count("\n") + (rspace ? 1 : 0) if trim && lspace && rspace add_code("\n" * n) else add_text(lspace) if lspace add_code("\n" * n) add_text(rspace) if rspace end when '%' add_text("#{lspace}#{literal_prefix}#{code}#{tailch}#{literal_postfix}#{rspace}") else handle(indicator, code, tailch, rspace, lspace) end end rest = pos == 0 ? input : input[pos..-1] add_text(rest) src << "\n" unless src[RANGE_LAST] == "\n" add_postamble(postamble) src << "; ensure\n " << bufvar << " = __original_outvar\nend\n" if properties[:ensure] src.freeze freeze end private if RUBY_VERSION >= '2.3' def _dup_string_if_frozen(string) +string end # :nocov: else def _dup_string_if_frozen(string) string.frozen? ? string.dup : string end end # :nocov: # 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) return if text.empty? text = _dup_string_if_frozen(text) text.gsub!(/['\\]/, '\\\\\&') 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, # escaping it based on the indicator given and escape flag. def add_expression(indicator, code) if ((indicator == '=') ^ @escape) add_expression_result(code) else add_expression_result_escaped(code) end end # Add the result of Ruby expression to the template def add_expression_result(code) with_buffer{@src << ' << (' << code << ').to_s'} end # Add the escaped result of Ruby expression to the template def add_expression_result_escaped(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) 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 erubi-1.13.1/lib/erubi/000077500000000000000000000000001473105126600145645ustar00rootroot00000000000000erubi-1.13.1/lib/erubi/capture_block.rb000066400000000000000000000043621473105126600177330ustar00rootroot00000000000000# frozen_string_literal: true require 'erubi' module Erubi # An engine class that supports capturing blocks via the <%= and <%== tags: # # <%= upcase_form do %> # <%= 'foo' %> # <% end %> # # Where +upcase_form+ is defined like: # # def upcase_form(&block) # "
#{@bufvar.capture(&block).upcase}
" # end # # With output being: # #
# FOO #
# # This requires using a string subclass as the buffer value, provided by the # CaptureBlockEngine::Buffer class. # # This engine does not support the :escapefunc option. To change the escaping function, # use a subclass of CaptureBlockEngine::Buffer and override the #| method. # # This engine does not support the :chain_appends option, and ignores it if present. class CaptureBlockEngine < Engine class Buffer < ::String # Convert argument to string when concatening def <<(v) concat(v.to_s) end # Escape argument using Erubi.h then then concatenate it to the receiver. def |(v) concat(h(v)) end # Temporarily clear the receiver before yielding to the block, yield the # given args to the block, return any data captured by the receiver, and # restore the original data the receiver contained before returning. def capture(*args) prev = dup replace("") # 1.8 support! yield(*args) dup ensure replace(prev) end private if RUBY_VERSION >= '2' define_method(:h, ::Erubi.instance_method(:h)) # :nocov: else def h(v) ::Erubi.h(v) end end # :nocov: end def initialize(input, properties={}) properties = Hash[properties] properties[:bufval] ||= '::Erubi::CaptureBlockEngine::Buffer.new' properties[:chain_appends] = false super end private def add_expression_result(code) add_expression_op(' <<= ', code) end def add_expression_result_escaped(code) add_expression_op(' |= ', code) end def add_expression_op(op, code) check = /\A\s*\z/.send(MATCH_METHOD, code) ? "''" : '' with_buffer{@src << op << check << code} end end end erubi-1.13.1/lib/erubi/capture_end.rb000066400000000000000000000050531473105126600174050ustar00rootroot00000000000000# frozen_string_literal: true require 'erubi' module Erubi # An engine class that supports capturing blocks via the <%|= and <%|== tags, # explicitly ending the captures using <%| end %> 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, # 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 # 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, # and therefore a different object will be returned. def initialize(input, properties={}) properties = Hash[properties] escape = properties.fetch(:escape){properties.fetch(:escape_html, false)} @escape_capture = properties.fetch(:escape_capture, escape) @yield_returns_buffer = properties.fetch(:yield_returns_buffer, false) @bufval = properties[:bufval] ||= '::String.new' @bufstack = '__erubi_stack' properties[:regexp] ||= /<%(\|?={1,2}|-|\#|%|\|)?(.*?)([-=])?%>([ \t]*\r?\n)?/m super end private # Handle the <%|= and <%|== tags def handle(indicator, code, tailch, rspace, lspace) case indicator when '|=', '|==' rspace = nil if tailch && !tailch.empty? add_text(lspace) if lspace escape_capture = !((indicator == '|=') ^ @escape_capture) 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 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 end end end end erubi-1.13.1/test/000077500000000000000000000000001473105126600136675ustar00rootroot00000000000000erubi-1.13.1/test/test.rb000066400000000000000000000632541473105126600152050ustar00rootroot00000000000000require 'rubygems' unless defined?(TESTDIR) TESTDIR = File.dirname(__FILE__) LIBDIR = TESTDIR == '.' ? '../lib' : File.dirname(TESTDIR) + '/lib' $: << TESTDIR $: << LIBDIR end if ENV['COVERAGE'] require 'coverage' require 'simplecov' ENV.delete('COVERAGE') SimpleCov.instance_eval do enable_coverage :branch start do add_filter "/test/" add_group('Missing'){|src| src.covered_percent < 100} add_group('Covered'){|src| src.covered_percent == 100} end end end require 'erubi' require 'erubi/capture_end' require 'erubi/capture_block' ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins gem 'minitest' require 'minitest/global_expectations/autorun' describe 'Erubi' do before do @options = {} end def check_output(input, src, result, &block) t = @engine.new(input, @options) tsrc = t.src eval(tsrc, block.binding).must_equal result return if @engine <= Erubi::CaptureBlockEngine && !@check_src strip_freeze = defined?(@strip_freeze) ? @strip_freeze : RUBY_VERSION >= '2.1' tsrc = tsrc.gsub(/\.freeze/, '') if strip_freeze tsrc.must_equal src end def setup_foo @foo = Object.new @foo.instance_variable_set(:@t, self) def self.a; @a; end def @foo.bar @t.a << "a" yield @t.a << 'b' @t.a.buffer.upcase! end end def setup_bar if @engine <= Erubi::CaptureBlockEngine def self.bar(&block) "a#{@a.capture(&block)}b".upcase end def self.baz(&block) "c#{@a.capture(&block)}d" * 2 end def self.quux(&block) v = 3.times.map do |i| "c#{i}#{@a.capture(i, &block)}d#{i}" end.join "a#{v}b".upcase end else def self.bar @a << "a" yield @a << 'b' @a.upcase end def self.baz @a << "c" yield @a << 'd' @a * 2 end def self.quux @a << "a" 3.times do |i| @a << "c#{i}" yield i @a << "d#{i}" end @a << "b" @a.upcase end end end {'Engine'=>Erubi::Engine, 'CaptureEndEngine'=>Erubi::CaptureEndEngine, 'CaptureBlockEngine'=>Erubi::CaptureBlockEngine}.each do |desc, engine| describe desc do before do @engine = engine end it "should handle no tags with frozen source" do check_output(<"2'] check_output(< <% i = 0 list.each_with_index do |item, i| %> <%= i+1 %> <%== item %> <% end %> <%== i+1 %> END1 _buf = ::String.new; _buf << ' '; i = 0 list.each_with_index do |item, i| _buf << ' '; end _buf << '
'; _buf << ( i+1 ).to_s; _buf << ' '; _buf << ::Erubi.h(( item )); _buf << '
'; _buf << ::Erubi.h(( i+1 )); _buf << ' '; _buf.to_s END2
1 &'<>"2
1 END3 end it "should escape all backslashes and apostrophes in text" do list = list = ['&\'<>"2'] check_output(< ' ' \\ \\ <% i = 0 list.each_with_index do |item, i| %> <%= i+1 -%> <%== item %> <% end %> <%== i+1 %> <% %> END1 _buf = ::String.new; _buf << '\\' \\' \\\\ \\\\ '; i = 0 list.each_with_index do |item, i| _buf << ' '; end _buf << '
'; _buf << ( i+1 ).to_s; _buf << ' '; _buf << ::Erubi.h(( item )); _buf << '
'; _buf << ::Erubi.h(( i+1 )); _buf << ' '; _buf.to_s END2 ' ' \\ \\
1 &'<>"2
1 END3 end it "should strip only whitespace for <%, <%- and <%# tags" do check_output(< a <%- a = 2 %> b <%# a = 3 %> c /<% a = 1 %> a / <%- a = 2 %> b //<%# a = 3 %> c <% a = 1 %> / a <%- a = 2 %>/ b <%# a = 3 %>// c END1 _buf = ::String.new; a = 1 _buf << 'a '; a = 2 _buf << 'b '; _buf << 'c /'; a = 1 ; _buf << ' '; _buf << 'a / '; a = 2 ; _buf << ' '; _buf << 'b //'; _buf << ' '; _buf << 'c '; _buf << ' '; a = 1 ; _buf << ' / a '; _buf << ' '; a = 2 ; _buf << '/ b '; _buf << ' ';; _buf << '// c '; _buf.to_s END2 a b c / a / b // c / a / b // c END3 end it "should handle ensure option" do list = list = ['&\'<>"2'] @options[:ensure] = true @options[:bufvar] = '@a' @a = 'bar' check_output(< <% i = 0 list.each_with_index do |item, i| %> <%= i+1 %> <%== item %> <% end %> <%== i+1 %> END1 begin; __original_outvar = @a#{' if defined?(@a)' if RUBY_VERSION < '3'}; @a = ::String.new; @a << ' '; i = 0 list.each_with_index do |item, i| @a << ' '; end @a << '
'; @a << ( i+1 ).to_s; @a << ' '; @a << ::Erubi.h(( item )); @a << '
'; @a << ::Erubi.h(( i+1 )); @a << ' '; @a.to_s ; ensure @a = __original_outvar end END2
1 &'<>"2
1 END3 @a.must_equal 'bar' end it "should handle chain_appends option" do @options[:chain_appends] = true list = list = ['&\'<>"2'] check_output(< <% i = 0 list.each_with_index do |item, i| %> <%= i+1 %> <%== item %> <% end %> <%== i+1 %> END1 _buf = ::String.new;; _buf << ' '; i = 0 list.each_with_index do |item, i| ; _buf << ' '; end ; _buf << '
' << ( i+1 ).to_s << ' ' << ::Erubi.h(( item )) << '
' << ::Erubi.h(( i+1 )) << ' ' ; _buf.to_s END2
1 &'<>"2
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(< <% i = 0 list.each_with_index do |item, i| %> <%= i+1 %> <%== item %> <% end %> <%== i+1 %> END1 _buf = ::String.new; _buf << ' '.freeze; i = 0 list.each_with_index do |item, i| _buf << ' '.freeze; end _buf << '
'.freeze; _buf << ( i+1 ).to_s; _buf << ' '.freeze; _buf << ::Erubi.h(( item )); _buf << '
'.freeze; _buf << ::Erubi.h(( i+1 )); _buf << ' '.freeze; _buf.to_s END2
1 &'<>"2
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(< <% i = 0 list.each_with_index do |item, i| %> <%= i+1 %> <%== item %> <% end %> <%== i+1 %> END1 _buf = ::String.new; _buf << ' '; i = 0 list.each_with_index do |item, i| _buf << ' '; end _buf << '
'; _buf << ( i+1 ).to_s; _buf << ' '; _buf << ::Erubi.h(( item )); _buf << '
'; _buf << ::Erubi.h(( i+1 )); _buf << ' '; _buf.to_s END2
1 &'<>"2
1 END3 end it "should handle ensure option with no bufvar" do list = list = ['&\'<>"2'] @options[:ensure] = true check_output(< <% i = 0 list.each_with_index do |item, i| %> <%= i+1 %> <%== item %> <% end %> <%== i+1 %> END1 begin; __original_outvar = _buf if defined?(_buf); _buf = ::String.new; _buf << ' '; i = 0 list.each_with_index do |item, i| _buf << ' '; end _buf << '
'; _buf << ( i+1 ).to_s; _buf << ' '; _buf << ::Erubi.h(( item )); _buf << '
'; _buf << ::Erubi.h(( i+1 )); _buf << ' '; _buf.to_s ; ensure _buf = __original_outvar end END2
1 &'<>"2
1 END3 end [:outvar, :bufvar].each do |var| it "should handle :#{var} and :freeze options" do @options[var] = "@_out_buf" @options[:freeze] = true @items = [2] i = i = 0 check_output(< <% for item in @items %> <%= i+1 %> <%== item %> <% end %> END1 # frozen_string_literal: true @_out_buf = ::String.new; @_out_buf << ' '; for item in @items @_out_buf << ' '; end @_out_buf << '
'; @_out_buf << ( i+1 ).to_s; @_out_buf << ' '; @_out_buf << ::Erubi.h(( item )); @_out_buf << '
'; @_out_buf.to_s END2
1 2
END3 end end it "should handle <%% and <%# syntax" do @items = [2] i = i = 0 check_output(< <%% for item in @items %> <%# i+1 %> <%# item %> <%% end %> END1 _buf = ::String.new; _buf << ' '; _buf << '<% for item in @items %> '; _buf << ' '; _buf << ' <% end %> '; _buf << '
';; _buf << ' ';; _buf << '
'; _buf.to_s END2 <% for item in @items %> <% end %>
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(< <%% for item in @items %> <%% end %> <%%= "literal" %> END1 _buf = ::String.new; _buf << ' '; _buf << ' {% for item in @items %} '; _buf << ' '; _buf << ' {% end %} '; _buf << ' {%= "literal" %} '; _buf << '
'; _buf.to_s END2 {% for item in @items %} {% end %} {%= "literal" %}
END3 end it "should handle :trim => false option" do @options[:trim] = false @items = [2] i = i = 0 check_output(< <% for item in @items %> <%# i+1 %> <%== item %> <% end %><%#%> <% i = 1 %>a <% i = 1 %> END1 _buf = ::String.new; _buf << ' '; _buf << ' '; for item in @items ; _buf << ' '; _buf << ' '; _buf << ' '; end ; _buf << ' '; _buf << ' '; i = 1 ; _buf << 'a '; _buf << ' '; i = 1 ; _buf << ' '; _buf << '
'; _buf << ' '; _buf << ::Erubi.h(( item )); _buf << '
'; _buf.to_s END2 a
2
END3 end [:escape, :escape_html].each do |opt| it "should handle :#{opt} and :escapefunc options" do @options[opt] = true @options[:escapefunc] = 'h.call' h = h = proc{|s| s.to_s*2} list = list = ['2'] check_output(< <% i = 0 list.each_with_index do |item, i| %> <%= i+1 %> <%== item %> <% end %> <%== i+1 %> END1 _buf = ::String.new; _buf << ' '; i = 0 list.each_with_index do |item, i| _buf << ' '; end _buf << '
'; _buf << h.call(( i+1 )); _buf << ' '; _buf << ( item ).to_s; _buf << '
'; _buf << ( i+1 ).to_s; _buf << ' '; _buf.to_s END2
11 2
1 END3 end end unless engine == Erubi::CaptureBlockEngine it "should handle :escape option without :escapefunc option" do @options[:escape] = true list = list = ['&\'<>"2'] check_output(< <% i = 0 list.each_with_index do |item, i| %> <%== i+1 %> <%= item %> <% end %> END1 __erubi = ::Erubi; _buf = ::String.new; _buf << ' '; i = 0 list.each_with_index do |item, i| _buf << ' '; end _buf << '
'; _buf << ( i+1 ).to_s; _buf << ' '; _buf << __erubi.h(( item )); _buf << '
'; _buf.to_s END2
1 &'<>"2
END3 end it "should handle :preamble and :postamble options" do @options[:preamble] = '_buf = String.new("1");' @options[:postamble] = "_buf[0...18]\n" list = list = ['2'] check_output(< <% i = 0 list.each_with_index do |item, i| %> <%= i+1 %> <%== item %> <% end %> <%== i+1 %> END1 _buf = String.new("1"); _buf << ' '; i = 0 list.each_with_index do |item, i| _buf << ' '; end _buf << '
'; _buf << ( i+1 ).to_s; _buf << ' '; _buf << ::Erubi.h(( item )); _buf << '
'; _buf << ::Erubi.h(( i+1 )); _buf << ' '; _buf[0...18] END2 1 END3 end unless engine == Erubi::CaptureBlockEngine it "should have working filename accessor" do engine.new('', :filename=>'foo.rb').filename.must_equal 'foo.rb' end it "should have working bufvar accessor" do engine.new('', :bufvar=>'foo').bufvar.must_equal 'foo' engine.new('', :outvar=>'foo').bufvar.must_equal 'foo' end it "should work with BasicObject methods" do c = Class.new(BasicObject) c.class_eval("def a; #{engine.new('2').src} end") c.new.a.must_equal '2' end if defined?(BasicObject) it "should return frozen object" do engine.new('').frozen?.must_equal true end it "should have frozen src" do engine.new('').src.frozen?.must_equal true end it "should raise an error if a tag is not handled when a custom regexp is used" do proc{engine.new('<%] %>', :regexp =>/<%(={1,2}|\]|-|\#|%)?(.*?)([-=])?%>([ \t]*\r?\n)?/m)}.must_raise ArgumentError end end end # Hack to allow CaptureEndEngine tests to pass with minimal changes on CaptureBlock engine capture_block_engine = Class.new(::Erubi::CaptureBlockEngine) do def initialize(input, opts={}) input = input.gsub('<%|', '<%') super end end { ::Erubi::CaptureEndEngine=>'CaptureEndEngine', capture_block_engine=>'CaptureBlockEngine' }.each do |engine, desc| describe desc do before do @engine = engine end if engine == capture_block_engine def self.it(desc) desc = desc.gsub('<%|', '<%') super end else space = ' ' nl = "\n" end it "should handle trailing rspace with - modifier in <%|= and <%|" do eval(@engine.new("<%|= '&' -%>\n<%| -%>\n").src).must_equal '&' end it "should handle lspace in <%|=" do eval(@engine.new("<%|= %><%| %><%|= %><%| %>").src).must_equal '' end it "should have <%|= not escape by default" do eval(@engine.new('<%|= "&" %><%| %>').src).must_equal '&' eval(@engine.new('<%|= "&" %><%| %>', :escape=>false).src).must_equal '&' eval(@engine.new('<%|= "&" %><%| %>', :escape=>true).src).must_equal '&' end it "should have <%|== escape by default" do eval(@engine.new('<%|== "&" %><%| %>').src).must_equal '&' eval(@engine.new('<%|== "&" %><%| %>', :escape=>true).src).must_equal '&' eval(@engine.new('<%|== "&" %><%| %>', :escape=>false).src).must_equal '&' end it "should have <%|= and <%|== respect :escape_capture option" do eval(@engine.new('<%|= "&" %><%| %>', :escape_capture=>false).src).must_equal '&' eval(@engine.new('<%|= "&" %><%| %>', :escape_capture=>true).src).must_equal '&' eval(@engine.new('<%|== "&" %><%| %>', :escape_capture=>true).src).must_equal '&' eval(@engine.new('<%|== "&" %><%| %>', :escape_capture=>false).src).must_equal '&' end unless engine == capture_block_engine [['', false], ['=', true]].each do |ind, escape| it "should allow <%|=#{ind} and <%| for capturing with :escape_capture => #{escape} and :escape => #{!escape}" do @options[:bufvar] = '@a' @options[:capture] = true @options[:escape_capture] = escape @options[:escape] = !escape setup_bar check_output(< <%|=#{ind} bar do %> <%=#{ind} '&' %> <%| end %>
END1 #{'__erubi = ::Erubi; ' unless escape}@a = ::String.new; @a << ' '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << (( bar do @a << ' '; @a << ' '; @a << #{!escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << ' '; @a << ' '; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << ' '; @a << '
'; @a.to_s END2 A & B
END3 end end unless engine == capture_block_engine [['', true], ['=', false]].each do |ind, escape| it "should allow <%|=#{ind} and <%| for capturing when with :escape => #{escape}" do @options[:bufvar] = '@a' @options[:escape] = escape setup_bar check_output(< <%|=#{ind} bar do %> <%=#{ind} '&' %> <%| end %> END1 #{'__erubi = ::Erubi; ' if escape}@a = ::String.new; @a << ' '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << #{escape ? '__erubi' : '::Erubi'}.h(( bar do @a << ' '; @a << ' '; @a << #{escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << ' '; @a << ' '; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << ' '; @a << '
'; @a.to_s END2 A <B>&AMP;</B> #{space}B#{nl}
END3 end it "should handle loops in <%|=#{ind} and <%| for capturing when with :escape => #{escape}" do @options[:bufvar] = '@a' @options[:escape] = escape setup_bar check_output(< <%|=#{ind} quux do |i| %> <%=#{ind} "\#{i}&" %> <%| end %> END1 #{'__erubi = ::Erubi; ' if escape}@a = ::String.new; @a << ' '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << #{escape ? '__erubi' : '::Erubi'}.h(( quux do |i| @a << ' '; @a << ' '; @a << #{escape ? '__erubi' : '::Erubi'}.h(( "\#{i}&" )); @a << ' '; @a << ' '; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << ' '; @a << '
'; @a.to_s END2 AC0 <B>0&AMP;</B> #{space}D0C1 <B>1&AMP;</B> #{space}D1C2 <B>2&AMP;</B> #{space}D2B#{nl}
END3 end it "should allow <%|=#{ind} and <%| for nested capturing when with :escape => #{escape}" do @options[:bufvar] = '@a' @options[:escape] = escape setup_bar check_output(< <%|=#{ind} bar do %> <%=#{ind} '&' %> <%|=#{ind} baz do %>e<%| end %> <%| end %> END1 #{'__erubi = ::Erubi; ' if escape}@a = ::String.new; @a << ' '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << #{escape ? '__erubi' : '::Erubi'}.h(( bar do @a << ' '; @a << ' '; @a << #{escape ? '__erubi' : '::Erubi'}.h(( '&' )); @a << ' '; @a << ' ';begin; (__erubi_stack ||= []) << @a; @a = ::String.new; __erubi_stack.last << #{escape ? '__erubi' : '::Erubi'}.h(( baz do @a << 'e'; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << ' '; @a << ' '; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << ' '; @a << '
'; @a.to_s END2 A <B>&AMP;</B> CEDCED #{space}B#{nl}
END3 end end next if engine == capture_block_engine it "should respect the :yield_returns_buffer option for making templates return the (potentially modified) buffer" do @options[:bufvar] = '@a' def self.bar a = String.new a << "a" yield 'burgers' case b = (yield 'salads') when String a << b a << 'b' a.upcase end end check_output(< Let's eat <%= item %>! <% 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 ; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << ' '; @a.to_s END2 END3 @options[:yield_returns_buffer] = true check_output(< Let's eat <%= item %>! <% 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 << '! '; i = i = nil ; @a; end )).to_s; ensure; @a = __erubi_stack.pop; end; @a << ' '; @a.to_s END2 A LET'S EAT BURGERS! LET'S EAT SALADS! B END3 end it "should respect the :yield_returns_buffer option for making templates return the (potentially modified) buffer as the result of the block" do @options[:yield_returns_buffer] = true def self.bar(foo = nil) if foo.nil? yield else foo end end check_output(< Let's eat the tacos! <%| end %> Delicious! END1 _buf = ::String.new;begin; (__erubi_stack ||= []) << _buf; _buf = ::String.new; __erubi_stack.last << (( bar do _buf << ' '; _buf << 'Let\\'s eat the tacos! '; _buf; end )).to_s; ensure; _buf = __erubi_stack.pop; end; _buf << ' '; _buf << ' Delicious! '; _buf.to_s END2 Let's eat the tacos! Delicious! END3 check_output(< Let's eat burgers! <%| end %> Delicious! END1 _buf = ::String.new;begin; (__erubi_stack ||= []) << _buf; _buf = ::String.new; __erubi_stack.last << (( bar(\"Don't eat the burgers!\") do _buf << ' '; _buf << 'Let\\'s eat burgers! '; _buf; end )).to_s; ensure; _buf = __erubi_stack.pop; end; _buf << ' '; _buf << ' Delicious! '; _buf.to_s END2 Don't eat the burgers! Delicious! END3 end end end describe 'Erubi::CaptureBlockEngine' do before do @engine = Erubi::CaptureBlockEngine @check_src = true end it "should handle empty tags by concatening empty string" do check_output(<<%== %> END1 _buf = ::Erubi::CaptureBlockEngine::Buffer.new; _buf <<= '' ; _buf |= '' ; _buf << ' '; _buf.to_s END2 END3 end it "should work as specified in documentation" do @options[:bufvar] = '@a' def self.upcase_form(&block) "
#{@a.capture(&block).upcase}
" end check_output(< <%= 'foo' %> <% end %> END1 @a = ::Erubi::CaptureBlockEngine::Buffer.new; @a <<= upcase_form do ; @a << ' '; @a << ' '; @a <<= 'foo' ; @a << ' '; end @a << ' '; @a.to_s END2
FOO
END3 end [['', true], ['=', false]].each do |ind, escape| it "should allow <%=#{ind} and <% for escaped capturing with :escape => #{escape}" do @options[:bufvar] = '@a' @options[:escape] = escape setup_bar check_output(< <%=#{ind} bar do %> <%=#{ind} '&' %> <% end %> END1 #{'__erubi = ::Erubi; ' if escape}@a = ::Erubi::CaptureBlockEngine::Buffer.new; @a << ' '; @a |= bar do ; @a << ' '; @a << ' '; @a |= '&' ; @a << ' '; end @a << '
'; @a.to_s END2 A <B>&AMP;</B> B
END3 end it "should allow <%=#{ind} and <% for unescaped capturing when with :escape => #{!escape}" do @options[:bufvar] = '@a' @options[:escape] = !escape setup_bar check_output(< <%=#{ind} bar do %> <%=#{ind} '&' %> <% end %> END1 #{'__erubi = ::Erubi; ' if !escape}@a = ::Erubi::CaptureBlockEngine::Buffer.new; @a << ' '; @a <<= bar do ; @a << ' '; @a << ' '; @a <<= '&' ; @a << ' '; end @a << '
'; @a.to_s END2 A & B
END3 end end end end