Differentiate between lambdas in the context (which should have mustache semantics) and helpers (which have Handlebars helper semantics).
This commit is contained in:
@@ -408,6 +408,12 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||
preamble: function() {
|
||||
var out = [];
|
||||
|
||||
// this register will disambiguate helper lookup from finding a function in
|
||||
// a context. This is necessary for mustache compatibility, which requires
|
||||
// that context functions in blocks are evaluated by blockHelperMissing, and
|
||||
// then proceed as if the resulting value was provided to blockHelperMissing.
|
||||
this.useRegister('foundHelper');
|
||||
|
||||
if (!this.isChild) {
|
||||
var namespace = this.namespace;
|
||||
var copies = "helpers = helpers || " + namespace + ".helpers;";
|
||||
@@ -520,10 +526,8 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||
} else if (isScoped || this.options.knownHelpersOnly) {
|
||||
toPush = topStack + " = " + this.nameLookup('depth' + this.lastContext, name, 'context');
|
||||
} else {
|
||||
toPush = topStack + " = "
|
||||
+ this.nameLookup('helpers', name, 'helper')
|
||||
+ " || "
|
||||
+ this.nameLookup('depth' + this.lastContext, name, 'context');
|
||||
this.register('foundHelper', this.nameLookup('helpers', name, 'helper'));
|
||||
toPush = topStack + " = foundHelper || " + this.nameLookup('depth' + this.lastContext, name, 'context');
|
||||
}
|
||||
|
||||
toPush += ';';
|
||||
@@ -618,10 +622,10 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||
|
||||
params.push(stringOptions);
|
||||
|
||||
this.populateCall(params, id, helperId || id, fn);
|
||||
this.populateCall(params, id, helperId || id, fn, program !== '{}');
|
||||
},
|
||||
|
||||
populateCall: function(params, id, helperId, fn) {
|
||||
populateCall: function(params, id, helperId, fn, program) {
|
||||
var paramString = ["depth0"].concat(params).join(", ");
|
||||
var helperMissingString = ["depth0"].concat(helperId).concat(params).join(", ");
|
||||
|
||||
@@ -631,7 +635,8 @@ Handlebars.JavaScriptCompiler = function() {};
|
||||
this.source.push(nextStack + " = " + id + ".call(" + paramString + ");");
|
||||
} else {
|
||||
this.context.aliases.functionType = '"function"';
|
||||
this.source.push("if(typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
|
||||
var condition = program ? "foundHelper && " : ""
|
||||
this.source.push("if(" + condition + "typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
|
||||
}
|
||||
fn.call(this, nextStack, helperMissingString, id);
|
||||
this.usingKnownHelper = false;
|
||||
|
||||
+43
-14
@@ -274,7 +274,7 @@ test("block helper", function() {
|
||||
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
|
||||
var template = CompilerContext.compile(string);
|
||||
|
||||
result = template({goodbyes: function(fn) { return fn({text: "GOODBYE"}); }, world: "world"});
|
||||
result = template({world: "world"}, { helpers: {goodbyes: function(fn) { return fn({text: "GOODBYE"}); }}});
|
||||
equal(result, "GOODBYE! cruel world!", "Block helper executed");
|
||||
});
|
||||
|
||||
@@ -282,7 +282,7 @@ test("block helper staying in the same context", function() {
|
||||
var string = "{{#form}}<p>{{name}}</p>{{/form}}"
|
||||
var template = CompilerContext.compile(string);
|
||||
|
||||
result = template({form: function(fn) { return "<form>" + fn(this) + "</form>" }, name: "Yehuda"});
|
||||
result = template({name: "Yehuda"}, {helpers: {form: function(fn) { return "<form>" + fn(this) + "</form>" } }});
|
||||
equal(result, "<form><p>Yehuda</p></form>", "Block helper executed with current context");
|
||||
});
|
||||
|
||||
@@ -307,7 +307,7 @@ test("block helper passing a new context", function() {
|
||||
var string = "{{#form yehuda}}<p>{{name}}</p>{{/form}}"
|
||||
var template = CompilerContext.compile(string);
|
||||
|
||||
result = template({form: function(context, fn) { return "<form>" + fn(context) + "</form>" }, yehuda: {name: "Yehuda"}});
|
||||
result = template({yehuda: {name: "Yehuda"}}, { helpers: {form: function(context, fn) { return "<form>" + fn(context) + "</form>" }}});
|
||||
equal(result, "<form><p>Yehuda</p></form>", "Context variable resolved");
|
||||
});
|
||||
|
||||
@@ -315,7 +315,7 @@ test("block helper passing a complex path context", function() {
|
||||
var string = "{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}"
|
||||
var template = CompilerContext.compile(string);
|
||||
|
||||
result = template({form: function(context, fn) { return "<form>" + fn(context) + "</form>" }, yehuda: {name: "Yehuda", cat: {name: "Harold"}}});
|
||||
result = template({yehuda: {name: "Yehuda", cat: {name: "Harold"}}}, { helpers: {form: function(context, fn) { return "<form>" + fn(context) + "</form>" }}});
|
||||
equal(result, "<form><p>Harold</p></form>", "Complex path variable resolved");
|
||||
});
|
||||
|
||||
@@ -324,10 +324,12 @@ test("nested block helpers", function() {
|
||||
var template = CompilerContext.compile(string);
|
||||
|
||||
result = template({
|
||||
form: function(context, fn) { return "<form>" + fn(context) + "</form>" },
|
||||
yehuda: {name: "Yehuda",
|
||||
link: function(fn) { return "<a href='" + this.name + "'>" + fn(this) + "</a>"; }
|
||||
}
|
||||
yehuda: {name: "Yehuda" }
|
||||
}, {
|
||||
helpers: {
|
||||
link: function(fn) { return "<a href='" + this.name + "'>" + fn(this) + "</a>" },
|
||||
form: function(context, fn) { return "<form>" + fn(context) + "</form>" }
|
||||
}
|
||||
});
|
||||
equal(result, "<form><p>Yehuda</p><a href='Yehuda'>Hello</a></form>", "Both blocks executed");
|
||||
});
|
||||
@@ -359,10 +361,9 @@ test("block helper inverted sections", function() {
|
||||
}
|
||||
};
|
||||
|
||||
var hash = {list: list, people: [{name: "Alan"}, {name: "Yehuda"}]};
|
||||
var empty = {list: list, people: []};
|
||||
var hash = {people: [{name: "Alan"}, {name: "Yehuda"}]};
|
||||
var empty = {people: []};
|
||||
var rootMessage = {
|
||||
list: function(context, options) { if(context.length === 0) { return "<p>" + options.inverse(this) + "</p>"; } },
|
||||
people: [],
|
||||
message: "Nobody's here"
|
||||
}
|
||||
@@ -371,9 +372,9 @@ test("block helper inverted sections", function() {
|
||||
|
||||
// the meaning here may be kind of hard to catch, but list.not is always called,
|
||||
// so we should see the output of both
|
||||
shouldCompileTo(string, hash, "<ul><li>Alan</li><li>Yehuda</li></ul>", "an inverse wrapper is passed in as a new context");
|
||||
shouldCompileTo(string, empty, "<p><em>Nobody's here</em></p>", "an inverse wrapper can be optionally called");
|
||||
shouldCompileTo(messageString, rootMessage, "<p>Nobody's here</p>", "the context of an inverse is the parent of the block");
|
||||
shouldCompileTo(string, [hash, { list: list }], "<ul><li>Alan</li><li>Yehuda</li></ul>", "an inverse wrapper is passed in as a new context");
|
||||
shouldCompileTo(string, [empty, { list: list }], "<p><em>Nobody's here</em></p>", "an inverse wrapper can be optionally called");
|
||||
shouldCompileTo(messageString, [rootMessage, { list: list }], "<p>Nobody's here</p>", "the context of an inverse is the parent of the block");
|
||||
});
|
||||
|
||||
module("helpers hash");
|
||||
@@ -571,6 +572,14 @@ test("Invert blocks work in knownHelpers only mode", function() {
|
||||
equal(result, "bar", "'bar' should === '" + result);
|
||||
});
|
||||
|
||||
module("blockHelperMissing");
|
||||
|
||||
test("lambdas are resolved by blockHelperMissing, not handlebars proper", function() {
|
||||
var string = "{{#truthy}}yep{{/truthy}}";
|
||||
var data = { truthy: function() { return true; } };
|
||||
shouldCompileTo(string, data, "yep");
|
||||
});
|
||||
|
||||
var teardown;
|
||||
module("built-in helpers", {
|
||||
setup: function(){ teardown = null; },
|
||||
@@ -1036,3 +1045,23 @@ test("GH-158: Using array index twice, breaks the template", function() {
|
||||
|
||||
shouldCompileTo(string, data, "1, 2", "it works as expected");
|
||||
});
|
||||
|
||||
test("bug reported by @fat where lambdas weren't being properly resolved", function() {
|
||||
var string = "<strong>This is a slightly more complicated {{thing}}.</strong>.\n{{! Just ignore this business. }}\nCheck this out:\n{{#hasThings}}\n<ul>\n{{#things}}\n<li class={{className}}>{{word}}</li>\n{{/things}}</ul>.\n{{/hasThings}}\n{{^hasThings}}\n\n<small>Nothing to check out...</small>\n{{/hasThings}}";
|
||||
var data = {
|
||||
thing: function() {
|
||||
return "blah";
|
||||
},
|
||||
things: [
|
||||
{className: "one", word: "@fat"},
|
||||
{className: "two", word: "@dhg"},
|
||||
{className: "three", word:"@sayrer"}
|
||||
],
|
||||
hasThings: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var output = "<strong>This is a slightly more complicated blah.</strong>.\n\nCheck this out:\n\n<ul>\n\n<li class=one>@fat</li>\n\n<li class=two>@dhg</li>\n\n<li class=three>@sayrer</li>\n</ul>.\n\n";
|
||||
shouldCompileTo(string, data, output);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user