handlebars 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/.gitignore +1 -1
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +1 -1
  5. data/README.mdown +44 -0
  6. data/Rakefile +3 -0
  7. data/handlebars.gemspec +19 -13
  8. data/lib/handlebars.rb +4 -3
  9. data/lib/handlebars/context.rb +37 -0
  10. data/lib/handlebars/version.rb +1 -1
  11. data/spec/handlebars_spec.rb +40 -0
  12. data/spike.rb +17 -0
  13. data/vendor/handlebars/.gitignore +6 -0
  14. data/vendor/handlebars/.jshintrc +50 -0
  15. data/vendor/handlebars/.npmignore +11 -0
  16. data/vendor/handlebars/Gemfile +5 -0
  17. data/vendor/handlebars/LICENSE +20 -0
  18. data/vendor/handlebars/README.markdown +315 -0
  19. data/vendor/handlebars/Rakefile +116 -0
  20. data/vendor/handlebars/bench/benchwarmer.js +149 -0
  21. data/vendor/handlebars/bench/handlebars.js +163 -0
  22. data/vendor/handlebars/bin/handlebars +139 -0
  23. data/vendor/handlebars/lib/handlebars.js +14 -0
  24. data/vendor/handlebars/lib/handlebars/base.js +101 -0
  25. data/vendor/handlebars/lib/handlebars/compiler/ast.js +103 -0
  26. data/vendor/handlebars/lib/handlebars/compiler/base.js +27 -0
  27. data/vendor/handlebars/lib/handlebars/compiler/compiler.js +808 -0
  28. data/vendor/handlebars/lib/handlebars/compiler/index.js +7 -0
  29. data/vendor/handlebars/lib/handlebars/compiler/printer.js +137 -0
  30. data/vendor/handlebars/lib/handlebars/compiler/visitor.js +13 -0
  31. data/vendor/handlebars/lib/handlebars/runtime.js +68 -0
  32. data/vendor/handlebars/lib/handlebars/utils.js +68 -0
  33. data/vendor/handlebars/package.json +25 -0
  34. data/vendor/handlebars/spec/acceptance_spec.rb +101 -0
  35. data/vendor/handlebars/spec/parser_spec.rb +264 -0
  36. data/vendor/handlebars/spec/qunit_spec.js +1067 -0
  37. data/vendor/handlebars/spec/spec_helper.rb +157 -0
  38. data/vendor/handlebars/spec/tokenizer_spec.rb +254 -0
  39. data/vendor/handlebars/src/handlebars.l +42 -0
  40. data/vendor/handlebars/src/handlebars.yy +99 -0
  41. metadata +93 -77
  42. data/README.md +0 -39
  43. data/lib/handlebars/generator.rb +0 -4
  44. data/lib/handlebars/parser.rb +0 -240
  45. data/spec/generator_spec.rb +0 -5
  46. data/spec/parser_spec.rb +0 -163
  47. data/spec/spec_helper.rb +0 -17
@@ -0,0 +1,7 @@
1
+ // Each of these module will augment the Handlebars object as it loads. No need to perform addition operations
2
+ module.exports = require("./base");
3
+ require("./visitor");
4
+ require("./printer");
5
+
6
+ require("./ast");
7
+ require("./compiler");
@@ -0,0 +1,137 @@
1
+ var Handlebars = require("./base");
2
+
3
+ // BEGIN(BROWSER)
4
+ Handlebars.PrintVisitor = function() { this.padding = 0; };
5
+ Handlebars.PrintVisitor.prototype = new Handlebars.Visitor();
6
+
7
+ Handlebars.PrintVisitor.prototype.pad = function(string, newline) {
8
+ var out = "";
9
+
10
+ for(var i=0,l=this.padding; i<l; i++) {
11
+ out = out + " ";
12
+ }
13
+
14
+ out = out + string;
15
+
16
+ if(newline !== false) { out = out + "\n"; }
17
+ return out;
18
+ };
19
+
20
+ Handlebars.PrintVisitor.prototype.program = function(program) {
21
+ var out = this.pad("PROGRAM:"),
22
+ statements = program.statements,
23
+ inverse = program.inverse,
24
+ i, l;
25
+
26
+ this.padding++;
27
+
28
+ for(i=0, l=statements.length; i<l; i++) {
29
+ out = out + this.accept(statements[i]);
30
+ }
31
+
32
+ this.padding--;
33
+
34
+ if(inverse) {
35
+ out = out + this.pad("{{^}}");
36
+
37
+ this.padding++;
38
+
39
+ for(i=0, l=inverse.statements.length; i<l; i++) {
40
+ out = out + this.accept(inverse.statements[i]);
41
+ }
42
+ }
43
+
44
+ this.padding--;
45
+
46
+ return out;
47
+ };
48
+
49
+ Handlebars.PrintVisitor.prototype.block = function(block) {
50
+ var out = "";
51
+
52
+ out = out + this.pad("BLOCK:");
53
+ this.padding++;
54
+ out = out + this.accept(block.mustache);
55
+ out = out + this.accept(block.program);
56
+ this.padding--;
57
+
58
+ return out;
59
+ };
60
+
61
+ Handlebars.PrintVisitor.prototype.inverse = function(block) {
62
+ var out = "";
63
+
64
+ out = out + this.pad("INVERSE:");
65
+ this.padding++;
66
+ out = out + this.accept(block.mustache);
67
+ out = out + this.accept(block.program);
68
+ this.padding--;
69
+
70
+ return out;
71
+ };
72
+
73
+
74
+ Handlebars.PrintVisitor.prototype.mustache = function(mustache) {
75
+ var params = mustache.params, paramStrings = [], hash;
76
+
77
+ for(var i=0, l=params.length; i<l; i++) {
78
+ paramStrings.push(this.accept(params[i]));
79
+ }
80
+
81
+ params = "[" + paramStrings.join(", ") + "]";
82
+
83
+ hash = mustache.hash ? " " + this.accept(mustache.hash) : "";
84
+
85
+ return this.pad("{{ " + this.accept(mustache.id) + " " + params + hash + " }}");
86
+ };
87
+
88
+ Handlebars.PrintVisitor.prototype.partial = function(partial) {
89
+ var content = this.accept(partial.id);
90
+ if(partial.context) { content = content + " " + this.accept(partial.context); }
91
+ return this.pad("{{> " + content + " }}");
92
+ };
93
+
94
+ Handlebars.PrintVisitor.prototype.hash = function(hash) {
95
+ var pairs = hash.pairs;
96
+ var joinedPairs = [], left, right;
97
+
98
+ for(var i=0, l=pairs.length; i<l; i++) {
99
+ left = pairs[i][0];
100
+ right = this.accept(pairs[i][1]);
101
+ joinedPairs.push( left + "=" + right );
102
+ }
103
+
104
+ return "HASH{" + joinedPairs.join(", ") + "}";
105
+ };
106
+
107
+ Handlebars.PrintVisitor.prototype.STRING = function(string) {
108
+ return '"' + string.string + '"';
109
+ };
110
+
111
+ Handlebars.PrintVisitor.prototype.INTEGER = function(integer) {
112
+ return "INTEGER{" + integer.integer + "}";
113
+ };
114
+
115
+ Handlebars.PrintVisitor.prototype.BOOLEAN = function(bool) {
116
+ return "BOOLEAN{" + bool.bool + "}";
117
+ };
118
+
119
+ Handlebars.PrintVisitor.prototype.ID = function(id) {
120
+ var path = id.parts.join("/");
121
+ if(id.parts.length > 1) {
122
+ return "PATH:" + path;
123
+ } else {
124
+ return "ID:" + path;
125
+ }
126
+ };
127
+
128
+ Handlebars.PrintVisitor.prototype.content = function(content) {
129
+ return this.pad("CONTENT[ '" + content.string + "' ]");
130
+ };
131
+
132
+ Handlebars.PrintVisitor.prototype.comment = function(comment) {
133
+ return this.pad("{{! '" + comment.comment + "' }}");
134
+ };
135
+ // END(BROWSER)
136
+
137
+ exports.PrintVisitor = Handlebars.PrintVisitor;
@@ -0,0 +1,13 @@
1
+ var Handlebars = require("./base");
2
+
3
+ // BEGIN(BROWSER)
4
+
5
+ Handlebars.Visitor = function() {};
6
+
7
+ Handlebars.Visitor.prototype = {
8
+ accept: function(object) {
9
+ return this[object.type](object);
10
+ }
11
+ };
12
+ // END(BROWSER)
13
+
@@ -0,0 +1,68 @@
1
+ var Handlebars = require("./base");
2
+
3
+ // BEGIN(BROWSER)
4
+ Handlebars.VM = {
5
+ template: function(templateSpec) {
6
+ // Just add water
7
+ var container = {
8
+ escapeExpression: Handlebars.Utils.escapeExpression,
9
+ invokePartial: Handlebars.VM.invokePartial,
10
+ programs: [],
11
+ program: function(i, fn, data) {
12
+ var programWrapper = this.programs[i];
13
+ if(data) {
14
+ return Handlebars.VM.program(fn, data);
15
+ } else if(programWrapper) {
16
+ return programWrapper;
17
+ } else {
18
+ programWrapper = this.programs[i] = Handlebars.VM.program(fn);
19
+ return programWrapper;
20
+ }
21
+ },
22
+ programWithDepth: Handlebars.VM.programWithDepth,
23
+ noop: Handlebars.VM.noop
24
+ };
25
+
26
+ return function(context, options) {
27
+ options = options || {};
28
+ return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
29
+ };
30
+ },
31
+
32
+ programWithDepth: function(fn, data, $depth) {
33
+ var args = Array.prototype.slice.call(arguments, 2);
34
+
35
+ return function(context, options) {
36
+ options = options || {};
37
+
38
+ return fn.apply(this, [context, options.data || data].concat(args));
39
+ };
40
+ },
41
+ program: function(fn, data) {
42
+ return function(context, options) {
43
+ options = options || {};
44
+
45
+ return fn(context, options.data || data);
46
+ };
47
+ },
48
+ noop: function() { return ""; },
49
+ invokePartial: function(partial, name, context, helpers, partials, data) {
50
+ var options = { helpers: helpers, partials: partials, data: data };
51
+
52
+ if(partial === undefined) {
53
+ throw new Handlebars.Exception("The partial " + name + " could not be found");
54
+ } else if(partial instanceof Function) {
55
+ return partial(context, options);
56
+ } else if (!Handlebars.compile) {
57
+ throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
58
+ } else {
59
+ partials[name] = Handlebars.compile(partial);
60
+ return partials[name](context, options);
61
+ }
62
+ }
63
+ };
64
+
65
+ Handlebars.template = Handlebars.VM.template;
66
+
67
+ // END(BROWSER)
68
+
@@ -0,0 +1,68 @@
1
+ var Handlebars = require("./base");
2
+
3
+ // BEGIN(BROWSER)
4
+ Handlebars.Exception = function(message) {
5
+ var tmp = Error.prototype.constructor.apply(this, arguments);
6
+
7
+ for (var p in tmp) {
8
+ if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
9
+ }
10
+
11
+ this.message = tmp.message;
12
+ };
13
+ Handlebars.Exception.prototype = new Error();
14
+
15
+ // Build out our basic SafeString type
16
+ Handlebars.SafeString = function(string) {
17
+ this.string = string;
18
+ };
19
+ Handlebars.SafeString.prototype.toString = function() {
20
+ return this.string.toString();
21
+ };
22
+
23
+ (function() {
24
+ var escape = {
25
+ "<": "&lt;",
26
+ ">": "&gt;",
27
+ '"': "&quot;",
28
+ "'": "&#x27;",
29
+ "`": "&#x60;"
30
+ };
31
+
32
+ var badChars = /&(?!\w+;)|[<>"'`]/g;
33
+ var possible = /[&<>"'`]/;
34
+
35
+ var escapeChar = function(chr) {
36
+ return escape[chr] || "&amp;";
37
+ };
38
+
39
+ Handlebars.Utils = {
40
+ escapeExpression: function(string) {
41
+ // don't escape SafeStrings, since they're already safe
42
+ if (string instanceof Handlebars.SafeString) {
43
+ return string.toString();
44
+ } else if (string == null || string === false) {
45
+ return "";
46
+ }
47
+
48
+ if(!possible.test(string)) { return string; }
49
+ return string.replace(badChars, escapeChar);
50
+ },
51
+
52
+ isEmpty: function(value) {
53
+ if (typeof value === "undefined") {
54
+ return true;
55
+ } else if (value === null) {
56
+ return true;
57
+ } else if (value === false) {
58
+ return true;
59
+ } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
60
+ return true;
61
+ } else {
62
+ return false;
63
+ }
64
+ }
65
+ };
66
+ })();
67
+ // END(BROWSER)
68
+
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "handlebars",
3
+ "description": "Extension of the Mustache logicless template language",
4
+ "version": "1.0.5beta",
5
+ "homepage": "http://www.handlebarsjs.com/",
6
+ "keywords": [
7
+ "handlebars mustache template html"
8
+ ],
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git://github.com/kpdecker/handlebars.js.git"
12
+ },
13
+ "engines": {
14
+ "node": ">=0.4.7"
15
+ },
16
+ "dependencies": {
17
+ "optimist": "~0.3",
18
+ "uglify-js": "~1.2"
19
+ },
20
+ "devDependencies": {},
21
+ "main": "lib/handlebars.js",
22
+ "bin": {
23
+ "handlebars": "bin/handlebars"
24
+ }
25
+ }
@@ -0,0 +1,101 @@
1
+ require "spec_helper"
2
+
3
+ class TestContext
4
+ class TestModule
5
+ attr_reader :name, :tests
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ @tests = []
10
+ end
11
+ end
12
+
13
+ attr_reader :modules
14
+
15
+ def initialize
16
+ @modules = []
17
+ end
18
+
19
+ def module(name)
20
+ @modules << TestModule.new(name)
21
+ end
22
+
23
+ def test(name, function)
24
+ @modules.last.tests << [name, function]
25
+ end
26
+ end
27
+
28
+ test_context = TestContext.new
29
+ js_context = Handlebars::Spec::CONTEXT
30
+
31
+ Module.new do
32
+ extend Test::Unit::Assertions
33
+
34
+ def self.js_backtrace(context)
35
+ begin
36
+ context.eval("throw")
37
+ rescue V8::JSError => e
38
+ return e.backtrace(:javascript)
39
+ end
40
+ end
41
+
42
+ js_context["p"] = proc do |str|
43
+ p str
44
+ end
45
+
46
+ js_context["ok"] = proc do |ok, message|
47
+ js_context["$$RSPEC1$$"] = ok
48
+
49
+ result = js_context.eval("!!$$RSPEC1$$")
50
+
51
+ message ||= "#{ok} was not truthy"
52
+
53
+ unless result
54
+ backtrace = js_backtrace(js_context)
55
+ message << "\n#{backtrace.join("\n")}"
56
+ end
57
+
58
+ assert result, message
59
+ end
60
+
61
+ js_context["equals"] = proc do |first, second, message|
62
+ js_context["$$RSPEC1$$"] = first
63
+ js_context["$$RSPEC2$$"] = second
64
+
65
+ result = js_context.eval("$$RSPEC1$$ == $$RSPEC2$$")
66
+
67
+ additional_message = "#{first.inspect} did not == #{second.inspect}"
68
+ message = message ? "#{message} (#{additional_message})" : additional_message
69
+
70
+ unless result
71
+ backtrace = js_backtrace(js_context)
72
+ message << "\n#{backtrace.join("\n")}"
73
+ end
74
+
75
+ assert result, message
76
+ end
77
+
78
+ js_context["equal"] = js_context["equals"]
79
+
80
+ js_context["module"] = proc do |name|
81
+ test_context.module(name)
82
+ end
83
+
84
+ js_context["test"] = proc do |name, function|
85
+ test_context.test(name, function)
86
+ end
87
+
88
+ local = Regexp.escape(File.expand_path(Dir.pwd))
89
+ qunit_spec = File.expand_path("../qunit_spec.js", __FILE__)
90
+ js_context.load(qunit_spec.sub(/^#{local}\//, ''))
91
+ end
92
+
93
+ test_context.modules.each do |mod|
94
+ describe mod.name do
95
+ mod.tests.each do |name, function|
96
+ it name do
97
+ function.call
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,264 @@
1
+ require "spec_helper"
2
+
3
+ describe "Parser" do
4
+ let(:handlebars) { @context["Handlebars"] }
5
+
6
+ before(:all) do
7
+ @compiles = true
8
+ end
9
+
10
+ def program(&block)
11
+ ASTBuilder.build do
12
+ program do
13
+ instance_eval(&block)
14
+ end
15
+ end
16
+ end
17
+
18
+ def ast_for(string)
19
+ ast = handlebars.parse(string)
20
+ handlebars.print(ast)
21
+ end
22
+
23
+ class ASTBuilder
24
+ def self.build(&block)
25
+ ret = new
26
+ ret.evaluate(&block)
27
+ ret.out
28
+ end
29
+
30
+ attr_reader :out
31
+
32
+ def initialize
33
+ @padding = 0
34
+ @out = ""
35
+ end
36
+
37
+ def evaluate(&block)
38
+ instance_eval(&block)
39
+ end
40
+
41
+ def pad(string)
42
+ @out << (" " * @padding) + string + "\n"
43
+ end
44
+
45
+ def with_padding
46
+ @padding += 1
47
+ ret = yield
48
+ @padding -= 1
49
+ ret
50
+ end
51
+
52
+ def program
53
+ pad("PROGRAM:")
54
+ with_padding { yield }
55
+ end
56
+
57
+ def inverse
58
+ pad("{{^}}")
59
+ with_padding { yield }
60
+ end
61
+
62
+ def block
63
+ pad("BLOCK:")
64
+ with_padding { yield }
65
+ end
66
+
67
+ def inverted_block
68
+ pad("INVERSE:")
69
+ with_padding { yield }
70
+ end
71
+
72
+ def mustache(id, params = [], hash = nil)
73
+ hash = " #{hash}" if hash
74
+ pad("{{ #{id} [#{params.join(", ")}]#{hash} }}")
75
+ end
76
+
77
+ def partial(id, context = nil)
78
+ content = id.dup
79
+ content << " #{context}" if context
80
+ pad("{{> #{content} }}")
81
+ end
82
+
83
+ def comment(comment)
84
+ pad("{{! '#{comment}' }}")
85
+ end
86
+
87
+ def multiline_comment(comment)
88
+ pad("{{! '\n#{comment}\n' }}")
89
+ end
90
+
91
+ def content(string)
92
+ pad("CONTENT[ '#{string}' ]")
93
+ end
94
+
95
+ def string(string)
96
+ string.inspect
97
+ end
98
+
99
+ def integer(string)
100
+ "INTEGER{#{string}}"
101
+ end
102
+
103
+ def boolean(string)
104
+ "BOOLEAN{#{string}}"
105
+ end
106
+
107
+ def hash(*pairs)
108
+ "HASH{" + pairs.map {|k,v| "#{k}=#{v}" }.join(", ") + "}"
109
+ end
110
+
111
+ def id(id)
112
+ "ID:#{id}"
113
+ end
114
+
115
+ def path(*parts)
116
+ "PATH:#{parts.join("/")}"
117
+ end
118
+ end
119
+
120
+ it "parses simple mustaches" do
121
+ ast_for("{{foo}}").should == program { mustache id("foo") }
122
+ end
123
+
124
+ it "parses mustaches with paths" do
125
+ ast_for("{{foo/bar}}").should == program { mustache path("foo", "bar") }
126
+ end
127
+
128
+ it "parses mustaches with this/foo" do
129
+ ast_for("{{this/foo}}").should == program { mustache id("foo") }
130
+ end
131
+
132
+ it "parses mustaches with - in a path" do
133
+ ast_for("{{foo-bar}}").should == program { mustache id("foo-bar") }
134
+ end
135
+
136
+ it "parses mustaches with parameters" do
137
+ ast_for("{{foo bar}}").should == program { mustache id("foo"), [id("bar")] }
138
+ end
139
+
140
+ it "parses mustaches with hash arguments" do
141
+ ast_for("{{foo bar=baz}}").should == program do
142
+ mustache id("foo"), [], hash(["bar", id("baz")])
143
+ end
144
+
145
+ ast_for("{{foo bar=1}}").should == program do
146
+ mustache id("foo"), [], hash(["bar", integer("1")])
147
+ end
148
+
149
+ ast_for("{{foo bar=true}}").should == program do
150
+ mustache id("foo"), [], hash(["bar", boolean("true")])
151
+ end
152
+
153
+ ast_for("{{foo bar=false}}").should == program do
154
+ mustache id("foo"), [], hash(["bar", boolean("false")])
155
+ end
156
+
157
+ ast_for("{{foo bar=baz bat=bam}}").should == program do
158
+ mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "ID:bam"])
159
+ end
160
+
161
+ ast_for("{{foo bar=baz bat=\"bam\"}}").should == program do
162
+ mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "\"bam\""])
163
+ end
164
+
165
+ ast_for("{{foo omg bar=baz bat=\"bam\"}}").should == program do
166
+ mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")])
167
+ end
168
+
169
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should == program do
170
+ mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", integer("1")])
171
+ end
172
+
173
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should == program do
174
+ mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("true")])
175
+ end
176
+
177
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should == program do
178
+ mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("false")])
179
+ end
180
+ end
181
+
182
+ it "parses mustaches with string parameters" do
183
+ ast_for("{{foo bar \"baz\" }}").should == program { mustache id("foo"), [id("bar"), string("baz")] }
184
+ end
185
+
186
+ it "parses mustaches with INTEGER parameters" do
187
+ ast_for("{{foo 1}}").should == program { mustache id("foo"), [integer("1")] }
188
+ end
189
+
190
+ it "parses mustaches with BOOLEAN parameters" do
191
+ ast_for("{{foo true}}").should == program { mustache id("foo"), [boolean("true")] }
192
+ ast_for("{{foo false}}").should == program { mustache id("foo"), [boolean("false")] }
193
+ end
194
+
195
+ it "parses contents followed by a mustache" do
196
+ ast_for("foo bar {{baz}}").should == program do
197
+ content "foo bar "
198
+ mustache id("baz")
199
+ end
200
+ end
201
+
202
+ it "parses a partial" do
203
+ ast_for("{{> foo }}").should == program { partial id("foo") }
204
+ end
205
+
206
+ it "parses a partial with context" do
207
+ ast_for("{{> foo bar}}").should == program { partial id("foo"), id("bar") }
208
+ end
209
+
210
+ it "parses a comment" do
211
+ ast_for("{{! this is a comment }}").should == program do
212
+ comment " this is a comment "
213
+ end
214
+ end
215
+
216
+ it "parses a multi-line comment" do
217
+ ast_for("{{!\nthis is a multi-line comment\n}}").should == program do
218
+ multiline_comment "this is a multi-line comment"
219
+ end
220
+ end
221
+
222
+ it "parses an inverse section" do
223
+ ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should == program do
224
+ block do
225
+ mustache id("foo")
226
+
227
+ program do
228
+ content " bar "
229
+ end
230
+
231
+ inverse do
232
+ content " baz "
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ it "parses a standalone inverse section" do
239
+ ast_for("{{^foo}}bar{{/foo}}").should == program do
240
+ inverted_block do
241
+ mustache id("foo")
242
+
243
+ program do
244
+ content "bar"
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ it "raises if there's a Parse error" do
251
+ lambda { ast_for("{{foo}") }.should raise_error(V8::JSError, /Parse error on line 1/)
252
+ lambda { ast_for("{{foo &}}")}.should raise_error(V8::JSError, /Parse error on line 1/)
253
+ lambda { ast_for("{{#goodbyes}}{{/hellos}}") }.should raise_error(V8::JSError, /goodbyes doesn't match hellos/)
254
+ end
255
+
256
+ it "knows how to report the correct line number in errors" do
257
+ lambda { ast_for("hello\nmy\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 3/m)
258
+ lambda { ast_for("hello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 5/m)
259
+ end
260
+
261
+ it "knows how to report the correct line number in errors when the first character is a newline" do
262
+ lambda { ast_for("\n\nhello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 7/m)
263
+ end
264
+ end