class Erubi::Engine

def add_code(code)

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

def add_expression(indicator, code)

escaping it based on the indicator given and escape flag.
Add the given ruby expression result to the template,
def add_expression(indicator, code)
  if ((indicator == '=') ^ @escape)
    add_expression_result(code)
  else
    add_expression_result_escaped(code)
  end
end

def add_expression_result(code)

Add the result of Ruby expression to the template
def add_expression_result(code)
  with_buffer{@src << ' << (' << code << ').to_s'}
end

def add_expression_result_escaped(code)

Add the escaped result of Ruby expression to the template
def add_expression_result_escaped(code)
  with_buffer{@src << ' << ' << @escapefunc << '((' << code << '))'}
end

def add_postamble(postamble)

to make additional changes to src that depend on the current state.
Add the given postamble to the src. Can be overridden in subclasses
def add_postamble(postamble)
  terminate_expression
  @src << postamble
end

def add_text(text)

Must be called with a string, cannot be called with nil (Rails's subclass depends on it).
Add raw text to the template. Modifies argument if argument is mutable as a memory optimization.
def add_text(text)
  return if text.empty?
  if text.frozen?
    text = text.gsub(/['\\]/, '\\\\\&')
  else
    text.gsub!(/['\\]/, '\\\\\&')
  end
  with_buffer{@src << " << '" << text << @text_end}
end

def handle(indicator, code, tailch, rspace, lspace)

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

def initialize(input, properties={})

+:trim+ :: Whether to trim leading and trailing whitespace, true by default.
+:src+ :: The initial value to use for the source code, an empty string by default.
+:regexp+ :: The regexp to use for scanning.
+:preamble+ :: The preamble for the template, by default initializes the buffer variable.
+:postamble+ :: The postamble for the template, by default returns the resulting source code.
+:outvar+ :: Same as +:bufvar+, with lower priority.
+:literal_postfix+ :: The postfix to output when using escaped tag delimiters (default '%>').
+:literal_prefix+ :: The prefix to output when using escaped tag delimiters (default '<%').
in order to improve performance.
Can be set to +false+ on Ruby 2.3+ when frozen string literals are enabled
(default: +true+ on Ruby 2.1+, +false+ on Ruby 2.0 and older).
+:freeze_template_literals+ :: Whether to suffix all literal strings for template code with .freeze
the file, and having the magic comment later in the file can trigger warnings.
source code in other code, because the magic comment only has an effect at the beginning of
the resulting source code. Note this may cause problems if you are wrapping the resulting
+:freeze+ :: Whether to enable add a frozen_string_literal: true magic comment at the top of
+:filename+ :: The filename for the template.
+:escape_html+ :: Same as +:escape+, with lower priority.
+:escape+ :: Whether to make <%= escape by default, and <%== not escape by default.
+:escapefunc+ :: The function to use for escaping, as a string (default: '::Erubi.h').
+:ensure+ :: Wrap the template in a begin/ensure block restoring the previous value of bufvar.
template rendering (default +false+).
performance, but can cause issues when the buffer variable is reassigned during
+:chain_appends+ :: Whether to chain << calls to the buffer variable. Offers better
+:bufvar+ :: The variable name to use for the buffer variable, as a string.
+:bufval+ :: The value to use for the buffer variable, as a string (default '::String.new').
Initialize a new Erubi::Engine. Options:
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

def terminate_expression

terminated.
the chain_appends option is used, expressions may not be
The default is to terminate all expressions, but when
Make sure that any current expression has been terminated.
def terminate_expression
  @src << '; ' if @chain_appends
end

def with_buffer

code where << will append to the bufvar.
This method should only be called if the block will result in

of the next append after the block executes.
before yielding to the block. Mark that the buffer is the target
Make sure the buffer variable is the target of the next append
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