Class: Mustache::Template

Inherits:
Object
  • Object
show all
Defined in:
lib/mustache/template.rb

Overview

A Template is a compiled version of a Mustache template.

The idea is this: when handed a Mustache template, convert it into a Ruby string by transforming Mustache tags into interpolated Ruby.

You shouldn’t use this class directly.

Defined Under Namespace

Classes: UnclosedSection

Instance Method Summary collapse

Constructor Details

#initialize(source, template_path = '.', template_extension = 'mustache') ⇒ Template

Expects a Mustache template as a string along with a template path, which it uses to find partials.



35
36
37
38
39
40
# File 'lib/mustache/template.rb', line 35

def initialize(source, template_path = '.', template_extension = 'mustache')
  @source = source
  @template_path = template_path
  @template_extension = template_extension
  @tmpid = 0
end

Instance Method Details

#compile(src = @source) ⇒ Object

Does the dirty work of transforming a Mustache template into an interpolation-friendly Ruby string.



59
60
61
# File 'lib/mustache/template.rb', line 59

def compile(src = @source)
  "\"#{compile_sections(src)}\""
end

#compile_partial(name) ⇒ Object

Partials are basically a way to render views from inside other views.



121
122
123
124
# File 'lib/mustache/template.rb', line 121

def compile_partial(name)
  src = File.read("#{@template_path}/#{name}.#{@template_extension}")
  compile(src)[1..-2]
end

#compile_sections(src) ⇒ Object

Mustache::Template.{{#sections}okay{/sections}

Sections can return true, false, or an enumerable. If true, the section is displayed. If false, the section is not displayed. If enumerable, the return value is iterated over (a ‘for` loop).



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/mustache/template.rb', line 69

def compile_sections(src)
  res = ""
  while src =~ /#{otag}\#([^\}]*)#{ctag}\s*(.+?)#{otag}\/\1#{ctag}\s*/m
    # $` = The string to the left of the last successful match
    res << compile_tags($`)
    name = $1.strip.to_sym.inspect
    code = compile($2)
    ctxtmp = "ctx#{tmpid}"
    res << ev(<<-compiled)
    if v = ctx[#{name}]
      v = [v] unless v.is_a?(Array) # shortcut when passed non-array
      v.map { |h| ctx.push(h); c = #{code}; ctx.pop; c }.join
    end
    compiled
    # $' = The string to the right of the last successful match
    src = $'
  end
  res << compile_tags(src)
end

#compile_tags(src) ⇒ Object

Find and replace all non-section tags. In particular we look for four types of tags:

  1. Escaped variable tags - {var}

  2. Unescaped variable tags - {{var}}

  3. Comment variable tags - comment

  4. Partial tags - partial_name }



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/mustache/template.rb', line 95

def compile_tags(src)
  res = ""
  while src =~ /#{otag}(#|=|!|<|>|\{)?(.+?)\1?#{ctag}+/m
    res << str($`)
    case $1
    when '#'
      # Unclosed section - raise an error and
      # report the line number
      raise UnclosedSection.new(@source, $&, $2)
    when '!'
      # ignore comments
    when '='
      self.otag, self.ctag = $2.strip.split(' ', 2)
    when '>', '<'
      res << compile_partial($2.strip)
    when '{'
      res << utag($2.strip)
    else
      res << etag($2.strip)
    end
    src = $'
  end
  res << str(src)
end

#ctagObject

}} - closing tag delimiter



146
147
148
# File 'lib/mustache/template.rb', line 146

def ctag
  @ctag ||= Regexp.escape('}}')
end

#ctag=(tag) ⇒ Object



150
151
152
# File 'lib/mustache/template.rb', line 150

def ctag=(tag)
  @ctag = Regexp.escape(tag)
end

#etag(s) ⇒ Object

{} - an escaped tag



155
156
157
# File 'lib/mustache/template.rb', line 155

def etag(s)
  ev("CGI.escapeHTML(ctx[#{s.strip.to_sym.inspect}].to_s)")
end

#ev(s) ⇒ Object

An interpolation-friendly version of a string, for use within a Ruby string.



166
167
168
# File 'lib/mustache/template.rb', line 166

def ev(s)
  "#\{#{s}}"
end

#otagObject

{{ - opening tag delimiter



137
138
139
# File 'lib/mustache/template.rb', line 137

def otag
  @otag ||= Regexp.escape('{{')
end

#otag=(tag) ⇒ Object



141
142
143
# File 'lib/mustache/template.rb', line 141

def otag=(tag)
  @otag = Regexp.escape(tag)
end

#render(context) ⇒ Object

Renders the ‘@source` Mustache template using the given `context`, which should be a simple hash keyed with symbols.



44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/mustache/template.rb', line 44

def render(context)
  # Compile our Mustache template into a Ruby string
  compiled = "def render(ctx) #{compile} end"

  # Here we rewrite ourself with the interpolated Ruby version of
  # our Mustache template so subsequent calls are very fast and
  # can skip the compilation stage.
  instance_eval(compiled, __FILE__, __LINE__ - 1)

  # Call the newly rewritten version of #render
  render(context)
end

#str(s) ⇒ Object

Get a (hopefully) literal version of an object, sans quotes



132
133
134
# File 'lib/mustache/template.rb', line 132

def str(s)
  s.inspect[1..-2]
end

#tmpidObject

Generate a temporary id, used when compiling code.



127
128
129
# File 'lib/mustache/template.rb', line 127

def tmpid
  @tmpid += 1
end

#utag(s) ⇒ Object

{{}} - an unescaped tag



160
161
162
# File 'lib/mustache/template.rb', line 160

def utag(s)
  ev("ctx[#{s.strip.to_sym.inspect}]")
end