diff options
-rw-r--r-- | lib/handlebars/ast.js | 5 | ||||
-rw-r--r-- | lib/handlebars/compiler.js | 4 | ||||
-rw-r--r-- | lib/handlebars/printer.js | 4 | ||||
-rw-r--r-- | spec/parser_spec.rb | 25 | ||||
-rw-r--r-- | spec/qunit_spec.js | 58 | ||||
-rw-r--r-- | spec/tokenizer_spec.rb | 16 | ||||
-rw-r--r-- | src/handlebars.l | 4 | ||||
-rw-r--r-- | src/handlebars.yy | 2 |
8 files changed, 114 insertions, 4 deletions
diff --git a/lib/handlebars/ast.js b/lib/handlebars/ast.js index 86960ab..1002bd5 100644 --- a/lib/handlebars/ast.js +++ b/lib/handlebars/ast.js @@ -88,6 +88,11 @@ var Handlebars = require("handlebars"); this.integer = integer; }; + Handlebars.AST.BooleanNode = function(boolean) { + this.type = "BOOLEAN"; + this.boolean = boolean; + }; + Handlebars.AST.CommentNode = function(comment) { this.type = "comment"; this.comment = comment; diff --git a/lib/handlebars/compiler.js b/lib/handlebars/compiler.js index d66b1db..42e5c2c 100644 --- a/lib/handlebars/compiler.js +++ b/lib/handlebars/compiler.js @@ -228,6 +228,10 @@ Handlebars.JavaScriptCompiler = function() {}; this.opcode('push', integer.integer); }, + BOOLEAN: function(boolean) { + this.opcode('push', boolean.boolean); + }, + comment: function() {}, // HELPERS diff --git a/lib/handlebars/printer.js b/lib/handlebars/printer.js index 6388f01..ff3f038 100644 --- a/lib/handlebars/printer.js +++ b/lib/handlebars/printer.js @@ -113,6 +113,10 @@ Handlebars.PrintVisitor.prototype.INTEGER = function(integer) { return "INTEGER{" + integer.integer + "}"; } +Handlebars.PrintVisitor.prototype.BOOLEAN = function(boolean) { + return "BOOLEAN{" + boolean.boolean + "}"; +} + Handlebars.PrintVisitor.prototype.ID = function(id) { var path = id.parts.join("/"); if(id.parts.length > 1) { diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index 941887d..ead3315 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -96,6 +96,10 @@ describe "Parser" do "INTEGER{#{string}}" end + def boolean(string) + "BOOLEAN{#{string}}" + end + def hash(*pairs) "HASH{" + pairs.map {|k,v| "#{k}=#{v}" }.join(", ") + "}" end @@ -138,6 +142,14 @@ describe "Parser" do mustache id("foo"), [], hash(["bar", integer("1")]) end + ast_for("{{foo bar=true}}").should == program do + mustache id("foo"), [], hash(["bar", boolean("true")]) + end + + ast_for("{{foo bar=false}}").should == program do + mustache id("foo"), [], hash(["bar", boolean("false")]) + end + ast_for("{{foo bar=baz bat=bam}}").should == program do mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "ID:bam"]) end @@ -153,6 +165,14 @@ describe "Parser" do ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should == program do mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", integer("1")]) end + + ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should == program do + mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("true")]) + end + + ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should == program do + mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("false")]) + end end it "parses mustaches with string parameters" do @@ -163,6 +183,11 @@ describe "Parser" do ast_for("{{foo 1}}").should == program { mustache id("foo"), [integer("1")] } end + it "parses mustaches with BOOLEAN parameters" do + ast_for("{{foo true}}").should == program { mustache id("foo"), [boolean("true")] } + ast_for("{{foo false}}").should == program { mustache id("foo"), [boolean("false")] } + end + it "parses contents followed by a mustache" do ast_for("foo bar {{baz}}").should == program do content "foo bar " diff --git a/spec/qunit_spec.js b/spec/qunit_spec.js index 64fee91..65100e0 100644 --- a/spec/qunit_spec.js +++ b/spec/qunit_spec.js @@ -434,10 +434,15 @@ test("GH-14: a partial preceding a selector", function() { module("String literal parameters"); test("simple literals work", function() { - var string = 'Message: {{hello "world" 12}}'; + var string = 'Message: {{hello "world" 12 true false}}'; var hash = {}; - var helpers = {hello: function(param, times) { return "Hello " + param + " " + times + " times"; }} - shouldCompileTo(string, [hash, helpers], "Message: Hello world 12 times", "template with a simple String literal"); + var helpers = {hello: function(param, times, bool1, bool2) { + if(typeof times !== 'number') { times = "NaN"; } + if(typeof bool1 !== 'boolean') { bool1 = "NaB"; } + if(typeof bool2 !== 'boolean') { bool2 = "NaB"; } + return "Hello " + param + " " + times + " times: " + bool1 + " " + bool2; + }} + shouldCompileTo(string, [hash, helpers], "Message: Hello world 12 times: true false", "template with a simple String literal"); }); test("using a quote in the middle of a parameter raises an error", function() { @@ -695,6 +700,30 @@ test("helpers can take an optional hash", function() { equals(result, "GOODBYE CRUEL WORLD 12 TIMES"); }); +test("helpers can take an optional hash with booleans", function() { + var helpers = { + goodbye: function(options) { + if (options.hash.print === true) { + return "GOODBYE " + options.hash.cruel + " " + options.hash.world; + } else if (options.hash.print === false) { + return "NOT PRINTING"; + } else { + return "THIS SHOULD NOT HAPPEN"; + } + } + }; + + var context = {}; + + var template = Handlebars.compile('{{goodbye cruel="CRUEL" world="WORLD" print=true}}'); + var result = template(context, {helpers: helpers}); + equals(result, "GOODBYE CRUEL WORLD"); + + var template = Handlebars.compile('{{goodbye cruel="CRUEL" world="WORLD" print=false}}'); + var result = template(context, {helpers: helpers}); + equals(result, "NOT PRINTING"); +}); + test("block helpers can take an optional hash", function() { var template = Handlebars.compile('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}'); @@ -708,6 +737,29 @@ test("block helpers can take an optional hash", function() { equals(result, "GOODBYE CRUEL world 12 TIMES"); }); +test("block helpers can take an optional hash with booleans", function() { + var helpers = { + goodbye: function(options) { + if (options.hash.print === true) { + return "GOODBYE " + options.hash.cruel + " " + options.fn(this); + } else if (options.hash.print === false) { + return "NOT PRINTING"; + } else { + return "THIS SHOULD NOT HAPPEN"; + } + } + }; + + var template = Handlebars.compile('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}'); + var result = template({}, {helpers: helpers}); + equals(result, "GOODBYE CRUEL world"); + + var template = Handlebars.compile('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}'); + var result = template({}, {helpers: helpers}); + equals(result, "NOT PRINTING"); +}); + + test("arguments to helpers can be retrieved from options hash in string form", function() { var template = Handlebars.compile('{{wycats is.a slave.driver}}', {stringParams: true}); diff --git a/spec/tokenizer_spec.rb b/spec/tokenizer_spec.rb index d98ee3d..9b61fe2 100644 --- a/spec/tokenizer_spec.rb +++ b/spec/tokenizer_spec.rb @@ -177,6 +177,16 @@ describe "Tokenizer" do result[2].should be_token("INTEGER", "1") end + it "tokenizes booleans" do + result = tokenize(%|{{ foo true }}|) + result.should match_tokens(%w(OPEN ID BOOLEAN CLOSE)) + result[2].should be_token("BOOLEAN", "true") + + result = tokenize(%|{{ foo false }}|) + result.should match_tokens(%w(OPEN ID BOOLEAN CLOSE)) + result[2].should be_token("BOOLEAN", "false") + end + it "tokenizes hash arguments" do result = tokenize("{{ foo bar=baz }}") result.should match_tokens %w(OPEN ID ID EQUALS ID CLOSE) @@ -187,6 +197,12 @@ describe "Tokenizer" do result = tokenize("{{ foo bar baz=1 }}") result.should match_tokens %w(OPEN ID ID ID EQUALS INTEGER CLOSE) + result = tokenize("{{ foo bar baz=true }}") + result.should match_tokens %w(OPEN ID ID ID EQUALS BOOLEAN CLOSE) + + result = tokenize("{{ foo bar baz=false }}") + result.should match_tokens %w(OPEN ID ID ID EQUALS BOOLEAN CLOSE) + result = tokenize("{{ foo bar\n baz=bat }}") result.should match_tokens %w(OPEN ID ID ID EQUALS ID CLOSE) diff --git a/src/handlebars.l b/src/handlebars.l index 895974a..0d3cdf0 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -24,7 +24,9 @@ <mu>"}}}" { this.begin("INITIAL"); return 'CLOSE'; } <mu>"}}" { this.begin("INITIAL"); return 'CLOSE'; } <mu>'"'("\\"["]|[^"])*'"' { yytext = yytext.substr(1,yyleng-2).replace(/\\"/g,'"'); return 'STRING'; } -<mu>[0-9]+/[}\s] { return 'INTEGER' } +<mu>"true"/[}\s] { return 'BOOLEAN'; } +<mu>"false"/[}\s] { return 'BOOLEAN'; } +<mu>[0-9]+/[}\s] { return 'INTEGER'; } <mu>[a-zA-Z0-9_$-]+/[=}\s/.] { return 'ID'; } <mu>. { return 'INVALID'; } diff --git a/src/handlebars.yy b/src/handlebars.yy index 02f8e21..d3d41df 100644 --- a/src/handlebars.yy +++ b/src/handlebars.yy @@ -69,6 +69,7 @@ param : path { $$ = $1 } | STRING { $$ = new yy.StringNode($1) } | INTEGER { $$ = new yy.IntegerNode($1) } + | BOOLEAN { $$ = new yy.BooleanNode($1) } ; hash @@ -84,6 +85,7 @@ hashSegment : ID EQUALS path { $$ = [$1, $3] } | ID EQUALS STRING { $$ = [$1, new yy.StringNode($3)] } | ID EQUALS INTEGER { $$ = [$1, new yy.IntegerNode($3)] } + | ID EQUALS BOOLEAN { $$ = [$1, new yy.BooleanNode($3)] } ; path |