handlebars 0.0.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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