Skip to content

Commit

Permalink
Merge pull request #634 from wycats/name-option
Browse files Browse the repository at this point in the history
It would be great to have the helper name passed to `blockHelperMissing`
  • Loading branch information
kpdecker committed Jan 15, 2014
2 parents cb80f46 + d385e2c commit 5659db4
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 25 deletions.
8 changes: 5 additions & 3 deletions lib/handlebars/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ HandlebarsEnvironment.prototype = {
};

function registerDefaultHelpers(instance) {
instance.registerHelper('helperMissing', function(arg) {
if(arguments.length === 2) {
instance.registerHelper('helperMissing', function(/* [args, ]options */) {
if(arguments.length === 1) {
// A missing field in a {{foo}} constuct.
return undefined;
} else {
throw new Exception("Missing helper: '" + arg + "'");
// Someone is actually trying to call something, blow up.
throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'");
}
});

Expand Down
2 changes: 1 addition & 1 deletion lib/handlebars/compiler/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ Compiler.prototype = {
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
this.opcode('emptyHash');
this.opcode('blockValue');
this.opcode('blockValue', sexpr.id.original);
} else {
this.ambiguousSexpr(sexpr, program, inverse);

Expand Down
31 changes: 13 additions & 18 deletions lib/handlebars/compiler/javascript-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,11 @@ JavaScriptCompiler.prototype = {
// `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and
// replace it on the stack with the result of properly
// invoking blockHelperMissing.
blockValue: function() {
blockValue: function(name) {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';

var params = ["depth0"];
this.setupParams(0, params);
this.setupParams(name, 0, params);

this.replaceStack(function(current) {
params.splice(1, 0, current);
Expand All @@ -247,7 +247,7 @@ JavaScriptCompiler.prototype = {

// We're being a bit cheeky and reusing the options value from the prior exec
var params = ["depth0"];
this.setupParams(0, params, true);
this.setupParams('', 0, params, true);

this.flushInline();

Expand Down Expand Up @@ -504,20 +504,15 @@ JavaScriptCompiler.prototype = {
this.context.aliases.helperMissing = 'helpers.helperMissing';
this.useRegister('helper');

var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
var helper = this.setupHelper(paramSize, name);
var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');

var lookup = 'helper = ' + helper.name + ' || ' + nonHelper;
var lookup = 'helper = ' + helper.name + ' || ' + nonHelper + ' || helperMissing';
if (helper.paramsInit) {
lookup += ',' + helper.paramsInit;
}

this.push(
'('
+ lookup
+ ',helper '
+ '? helper.call(' + helper.callParams + ') '
+ ': helperMissing.call(' + helper.helperMissingParams + '))');
this.push('(' + lookup + ',helper.call(' + helper.callParams + '))');

// Always flush subexpressions. This is both to prevent the compounding size issue that
// occurs when the code has to be duplicated for inlining and also to prevent errors
Expand Down Expand Up @@ -824,23 +819,23 @@ JavaScriptCompiler.prototype = {
.replace(/\u2029/g, '\\u2029') + '"';
},

setupHelper: function(paramSize, name, missingParams) {
setupHelper: function(paramSize, name, blockHelper) {
var params = [],
paramsInit = this.setupParams(paramSize, params, missingParams);
paramsInit = this.setupParams(name, paramSize, params, blockHelper);
var foundHelper = this.nameLookup('helpers', name, 'helper');

return {
params: params,
paramsInit: paramsInit,
name: foundHelper,
callParams: ["depth0"].concat(params).join(", "),
helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
callParams: ["depth0"].concat(params).join(", ")
};
},

setupOptions: function(paramSize, params) {
setupOptions: function(helper, paramSize, params) {
var options = [], contexts = [], types = [], param, inverse, program;

options.push("name:" + this.quotedString(helper));
options.push("hash:" + this.popStack());

if (this.stringParams) {
Expand Down Expand Up @@ -892,8 +887,8 @@ JavaScriptCompiler.prototype = {

// the params and contexts arguments are passed in arrays
// to fill in
setupParams: function(paramSize, params, useRegister) {
var options = '{' + this.setupOptions(paramSize, params).join(',') + '}';
setupParams: function(helperName, paramSize, params, useRegister) {
var options = '{' + this.setupOptions(helperName, paramSize, params).join(',') + '}';

if (useRegister) {
this.useRegister('options');
Expand Down
43 changes: 40 additions & 3 deletions spec/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,9 @@ describe('helpers', function() {
var context = { hello: "Hello", world: "world" };

var helpers = {
helperMissing: function(helper, context) {
if(helper === "link_to") {
return new Handlebars.SafeString("<a>" + context + "</a>");
helperMissing: function(mesg, options) {
if(options.name === "link_to") {
return new Handlebars.SafeString("<a>" + mesg + "</a>");
}
}
};
Expand Down Expand Up @@ -436,6 +436,43 @@ describe('helpers', function() {
});
});

describe('name field', function() {
var context = {};
var helpers = {
blockHelperMissing: function() {
return 'missing: ' + arguments[arguments.length-1].name;
},
helper: function() {
return 'ran: ' + arguments[arguments.length-1].name;
}
};

it('should include in ambiguous mustache calls', function() {
shouldCompileTo('{{helper}}', [context, helpers], 'ran: helper');
});
it('should include in helper mustache calls', function() {
shouldCompileTo('{{helper 1}}', [context, helpers], 'ran: helper');
});
it('should include in ambiguous block calls', function() {
shouldCompileTo('{{#helper}}{{/helper}}', [context, helpers], 'ran: helper');
});
it('should include in simple block calls', function() {
shouldCompileTo('{{#./helper}}{{/./helper}}', [context, helpers], 'missing: ./helper');
});
it('should include in helper block calls', function() {
shouldCompileTo('{{#helper 1}}{{/helper}}', [context, helpers], 'ran: helper');
});
it('should include in known helper calls', function() {
var template = CompilerContext.compile("{{helper}}", {knownHelpers: {'helper': true}, knownHelpersOnly: true});

equal(template({}, {helpers: helpers}), 'ran: helper');
});

it('should include full id', function() {
shouldCompileTo('{{#foo.helper}}{{/foo.helper}}', [{foo: {}}, helpers], 'missing: foo.helper');
});
});

describe('name conflicts', function() {
it("helpers take precedence over same-named context properties", function() {
var template = CompilerContext.compile("{{goodbye}} {{cruel world}}");
Expand Down

0 comments on commit 5659db4

Please sign in to comment.