Reworked the way that expression work a good bit. They're a lot less error prone now.

This commit is contained in:
Alan Johnson
2010-09-24 22:26:10 -04:00
parent a07dae5d8e
commit c1bcd72eb1
2 changed files with 40 additions and 25 deletions
+35 -25
View File
@@ -78,23 +78,28 @@ var Handlebars = {
return compiled;
},
evalExpression: function(path, context, stack) {
var parsedPath = Handlebars.parsePath(path);
var depth = parsedPath[0];
var parts = parsedPath[1];
if (depth > stack.length) {
context = null;
} else if (depth > 0) {
context = stack[stack.length - depth];
}
for (var i = 0; i < parts.length && context !== undefined; i++) {
context = context[parts[i]];
}
return context;
},
buildContext: function(context, stack) {
var ContextWrapper = function(stack) {
this.__stack__ = stack.slice(0);
this.__get__ = function(path) {
var context = this;
var parsedPath = Handlebars.parsePath(path);
var depth = parsedPath[0];
var parts = parsedPath[1];
if (depth > 0) {
context = this.__stack__[this.__stack__.length - depth];
}
for (var i = 0; i < parts.length; i++) {
context = context[parts[i]];
}
return context;
return Handlebars.evalExpression(path, this, this.__stack__);
};
};
@@ -102,13 +107,18 @@ var Handlebars = {
return new ContextWrapper(stack);
},
// Returns a two element array containing the numbers of contexts to back up the stack and
// spot to memoize paths to speed up loops and subsequent parses
pathPatterns: {},
// returns a two element array containing the numbers of contexts to back up the stack and
// the properties to dig into on the current context
//
// For example, if the path is "../../alan/name", the result will be [2, ["alan", "name"]].
// for example, if the path is "../../alan/name", the result will be [2, ["alan", "name"]].
parsePath: function(path) {
if (path == null) {
return [0, []];
} else if (Handlebars.pathPatterns[path] != null) {
return Handlebars.pathPatterns[path];
}
var parts = path.split("/");
@@ -134,8 +144,10 @@ var Handlebars = {
dig.push(parts[i]);
}
}
return [depth, dig];
var ret = [depth, dig];
Handlebars.pathPatterns[path] = ret;
return ret;
},
isEmpty: function(value) {
@@ -180,6 +192,7 @@ var Handlebars = {
handleExpression: function(lookup, context, arg, isEscaped) {
var out = "";
if (Handlebars.isFunction(lookup)) {
out = out + Handlebars.filterOutput(lookup.call(context, arg), isEscaped);
} else if(!Handlebars.isEmpty(lookup)) {
@@ -330,15 +343,12 @@ Handlebars.Compiler.prototype = {
var depth = parsed[0];
var parts = parsed[1];
var paramExpr = "";
for(var i = 0; i < parts.length; i++) {
paramExpr += "['" + parts[i] + "']";
}
if (depth > 0) {
return "( stack[stack.length - " + depth + "]" + paramExpr + ")";
if (depth > 0 || parts.length > 1) {
return "(Handlebars.evalExpression('" + param + "', context, stack))";
} else if (parts.length == 1) {
return "(context['" + parts[0] + "'] || fallback['" + parts[0] + "'])";
} else {
return "( context" + paramExpr + " != null ? context" + paramExpr + " : fallback" + paramExpr + " )";
return "(context || fallback)";
}
},
+5
View File
@@ -103,6 +103,11 @@ test("bad idea nested paths", function() {
shouldCompileTo(string, hash, "world world world ", "Same context (.) is ignored in paths");
});
test("complex but empty paths", function() {
shouldCompileTo("{{person/name}}", {person: {name: null}}, "");
shouldCompileTo("{{person/name}}", {person: {}}, "");
});
test("this keyword in paths", function() {
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}";
var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};