Class Tenjin::Template
In: lib/tenjin.rb
Parent: Object

template class

ex. file ‘example.rbhtml‘

  <html>
   <body>
    <h1>${@title}</h1>
    <ul>
    <?rb i = 0 ?>
    <?rb for item in @items ?>
    <?rb   i += 1 ?>
      <li>#{i} : ${item}</li>
    <?rb end ?>
    </ul>
   </body>
  </html>

ex. convertion

  require 'tenjin'
  template = Tenjin::Template.new('example.rbhtml')
  print template.script
  ## or
  # template = Tenjin::Template.new()
  # print template.convert_file('example.rbhtml')
  ## or
  # template = Tenjin::Template.new()
  # fname = 'example.rbhtml'
  # print template.convert(File.read(fname), fname)  # filename is optional

ex. evaluation

  context = {:title=>'Tenjin Example', :items=>['foo', 'bar', 'baz'] }
  output = template.render(context)
  ## or
  # context = Tenjin::Context(:title=>'Tenjin Example', :items=>['foo','bar','baz'])
  # output = template.render(context)
  ## or
  # output = template.render(:title=>'Tenjin Example', :items=>['foo','bar','baz'])
  print output

Methods

Constants

ESCAPE_FUNCTION = 'escape'
STMT_PATTERN = self.compile_stmt_pattern('rb')

Attributes

args  [RW] 
escapefunc  [RW] 
filename  [RW] 
initbuf  [RW] 
newline  [RW] 
script  [RW] 
timestamp  [RW] 

Public Class methods

initializer of Template class.

options:

:escapefunc :function name to escape value (default ‘escape’)
:preamble :preamble such as "_buf = ’’" (default nil)
:postamble :postamble such as "_buf.to_s" (default nil)

[Source]

# File lib/tenjin.rb, line 327
    def initialize(filename=nil, options={})
      if filename.is_a?(Hash)
        options = filename
        filename = nil
      end
      @filename   = filename
      @escapefunc = options[:escapefunc] || ESCAPE_FUNCTION
      @preamble   = options[:preamble]  == true ? "_buf = #{init_buf_expr()}; " : options[:preamble]
      @postamble  = options[:postamble] == true ? "_buf.to_s"   : options[:postamble]
      @args       = nil  # or array of argument names
      convert_file(filename) if filename
    end

Protected Class methods

[Source]

# File lib/tenjin.rb, line 381
    def self.compile_stmt_pattern(pi)
      return /<\?#{pi}( |\t|\r?\n)(.*?) ?\?>([ \t]*\r?\n)?/m
    end

Public Instance methods

convert string into ruby code

[Source]

# File lib/tenjin.rb, line 349
    def convert(input, filename=nil)
      @input = input
      @filename = filename
      @proc = nil
      pos = input.index(?\n)
      if pos && input[pos-1] == ?\r
        @newline = "\r\n"
        @newlinestr = '\\r\\n'
      else
        @newline = "\n"
        @newlinestr = '\\n'
      end
      before_convert()
      parse_stmts(input)
      after_convert()
      return @script
    end

convert file into ruby code

[Source]

# File lib/tenjin.rb, line 344
    def convert_file(filename)
      return convert(File.read(filename), filename)
    end

evaluate converted ruby code and return it. argument ‘_context’ should be a Hash object or Context object.

[Source]

# File lib/tenjin.rb, line 612
    def render(context=Context.new)
      context = Context.new(context) if context.is_a?(Hash)
      @proc ||= _render()
      return context.instance_eval(&@proc)
    end

Protected Instance methods

add expression code

[Source]

# File lib/tenjin.rb, line 587
    def add_expr(code, flag_escape=nil)
      return if !code || code.empty?
      @script << (flag_escape ? "\#{#{@escapefunc}((#{code}).to_s)}" : "\#{#{code}}")
    end

add statement code

[Source]

# File lib/tenjin.rb, line 593
    def add_stmt(code)
      @script << code
    end

add text string

[Source]

# File lib/tenjin.rb, line 569
    def add_text(text, encode_newline=false)
      return unless text && !text.empty?
      if encode_newline && text[-1] == ?\n
        text.chomp!
        @script << escape_str(text) << @newlinestr
      else
        @script << escape_str(text)
      end
    end

hook method called after convert()

[Source]

# File lib/tenjin.rb, line 376
    def after_convert()
      @script << @newline unless @script[-1] == ?\n
      @script << @postamble << @newline if @postamble
    end

hook method called before convert()

[Source]

# File lib/tenjin.rb, line 370
    def before_convert()
      @script = ''
      @script << @preamble if @preamble
    end

escape ’\’ and ’`’ into ’\\’ and ’\`’

[Source]

# File lib/tenjin.rb, line 580
    def escape_str(str)
      str.gsub!(/[`\\]/, '\\\\\&')
      str.gsub!(/\r\n/, "\\r\r\n") if @newline == "\r\n"
      return str
    end

[Source]

# File lib/tenjin.rb, line 454
    def expr_pattern
      #return /([\#$])\{(.*?)\}/
      return /(\$)\{(.*?)\}/m
      #return /\$\{.*?\}/
    end

ex. get_expr_and_escapeflag(’$’, ‘item[:name]’) => ‘item[:name]’, true

[Source]

# File lib/tenjin.rb, line 461
    def get_expr_and_escapeflag(matched)
      return matched[2], matched[1] == '$'
    end

parse expressions (’#{…}’ and ’${…}’)

[Source]

# File lib/tenjin.rb, line 466
    def parse_exprs(input)
      return if !input or input.empty?
      pos = 0
      start_text_part()
      input.scan(expr_pattern()) do
        m = Regexp.last_match
        text = input[pos, m.begin(0) - pos]
        pos = m.end(0)
        expr, flag_escape = get_expr_and_escapeflag(m)
        #m = Regexp.last_match
        #start = m.begin(0)
        #stop  = m.end(0)
        #text  = input[pos, start - pos]
        #expr  = input[start+2, stop-start-3]
        #pos = stop
        add_text(text)
        add_expr(expr, flag_escape)
      end
      rest = $' || input
      #if !rest || rest.empty?
      #  @script << '`; '
      #elsif rest[-1] == ?\n
      #  rest.chomp!
      #  @script << escape_str(rest) << @newlinestr << '`' << @newline
      #else
      #  @script << escape_str(rest) << '`; '
      #end
      flag_newline = input[-1] == ?\n
      add_text(rest, true)
      stop_text_part()
      @script << (flag_newline ? @newline : '; ')
    end

parse statements (’<?rb … ?>’)

[Source]

# File lib/tenjin.rb, line 392
    def parse_stmts(input)
      return unless input
      is_bol = true
      prev_rspace = nil
      pos = 0
      input.scan(stmt_pattern()) do |mspace, code, rspace|
        m = Regexp.last_match
        text = input[pos, m.begin(0) - pos]
        pos = m.end(0)
        ## detect spaces at beginning of line
        lspace = nil
        if rspace.nil?
          # nothing
        elsif text.empty?
          lspace = "" if is_bol
        elsif text[-1] == ?\n
          lspace = ""
        else
          rindex = text.rindex(?\n)
          if rindex
            s = text[rindex+1..-1]
            if s =~ /\A[ \t]*\z/
              lspace = s
              text = text[0..rindex]
              #text[rindex+1..-1] = ''
            end
          else
            if is_bol && text =~ /\A[ \t]*\z/
              lspace = text
              text = nil
              #lspace = text.dup
              #text[0..-1] = ''
            end
          end
        end
        is_bol = rspace ? true : false
        ##
        text.insert(0, prev_rspace) if prev_rspace
        parse_exprs(text)
        code.insert(0, mspace) if mspace != ' '
        if lspace
          assert if rspace.nil?
          code.insert(0, lspace)
          code << rspace
          #add_stmt(code)
          prev_rspace = nil
        else
          code << ';' unless code[-1] == ?\n
          #add_stmt(code)
          prev_rspace = rspace
        end
        if code
          code = statement_hook(code)
          add_stmt(code)
        end
      end
      #rest = $' || input
      rest = pos > 0 ? input[pos..-1] : input
      rest.insert(0, prev_rspace) if prev_rspace
      parse_exprs(rest) if rest && !rest.empty?
    end

start text part

[Source]

# File lib/tenjin.rb, line 559
    def start_text_part()
      @script << " _buf << %Q`"
    end

expand macros and parse ’#@ARGS’ in a statement.

[Source]

# File lib/tenjin.rb, line 500
    def statement_hook(stmt)
      ## macro expantion
      #macro_pattern = /\A\s*(\w+)\((.*?)\);?(\s*)\z/
      #if macro_pattern =~ stmt
      #  name = $1; arg  = $2; rspace = $3
      #  handler = get_macro_handler(name)
      #  ret = handler ? handler.call(arg) + $3 : stmt
      #  return ret
      #end
      ## arguments declaration
      if @args.nil?
        args_pattern = /\A *\#@ARGS([ \t]+(.*?))?(\s*)\z/   #
        if args_pattern =~ stmt
          @args = []
          declares = ''
          rspace = $3
          if $2
            for s in $2.split(/,/)
              arg = s.strip()
              next if s.empty?
              arg =~ /\A[a-zA-Z_]\w*\z/ or raise ArgumentError.new("#{arg}: invalid template argument.")
              @args << arg
              declares << " #{arg} = @#{arg};"
            end
          end
          declares << rspace
          return declares
        end
      end
      ##
      return stmt
    end

[Source]

# File lib/tenjin.rb, line 387
    def stmt_pattern
      STMT_PATTERN
    end

stop text part

[Source]

# File lib/tenjin.rb, line 564
    def stop_text_part()
      @script << '`'
    end

[Validate]