123456789_123456789_123456789_123456789_123456789_

Class: ERB::Compiler

Do not use. This class is for internal use only.
Relationships & Source Files
Namespace Children
Classes:
Inherits: Object
Defined in: lib/erb.rb

Overview

Compiles ERB templates into Ruby code; the compiled code produces the template result when evaluated. Compiler provides hooks to define how generated output is handled.

Internally ERB does something like this to generate the code returned by #src:

compiler = ERB::Compiler.new('<>')
compiler.pre_cmd    = ["_erbout=+''"]
compiler.put_cmd    = "_erbout.<<"
compiler.insert_cmd = "_erbout.<<"
compiler.post_cmd   = ["_erbout"]

code, enc = compiler.compile("Got <%= obj %>!\n")
puts code

Generates:

#coding:UTF-8
_erbout=+''; _erbout.<< "Got ".freeze; _erbout.<<(( obj ).to_s); _erbout.<< "!\n".freeze; _erbout

By default the output is sent to the print method. For example:

compiler = ERB::Compiler.new('<>')
code, enc = compiler.compile("Got <%= obj %>!\n")
puts code

Generates:

#coding:UTF-8
print "Got ".freeze; print(( obj ).to_s); print "!\n".freeze

Evaluation

The compiled code can be used in any context where the names in the code correctly resolve. Using the last example, each of these print ‘Got It!’

Evaluate using a variable:

obj = 'It'
eval code

Evaluate using an input:

mod = Module.new
mod.module_eval %{
  def get(obj)
    #{code}
  end
}
extend mod
get('It')

Evaluate using an accessor:

klass = Class.new Object
klass.class_eval %{
  attr_accessor :obj
  def initialize(obj)
    @obj = obj
  end
  def get_it
    #{code}
  end
}
klass.new('It').get_it

Good! See also #def_method, #def_module, and #def_class.

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(trim_mode) ⇒ Compiler

Construct a new compiler using the trim_mode. See ERB.new for available trim modes.

[ GitHub ]

  
# File 'lib/erb.rb', line 694

def initialize(trim_mode)
  @percent, @trim_mode = prepare_trim_mode(trim_mode)
  @put_cmd = 'print'
  @insert_cmd = @put_cmd
  @pre_cmd = []
  @post_cmd = []
end

Instance Attribute Details

#content (rw, private)

A buffered text in #compile

[ GitHub ]

  
# File 'lib/erb.rb', line 718

attr_accessor :content

#insert_cmd (rw)

The command to handle text that is inserted prior to a newline

[ GitHub ]

  
# File 'lib/erb.rb', line 707

attr_accessor :insert_cmd

#percent (readonly)

[ GitHub ]

  
# File 'lib/erb.rb', line 701

attr_reader :percent, :trim_mode

#post_cmd (rw)

An array of commands appended to compiled code

[ GitHub ]

  
# File 'lib/erb.rb', line 713

attr_accessor :post_cmd

#pre_cmd (rw)

An array of commands prepended to compiled code

[ GitHub ]

  
# File 'lib/erb.rb', line 710

attr_accessor :pre_cmd

#put_cmd (rw)

The command to handle text that ends with a newline

[ GitHub ]

  
# File 'lib/erb.rb', line 704

attr_accessor :put_cmd

#trim_mode (readonly)

[ GitHub ]

  
# File 'lib/erb.rb', line 701

attr_reader :percent, :trim_mode

Instance Method Details

#add_insert_cmd(out, content)

[ GitHub ]

  
# File 'lib/erb.rb', line 576

def add_insert_cmd(out, content)
  out.push("#{@insert_cmd}((#{content}).to_s)")
end

#add_put_cmd(out, content)

[ GitHub ]

  
# File 'lib/erb.rb', line 572

def add_put_cmd(out, content)
  out.push("#{@put_cmd} #{content.dump}.freeze#{"\n" * content.count("\n")}")
end

#compile(s)

Compiles an ::ERB template into Ruby code. Returns an array of the code and encoding like [“code”, Encoding].

Raises:

  • (ArgumentError)
[ GitHub ]

  
# File 'lib/erb.rb', line 582

def compile(s)
  enc = s.encoding
  raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
  s = s.b # see String#b
  magic_comment = detect_magic_comment(s, enc)
  out = Buffer.new(self, *magic_comment)

  self.content = +''
  scanner = make_scanner(s)
  scanner.scan do |token|
    next if token.nil?
    next if token == ''
    if scanner.stag.nil?
      compile_stag(token, out, scanner)
    else
      compile_etag(token, out, scanner)
    end
  end
  add_put_cmd(out, content) if content.size > 0
  out.close
  return out.script, *magic_comment
end

#compile_content(stag, out)

[ GitHub ]

  
# File 'lib/erb.rb', line 642

def compile_content(stag, out)
  case stag
  when '<%'
    if content[-1] == ?\n
      content.chop!
      out.push(content)
      out.cr
    else
      out.push(content)
    end
  when '<%='
    add_insert_cmd(out, content)
  when '<%#'
    # commented out
  end
end

#compile_etag(etag, out, scanner)

[ GitHub ]

  
# File 'lib/erb.rb', line 629

def compile_etag(etag, out, scanner)
  case etag
  when '%>'
    compile_content(scanner.stag, out)
    scanner.stag = nil
    self.content = +''
  when '%%>'
    content << '%>'
  else
    content << etag
  end
end

#compile_stag(stag, out, scanner)

[ GitHub ]

  
# File 'lib/erb.rb', line 605

def compile_stag(stag, out, scanner)
  case stag
  when PercentLine
    add_put_cmd(out, content) if content.size > 0
    self.content = +''
    out.push(stag.to_s)
    out.cr
  when :cr
    out.cr
  when '<%', '<%=', '<%#'
    scanner.stag = stag
    add_put_cmd(out, content) if content.size > 0
    self.content = +''
  when "\n"
    content << "\n"
    add_put_cmd(out, content)
    self.content = +''
  when '<%%'
    content << '<%'
  else
    content << stag
  end
end

#detect_magic_comment(s, enc = nil) (private)

[ GitHub ]

  
# File 'lib/erb.rb', line 720

def detect_magic_comment(s, enc = nil)
  re = @percent ? /\G(?:<%#(.*)%>|%#(.*)\n)/ : /\G<%#(.*)%>/
  frozen = nil
  s.scan(re) do
    comment = $+
    comment = $1 if comment[/-\*-\s*(.*?)\s*-*-$/]
    case comment
    when %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
      enc = Encoding.find($1.sub(/-(?:mac|dos|unix)/i, ''))
    when %r"frozen[-_]string[-_]literal\s*:\s*([[:alnum:]]+)"
      frozen = $1
    end
  end
  return enc, frozen
end

#make_scanner(src)

[ GitHub ]

  
# File 'lib/erb.rb', line 688

def make_scanner(src) # :nodoc:
  Scanner.make_scanner(src, @trim_mode, @percent)
end

#prepare_trim_mode(mode)

[ GitHub ]

  
# File 'lib/erb.rb', line 659

def prepare_trim_mode(mode) # :nodoc:
  case mode
  when 1
    return [false, '>']
  when 2
    return [false, '<>']
  when 0, nil
    return [false, nil]
  when String
    unless mode.match?(/\A(%|-|>|<>){1,2}\z/)
      warn_invalid_trim_mode(mode, uplevel: 5)
    end

    perc = mode.include?('%')
    if mode.include?('-')
      return [perc, '-']
    elsif mode.include?('<>')
      return [perc, '<>']
    elsif mode.include?('>')
      return [perc, '>']
    else
      [perc, nil]
    end
  else
    warn_invalid_trim_mode(mode, uplevel: 5)
    return [false, nil]
  end
end

#warn_invalid_trim_mode(mode, uplevel:) (private)

[ GitHub ]

  
# File 'lib/erb.rb', line 736

def warn_invalid_trim_mode(mode, uplevel:)
  warn "Invalid ERB trim mode: #{mode.inspect} (trim_mode: nil, 0, 1, 2, or String composed of '%' and/or '-', '>', '<>')", uplevel: uplevel + 1
end