Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partials can be paths #389

Merged
merged 2 commits into from
Dec 24, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions lib/handlebars/compiler/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,10 @@ var Handlebars = require('./base');
// pass or at runtime.
};

Handlebars.AST.PartialNode = function(id, context) {
this.type = "partial";

// TODO: disallow complex IDs

this.id = id;
this.context = context;
Handlebars.AST.PartialNode = function(partialName, context) {
this.type = "partial";
this.partialName = partialName;
this.context = context;
};

var verifyMatch = function(open, close) {
Expand Down Expand Up @@ -93,6 +90,11 @@ var Handlebars = require('./base');
this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
};

Handlebars.AST.PartialNameNode = function(name) {
this.type = "PARTIAL_NAME";
this.name = name;
};

Handlebars.AST.DataNode = function(id) {
this.type = "DATA";
this.id = id;
Expand Down
4 changes: 2 additions & 2 deletions lib/handlebars/compiler/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Handlebars.JavaScriptCompiler = function() {};
},

partial: function(partial) {
var id = partial.id;
var partialName = partial.partialName;
this.usePartial = true;

if(partial.context) {
Expand All @@ -169,7 +169,7 @@ Handlebars.JavaScriptCompiler = function() {};
this.opcode('push', 'depth0');
}

this.opcode('invokePartial', id.original);
this.opcode('invokePartial', partialName.name);
this.opcode('append');
},

Expand Down
6 changes: 5 additions & 1 deletion lib/handlebars/compiler/printer.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Handlebars.PrintVisitor.prototype.mustache = function(mustache) {
};

Handlebars.PrintVisitor.prototype.partial = function(partial) {
var content = this.accept(partial.id);
var content = this.accept(partial.partialName);
if(partial.context) { content = content + " " + this.accept(partial.context); }
return this.pad("{{> " + content + " }}");
};
Expand Down Expand Up @@ -111,6 +111,10 @@ Handlebars.PrintVisitor.prototype.ID = function(id) {
}
};

Handlebars.PrintVisitor.prototype.PARTIAL_NAME = function(partialName) {
return "PARTIAL:" + partialName.name;
};

Handlebars.PrintVisitor.prototype.DATA = function(data) {
return "@" + data.id;
};
Expand Down
12 changes: 10 additions & 2 deletions spec/parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ def data(id)
"@#{id}"
end

def partial_name(name)
"PARTIAL:#{name}"
end

def path(*parts)
"PATH:#{parts.join("/")}"
end
Expand Down Expand Up @@ -218,11 +222,15 @@ def path(*parts)
end

it "parses a partial" do
ast_for("{{> foo }}").should == root { partial id("foo") }
ast_for("{{> foo }}").should == root { partial partial_name("foo") }
end

it "parses a partial with context" do
ast_for("{{> foo bar}}").should == root { partial id("foo"), id("bar") }
ast_for("{{> foo bar}}").should == root { partial partial_name("foo"), id("bar") }
end

it "parses a partial with a complex name" do
ast_for("{{> shared/partial}}").should == root { partial partial_name("shared/partial") }
end

it "parses a comment" do
Expand Down
14 changes: 11 additions & 3 deletions spec/qunit_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,21 @@ test("GH-14: a partial preceding a selector", function() {
shouldCompileToWithPartials(string, [hash, {}, {dude:dude}], true, "Dudes: Jeepers Creepers", "Regular selectors can follow a partial");
});

test("Partials with literal paths", function() {
var string = "Dudes: {{> [dude]}}";
test("Partials with slash paths", function() {
var string = "Dudes: {{> shared/dude}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {dude:dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude':dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
});

test("Partials with integer path", function() {
var string = "Dudes: {{> 404}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {404:dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
});


suite("String literal parameters");

test("simple literals work", function() {
Expand Down
16 changes: 8 additions & 8 deletions spec/tokenizer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,24 +132,24 @@ def tokenize(string)
result[4].should be_token("CONTENT", " baz")
end

it "tokenizes a partial as 'OPEN_PARTIAL ID CLOSE'" do
it "tokenizes a partial as 'OPEN_PARTIAL PARTIAL_NAME CLOSE'" do
result = tokenize("{{> foo}}")
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
result.should match_tokens(%w(OPEN_PARTIAL PARTIAL_NAME CLOSE))
end

it "tokenizes a partial with context as 'OPEN_PARTIAL ID ID CLOSE'" do
it "tokenizes a partial with context as 'OPEN_PARTIAL PARTIAL_NAME ID CLOSE'" do
result = tokenize("{{> foo bar }}")
result.should match_tokens(%w(OPEN_PARTIAL ID ID CLOSE))
result.should match_tokens(%w(OPEN_PARTIAL PARTIAL_NAME ID CLOSE))
end

it "tokenizes a partial without spaces as 'OPEN_PARTIAL ID CLOSE'" do
it "tokenizes a partial without spaces as 'OPEN_PARTIAL PARTIAL_NAME CLOSE'" do
result = tokenize("{{>foo}}")
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
result.should match_tokens(%w(OPEN_PARTIAL PARTIAL_NAME CLOSE))
end

it "tokenizes a partial space at the end as 'OPEN_PARTIAL ID CLOSE'" do
it "tokenizes a partial space at the end as 'OPEN_PARTIAL PARTIAL_NAME CLOSE'" do
result = tokenize("{{>foo }}")
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
result.should match_tokens(%w(OPEN_PARTIAL PARTIAL_NAME CLOSE))
end

it "tokenizes a comment as 'COMMENT'" do
Expand Down
6 changes: 4 additions & 2 deletions src/handlebars.l
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

%x mu emu com
%x mu emu com par

%%

Expand All @@ -19,7 +19,7 @@

<com>[\s\S]*?"--}}" { yytext = yytext.substr(0, yyleng-4); this.popState(); return 'COMMENT'; }

<mu>"{{>" { return 'OPEN_PARTIAL'; }
<mu>"{{>" { this.begin("par"); return 'OPEN_PARTIAL'; }
<mu>"{{#" { return 'OPEN_BLOCK'; }
<mu>"{{/" { return 'OPEN_ENDBLOCK'; }
<mu>"{{^" { return 'OPEN_INVERSE'; }
Expand All @@ -46,6 +46,8 @@
<mu>[a-zA-Z0-9_$-]+/[=}\s\/.] { return 'ID'; }
<mu>'['[^\]]*']' { yytext = yytext.substr(1, yyleng-2); return 'ID'; }
<mu>. { return 'INVALID'; }
<par>\s+ { /*ignore whitespace*/ }
<par>[a-zA-Z0-9_$-/]+ { this.popState(); return 'PARTIAL_NAME'; }

<INITIAL,mu><<EOF>> { return 'EOF'; }

8 changes: 6 additions & 2 deletions src/handlebars.yy
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ mustache


partial
: OPEN_PARTIAL path CLOSE { $$ = new yy.PartialNode($2); }
| OPEN_PARTIAL path path CLOSE { $$ = new yy.PartialNode($2, $3); }
: OPEN_PARTIAL partialName CLOSE { $$ = new yy.PartialNode($2); }
| OPEN_PARTIAL partialName path CLOSE { $$ = new yy.PartialNode($2, $3); }
;

simpleInverse
Expand Down Expand Up @@ -91,6 +91,10 @@ hashSegment
| ID EQUALS DATA { $$ = [$1, new yy.DataNode($3)]; }
;

partialName
: PARTIAL_NAME { $$ = new yy.PartialNameNode($1); }
;

path
: pathSegments { $$ = new yy.IdNode($1); }
;
Expand Down