Skip to content

Commit

Permalink
Avoid requiring partials in global scope.
Browse files Browse the repository at this point in the history
  • Loading branch information
davisp committed Oct 29, 2009
1 parent 8ef180f commit 3a9eda1
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 72 deletions.
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,27 @@ Enumerable Sections use the same syntax as condition sections do. `{{#shopping_i
### View Partials
mustache.js supports a quite powerful but yet simple view partial mechanism. Use the following syntax for partials: `{{<partial_name}}`

var view = {name: "Joe"}
var view = {
name: "Joe",
winnings: {
value: 1000,
taxed_value: function() {
return this.value - (this.value * 0.4);
}
}
};

var template = "Welcome, {{jow}}! {{<winnings}}"
var partials = {winnings: "You just won ${{value}} (which is ${{taxed_value}} after tax)"};

var winnings = {value: 1000,
taxed_value: function() {
return this.value - (this.value * 0.4);
}
}
var winnings_template = "You just won ${{value}} (which is ${{taxed_value}} after tax)"

var output = Mustache.to_html(template, view)
var output = Mustache.to_html(template, view, partials)

output will be:
Welcome, Joe! You just won $1000 (which is $600 after tax)

You invoke a partial with `{{<name}}`. When `name` is an object, mustache.js will look for a JavaScript object called `name_template` and uses this for the template and `name` for the view. If `name` is a simple string, mustache.js will simply render the strings context like a normal template.

You invoke a partial with `{{<name}}`. Invoking the partial `name` will tell
mustache.js to look for a object in the context's property `name`. It will then
use that object as the context for the template found in `partials` for `name`.

## Escaping
mustache.js does escape all values when using the standard double mustache syntax. Characters which will be escaped: `& \ " < >`. To disable escaping, simply use tripple mustaches like `{{{unescaped_variable}}}`.
Expand Down
1 change: 1 addition & 0 deletions examples/template_partial.2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Again, {{again}}!
2 changes: 1 addition & 1 deletion examples/template_partial.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<h1>{{title}}</h1>
{{<inner_partial}}
{{<partial}}
9 changes: 5 additions & 4 deletions examples/template_partial.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
var template_partial = {
var partial_context = {
title: function() {
return "Welcome";
}
},
partial: {
again: "Goodbye"
}
}

var inner_partial = "Again, {{title}}!";
2 changes: 1 addition & 1 deletion examples/template_partial.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<h1>Welcome</h1>
Again, Welcome!
Again, Goodbye!
5 changes: 5 additions & 0 deletions examples/view_partial.2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Hello {{name}}
You have just won ${{value}}!
{{#in_ca}}
Well, ${{ taxed_value }}, after taxes.
{{/in_ca}}
2 changes: 1 addition & 1 deletion examples/view_partial.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<h1>{{greeting}}</h1>
{{<simple}}
{{<partial}}
<h3>{{farewell}}</h3>
25 changes: 10 additions & 15 deletions examples/view_partial.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
var view_partial = {
var partial_context = {
greeting: function() {
return "Welcome";
},

farewell: function() {
return "Fair enough, right?";
}
};

var simple = {
name: "Chris",
value: 10000,
taxed_value: function() {
return this.value - (this.value * 0.4);
},
in_ca: true

partial: {
name: "Chris",
value: 10000,
taxed_value: function() {
return this.value - (this.value * 0.4);
},
in_ca: true
}
};

var simple_template = "Hello {{name}}\n" +
"You have just won ${{value}}!\n" +
"{{#in_ca}}\n" +
"Well, ${{ taxed_value }}, after taxes.\n" +
"{{/in_ca}}\n";
39 changes: 18 additions & 21 deletions mustache.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,33 @@ var Mustache = function() {
otag: "{{",
ctag: "}}",

render: function(template, context) {
render: function(template, context, partials) {
// fail fast
if(template.indexOf(this.otag) == -1) {
return template;
}

var html = this.render_section(template, context);
return this.render_tags(html, context);
var html = this.render_section(template, context, partials);
return this.render_tags(html, context, partials);
},

/*
Tries to find a partial in the global scope and render it
*/
render_partial: function(name, context) {
// FIXME: too hacky
var evil_name = eval(name);
switch(typeof evil_name) {
case "string": // a string partial, we simply render
return this.render(evil_name, context);
case "object": // a view partial needs a `name_template` template
var tpl = name + "_template";
return this.render(eval(tpl), evil_name);
default: // should not happen #famouslastwords
throw("Unknown partial type.");
render_partial: function(name, context, partials) {
if(typeof(context[name]) != "object") {
throw({message: "subcontext for '" + name + "' is not an object"});
}
if(!partials || !partials[name]) {
throw({message: "unknown_partial"});
}
return this.render(partials[name], context[name], partials);
},

/*
Renders boolean and enumerable sections
*/
render_section: function(template, context) {
render_section: function(template, context, partials) {
if(template.indexOf(this.otag + "#") == -1) {
return template;
}
Expand All @@ -59,10 +55,11 @@ var Mustache = function() {
var value = that.find(name, context);
if(that.is_array(value)) { // Enumerable, Let's loop!
return that.map(value, function(row) {
return that.render(content, that.merge(context, that.create_context(row)));
return that.render(content, that.merge(context,
that.create_context(row)), partials);
}).join('');
} else if(value) { // boolean section
return that.render(content, context);
return that.render(content, context, partials);
} else {
return "";
}
Expand All @@ -72,7 +69,7 @@ var Mustache = function() {
/*
Replace {{foo}} and friends with values from our view
*/
render_tags: function(template, context) {
render_tags: function(template, context, partials) {
var lines = template.split("\n");

var new_regex = function() {
Expand All @@ -96,7 +93,7 @@ var Mustache = function() {
i--;
return "";
case "<": // render partial
return that.render_partial(name, context);
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
return that.find(name, context);
default: // escape the value
Expand Down Expand Up @@ -233,8 +230,8 @@ var Mustache = function() {
/*
Turns a template and view into HTML
*/
to_html: function(template, view) {
return new Renderer().render(template, view);
to_html: function(template, view, partials) {
return new Renderer().render(template, view, partials);
}
});
}();
73 changes: 55 additions & 18 deletions test/mustache_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@
File.basename name, '.js'
end

non_partials = testnames.select{|t| not t.include? "partial"}
partials = testnames.select{|t| t.include? "partial"}

def load_test(dir, name, partial=false)
view = File.read(dir + "/../examples/#{name}.js")
template = File.read(dir + "/../examples/#{name}.html").to_json
expect = File.read(dir + "/../examples/#{name}.txt")
if not partial
[view, template, expect]
else
partial = File.read(dir + "/../examples/#{name}.2.html").to_json
[view, template, partial, expect]
end
end

describe "mustache" do
before(:all) do
@mustache = File.read(__DIR__ + "/../mustache.js")
Expand All @@ -25,32 +40,54 @@
run_js(js).should == "ERROR: Can't find x in [object Object]\n"
end

testnames.each do |testname|
describe testname do
non_partials.each do |testname|
describe testname do
it "should generate the correct html" do
view = File.read(__DIR__ + "/../examples/#{testname}.js")
template = File.read(__DIR__ + "/../examples/#{testname}.html").to_json
expect = File.read(__DIR__ + "/../examples/#{testname}.txt")


view, template, expect = load_test(__DIR__, testname)

runner = <<-JS
try {
#{@mustache}
#{view}
var template = #{template};
var result = Mustache.to_html(template, #{testname});
print(result);
} catch(e) {
print('ERROR: ' + e.message);
}
try {
#{@mustache}
#{view}
var template = #{template};
var result = Mustache.to_html(template, #{testname});
print(result);
} catch(e) {
print('ERROR: ' + e.message);
}
JS



run_js(runner).should == expect
end
end
end


partials.each do |testname|
describe testname do
it "should generate the correct html" do

view, template, partial, expect =
load_test(__DIR__, testname, true)

runner = <<-JS
try {
#{@mustache}
#{view};
var template = #{template};
var partials = {"partial": #{partial}};
var result = Mustache.to_html(template, partial_context, partials);
print(result);
} catch(e) {
print('ERROR: ' + e.message);
}
JS

run_js(runner).should == expect
end
end
end

def run_js(js)
File.open("runner.js", 'w') {|f| f << js}
`js runner.js`
Expand Down

0 comments on commit 3a9eda1

Please sign in to comment.