From f90981adf608f98551d527c3109771d8babbba9f Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 17 Jan 2014 16:31:59 -0600 Subject: [PATCH 1/3] Add partial hash parser support --- lib/handlebars/compiler/ast.js | 3 ++- lib/handlebars/compiler/printer.js | 7 ++++++- spec/ast.js | 2 +- spec/parser.js | 8 ++++++++ src/handlebars.yy | 3 ++- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index dda682e7e..5538f4057 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -97,11 +97,12 @@ var AST = { // pass or at runtime. }, - PartialNode: function(partialName, context, strip, locInfo) { + PartialNode: function(partialName, context, hash, strip, locInfo) { LocationInfo.call(this, locInfo); this.type = "partial"; this.partialName = partialName; this.context = context; + this.hash = hash; this.strip = strip; }, diff --git a/lib/handlebars/compiler/printer.js b/lib/handlebars/compiler/printer.js index ad55c7d4a..b7b760fac 100644 --- a/lib/handlebars/compiler/printer.js +++ b/lib/handlebars/compiler/printer.js @@ -82,7 +82,12 @@ PrintVisitor.prototype.mustache = function(mustache) { PrintVisitor.prototype.partial = function(partial) { var content = this.accept(partial.partialName); - if(partial.context) { content = content + " " + this.accept(partial.context); } + if(partial.context) { + content += " " + this.accept(partial.context); + } + if (partial.hash) { + content += " " + this.accept(partial.hash); + } return this.pad("{{> " + content + " }}"); }; diff --git a/spec/ast.js b/spec/ast.js index 46f0131f7..430574e22 100644 --- a/spec/ast.js +++ b/spec/ast.js @@ -193,7 +193,7 @@ describe('ast', function() { describe("PartialNode", function(){ it('stores location info', function(){ - var pn = new handlebarsEnv.AST.PartialNode("so_partial", {}, {}, LOCATION_INFO); + var pn = new handlebarsEnv.AST.PartialNode("so_partial", {}, {}, {}, LOCATION_INFO); testLocationInfoStorage(pn); }); }); diff --git a/spec/parser.js b/spec/parser.js index 70c16359e..0f6ed312f 100644 --- a/spec/parser.js +++ b/spec/parser.js @@ -84,6 +84,14 @@ describe('parser', function() { equals(ast_for("{{> foo bar}}"), "{{> PARTIAL:foo ID:bar }}\n"); }); + it('parses a partial with hash', function() { + equals(ast_for("{{> foo bar=bat}}"), "{{> PARTIAL:foo HASH{bar=ID:bat} }}\n"); + }); + + it('parses a partial with context and hash', function() { + equals(ast_for("{{> foo bar bat=baz}}"), "{{> PARTIAL:foo ID:bar HASH{bat=ID:baz} }}\n"); + }); + it('parses a partial with a complex name', function() { equals(ast_for("{{> shared/partial?.bar}}"), "{{> PARTIAL:shared/partial?.bar }}\n"); }); diff --git a/src/handlebars.yy b/src/handlebars.yy index 7bff5125a..bac1cc9cb 100644 --- a/src/handlebars.yy +++ b/src/handlebars.yy @@ -63,7 +63,8 @@ mustache ; partial - : OPEN_PARTIAL partialName path? CLOSE -> new yy.PartialNode($2, $3, stripFlags($1, $4), @$) + : OPEN_PARTIAL partialName param hash? CLOSE -> new yy.PartialNode($2, $3, $4, stripFlags($1, $5), @$) + | OPEN_PARTIAL partialName hash? CLOSE -> new yy.PartialNode($2, undefined, $3, stripFlags($1, $4), @$) ; simpleInverse From 45ae86a248f3da7f24ee68da12454a2444ec398f Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 17 Jan 2014 16:49:44 -0600 Subject: [PATCH 2/3] Implement partial hash evaluation --- lib/handlebars/compiler/compiler.js | 10 ++++++++-- lib/handlebars/compiler/javascript-compiler.js | 2 +- lib/handlebars/runtime.js | 8 ++++++-- spec/partials.js | 8 ++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js index 3c4a9b790..b92289a7f 100644 --- a/lib/handlebars/compiler/compiler.js +++ b/lib/handlebars/compiler/compiler.js @@ -203,8 +203,14 @@ Compiler.prototype = { var partialName = partial.partialName; this.usePartial = true; - if(partial.context) { - this.ID(partial.context); + if (partial.hash) { + this.accept(partial.hash); + } else { + this.opcode('push', 'undefined'); + } + + if (partial.context) { + this.accept(partial.context); } else { this.opcode('push', 'depth0'); } diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 7898bd1f3..8ca0116b4 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -569,7 +569,7 @@ JavaScriptCompiler.prototype = { // This operation pops off a context, invokes a partial with that context, // and pushes the result of the invocation back. invokePartial: function(name) { - var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"]; + var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), this.popStack(), "helpers", "partials"]; if (this.options.data) { params.push("data"); diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index 699499c54..5df681f82 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -29,8 +29,12 @@ export function template(templateSpec, env) { // Note: Using env.VM references rather than local var references throughout this section to allow // for external users to override these as psuedo-supported APIs. - var invokePartialWrapper = function(partial, name, context, helpers, partials, data) { - var result = env.VM.invokePartial.apply(this, arguments); + var invokePartialWrapper = function(partial, name, context, hash, helpers, partials, data) { + if (hash) { + context = Utils.extend({}, context, hash); + } + + var result = env.VM.invokePartial.call(this, partial, name, context, helpers, partials, data); if (result != null) { return result; } if (env.compile) { diff --git a/spec/partials.js b/spec/partials.js index bea72f5b7..68c0441f2 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -23,6 +23,14 @@ describe('partials', function() { shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Empty"); }); + it("partials with parameters", function() { + var string = "Dudes: {{#dudes}}{{> dude otherDude=name}}{{/dudes}}"; + var partial = "{{otherDude}} ({{url}}) "; + var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]}; + shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Yehuda (http://yehuda) Alan (http://alan) ", + "Basic partials output based on current context."); + }); + it("partial in a partial", function() { var string = "Dudes: {{#dudes}}{{>dude}}{{/dudes}}"; var dude = "{{name}} {{> url}} "; From e290ec24f131f89ddf2c6aeb707a4884d41c3c6d Mon Sep 17 00:00:00 2001 From: kpdecker Date: Fri, 17 Jan 2014 17:11:28 -0600 Subject: [PATCH 3/3] Test more concrete behavior in partial hash test --- spec/partials.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/partials.js b/spec/partials.js index 68c0441f2..732436af0 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -24,10 +24,10 @@ describe('partials', function() { }); it("partials with parameters", function() { - var string = "Dudes: {{#dudes}}{{> dude otherDude=name}}{{/dudes}}"; - var partial = "{{otherDude}} ({{url}}) "; - var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]}; - shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Yehuda (http://yehuda) Alan (http://alan) ", + var string = "Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}"; + var partial = "{{others.foo}}{{name}} ({{url}}) "; + var hash = {foo: 'bar', dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]}; + shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: barYehuda (http://yehuda) barAlan (http://alan) ", "Basic partials output based on current context."); });