class Mustache::Generator
“Hi #{CGI.escapeHTML(ctx.to_s)}!n”
$ mustache –compile test.mustache
mustache(1) command line tool:
You can see the generated Ruby string for any template with the
“Hi #{CGI.escapeHTML(ctx.to_s)}!n”
>> puts Mustache::Generator.new.compile(tokens)
Now let’s hand that to the Generator:
[:static, “!n”]]
[:mustache, :etag, “thing”],
[:static, “Hi ”],
[:multi,
If we run this through the Parser we’ll get these tokens:
Hi {{thing}}!
For example, let’s take this template:
run our code.
because at that point we’re relying on Ruby to do the parsing and
Ruby string. This string is considered the “compiled” template
usually assembled by the Parser, and generating an interpolatable
The Generator is in charge of taking an array of Mustache tokens,
def compile(exp)
def compile(exp) "\"#{compile!(exp)}\"" end
def compile!(exp)
[:mustache, :etag, "taxed_value"],
[:static, "Well, $"],
[:multi,
"in_ca",
:section,
[:mustache,
[:static, "!\n"],
[:mustache, :etag, "value"],
[:static, "\nYou have just won $"],
[:mustache, :etag, "name"],
[:static, "Hello "],
[:multi,
tokens:
If we run this through the Parser, we'll get back this array of
{{/in_ca}}
Well, ${{taxed_value}}, after taxes.
{{#in_ca}}
You have just won ${{value}}!
Hello {{name}}
template:
To give you an idea of what you'll be dealing with take this
Any Mustache tag, from sections to partials.
:mustache
Normal HTML, the stuff outside of {{mustaches}}.
:static
Mixed bag of :static, :mustache, and whatever.
:multi
with:
particular there are three types of expressions we are concerned
Given an array of tokens, converts them into Ruby code. In
def compile!(exp) case exp.first when :multi exp[1..-1].map { |e| compile!(e) }.join when :static str(exp[1]) when :mustache send("on_#{exp[1]}", *exp[2..-1]) else raise "Unhandled exp: #{exp.first}" end end
def ev(s)
An interpolation-friendly version of a string, for use within a
def ev(s) "#\{#{s}}" end
def initialize(options = {})
def initialize(options = {}) @options = options end
def on_etag(name)
def on_etag(name) ev(<<-compiled) v = #{compile!(name)} if v.is_a?(Proc) v = Mustache::Template.new(v.call.to_s).render(ctx.dup) end ctx.escapeHTML(v.to_s) compiled end
def on_fetch(names)
def on_fetch(names) names = names.map { |n| n.to_sym } if names.length == 0 "ctx[:to_s]" elsif names.length == 1 "ctx[#{names.first.to_sym.inspect}]" else initial, *rest = names <<-compiled #{rest.inspect}.inject(ctx[#{initial.inspect}]) { |value, key| value && ctx.find(value, key) } compiled end end
def on_inverted_section(name, content, raw)
Fired when we find an inverted section. Just like `on_section`,
def on_inverted_section(name, content, raw) # Convert the tokenized content of this section into a Ruby # string we can use. code = compile(content) # Compile the Ruby for this inverted section now that we know # what's inside. ev(<<-compiled) v = #{compile!(name)} if v.nil? || v == false || v.respond_to?(:empty?) && v.empty? #{code} end compiled end
def on_partial(name, indentation)
which calls a partial at runtime instead of expanding and
Fired when the compiler finds a partial. We want to return code
def on_partial(name, indentation) ev("ctx.partial(#{name.to_sym.inspect}, #{indentation.inspect})") end
def on_section(name, content, raw)
Callback fired when the compiler finds a section token. We're
def on_section(name, content, raw) # Convert the tokenized content of this section into a Ruby # string we can use. code = compile(content) # Compile the Ruby for this section now that we know what's # inside the section. ev(<<-compiled) if v = #{compile!(name)} if v == true #{code} elsif v.is_a?(Proc) Mustache::Template.new(v.call(#{raw.inspect}).to_s).render(ctx.dup) else # Shortcut when passed non-array v = [v] if v.respond_to?(:has_key?) || !v.respond_to?(:map) || v.is_a?(Struct) v.map { |h| ctx.push(h); r = #{code}; ctx.pop; r }.join end end compiled end
def on_utag(name)
def on_utag(name) ev(<<-compiled) v = #{compile!(name)} if v.is_a?(Proc) v = Mustache::Template.new(v.call.to_s).render(ctx.dup) end v.to_s compiled end
def str(s)
def str(s) s.inspect[1..-2] end