summary refs log tree commit diff
path: root/vendor/htemplate.rb
blob: 5a429a139d5525057eb2e59d538db95dbb9d2408 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
require 'strscan'

class HTemplate
  def initialize(source, filename=nil)
    @source = source
    @filename = filename
    compile
  end

  if defined? Rack::Utils
    def escape_html(s)
      Rack::Utils.escape_html s
    end
  else
    require 'cgi'
    def escape_html(s)
      CGI.escapeHTML s
    end
  end

  def expand(data, output="", escape=nil, &block)
    escape ||= lambda { |v|
      escape_html v.to_s
    }
    @code.call(output, data, block, escape)
    output
  end

  def compile
    code = "lambda { |output, data, block, __escape| data.instance_eval {\n"

    scanner = StringScanner.new(@source)

    until scanner.eos?
      if scanner.bol? and scanner.scan(/\s*\$ (.*\n)/)
                                           # raw line of code, $ if bla
        code << scanner[1]
      elsif scanner.scan(/\$(:?)(\{(.*?)\}|(@?[\w.!?]+))/)
                                           # expression, ${foo} or $foo.bar
        expr = scanner[3] || scanner[4]

        if scanner[1] == ":"               # disable escaping?
          code << %Q{output << (#{expr}).to_s;}
        else
          code << %Q{output << __escape[#{expr}];}
        end
      elsif scanner.scan(/\$\$/)           # plain $
        code << %Q{output << '$';}
      elsif scanner.scan(/\$#.*?(?:#\$|$\n?)/)  # comment $#...#$ or $#...
        # nothing
      elsif scanner.scan(/([^\n$]+\n?)|([^\n$]*\n)/)   # text
        if scanner.matched =~ /\\$/ && scanner.bol?
          code << %Q{output << #{scanner.matched.chop.chop.dump};}
        else
          code << %Q{output << #{scanner.matched.dump};}
        end

        code << "\n"  if scanner.bol?
      else
        raise "can't parse template: #{scanner.rest[0..20].dump}"
      end
    end

    code << "}}"

    @code = eval(code, nil, @filename || '(template)', 0)
  end
end