handlebars 0.3.2 → 0.4.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 (34) hide show
  1. data/lib/handlebars/context.rb +3 -5
  2. data/lib/handlebars/version.rb +1 -1
  3. data/spec/handlebars_spec.rb +3 -3
  4. data/vendor/handlebars/.gitignore +3 -1
  5. data/vendor/handlebars/.jshintrc +2 -0
  6. data/vendor/handlebars/.npmignore +0 -1
  7. data/vendor/handlebars/.rspec +1 -0
  8. data/vendor/handlebars/Gemfile +1 -1
  9. data/vendor/handlebars/LICENSE +0 -1
  10. data/vendor/handlebars/README.markdown +8 -6
  11. data/vendor/handlebars/Rakefile +14 -21
  12. data/vendor/handlebars/bench/benchwarmer.js +1 -1
  13. data/vendor/handlebars/bench/handlebars.js +43 -34
  14. data/vendor/handlebars/bin/handlebars +56 -2
  15. data/vendor/handlebars/dist/handlebars.js +2201 -0
  16. data/vendor/handlebars/dist/handlebars.runtime.js +321 -0
  17. data/vendor/handlebars/lib/handlebars/base.js +68 -15
  18. data/vendor/handlebars/lib/handlebars/compiler/ast.js +50 -20
  19. data/vendor/handlebars/lib/handlebars/compiler/base.js +7 -13
  20. data/vendor/handlebars/lib/handlebars/compiler/compiler.js +758 -299
  21. data/vendor/handlebars/lib/handlebars/compiler/printer.js +24 -30
  22. data/vendor/handlebars/lib/handlebars/runtime.js +23 -3
  23. data/vendor/handlebars/lib/handlebars/utils.js +9 -10
  24. data/vendor/handlebars/package.json +15 -5
  25. data/vendor/handlebars/spec/acceptance_spec.rb +5 -5
  26. data/vendor/handlebars/spec/parser_spec.rb +201 -32
  27. data/vendor/handlebars/spec/qunit_spec.js +462 -159
  28. data/vendor/handlebars/spec/spec_helper.rb +7 -7
  29. data/vendor/handlebars/spec/tokenizer_spec.rb +55 -8
  30. data/vendor/handlebars/src/handlebars.l +14 -3
  31. data/vendor/handlebars/src/handlebars.yy +15 -5
  32. data/vendor/handlebars/src/parser-prefix.js +1 -0
  33. data/vendor/handlebars/src/parser-suffix.js +4 -0
  34. metadata +8 -3
@@ -18,31 +18,17 @@ Handlebars.PrintVisitor.prototype.pad = function(string, newline) {
18
18
  };
19
19
 
20
20
  Handlebars.PrintVisitor.prototype.program = function(program) {
21
- var out = this.pad("PROGRAM:"),
21
+ var out = "",
22
22
  statements = program.statements,
23
23
  inverse = program.inverse,
24
24
  i, l;
25
25
 
26
- this.padding++;
27
-
28
26
  for(i=0, l=statements.length; i<l; i++) {
29
27
  out = out + this.accept(statements[i]);
30
28
  }
31
29
 
32
30
  this.padding--;
33
31
 
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
32
  return out;
47
33
  };
48
34
 
@@ -52,25 +38,25 @@ Handlebars.PrintVisitor.prototype.block = function(block) {
52
38
  out = out + this.pad("BLOCK:");
53
39
  this.padding++;
54
40
  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);
41
+ if (block.program) {
42
+ out = out + this.pad("PROGRAM:");
43
+ this.padding++;
44
+ out = out + this.accept(block.program);
45
+ this.padding--;
46
+ }
47
+ if (block.inverse) {
48
+ if (block.program) { this.padding++; }
49
+ out = out + this.pad("{{^}}");
50
+ this.padding++;
51
+ out = out + this.accept(block.inverse);
52
+ this.padding--;
53
+ if (block.program) { this.padding--; }
54
+ }
68
55
  this.padding--;
69
56
 
70
57
  return out;
71
58
  };
72
59
 
73
-
74
60
  Handlebars.PrintVisitor.prototype.mustache = function(mustache) {
75
61
  var params = mustache.params, paramStrings = [], hash;
76
62
 
@@ -86,7 +72,7 @@ Handlebars.PrintVisitor.prototype.mustache = function(mustache) {
86
72
  };
87
73
 
88
74
  Handlebars.PrintVisitor.prototype.partial = function(partial) {
89
- var content = this.accept(partial.id);
75
+ var content = this.accept(partial.partialName);
90
76
  if(partial.context) { content = content + " " + this.accept(partial.context); }
91
77
  return this.pad("{{> " + content + " }}");
92
78
  };
@@ -125,6 +111,14 @@ Handlebars.PrintVisitor.prototype.ID = function(id) {
125
111
  }
126
112
  };
127
113
 
114
+ Handlebars.PrintVisitor.prototype.PARTIAL_NAME = function(partialName) {
115
+ return "PARTIAL:" + partialName.name;
116
+ };
117
+
118
+ Handlebars.PrintVisitor.prototype.DATA = function(data) {
119
+ return "@" + data.id;
120
+ };
121
+
128
122
  Handlebars.PrintVisitor.prototype.content = function(content) {
129
123
  return this.pad("CONTENT[ '" + content.string + "' ]");
130
124
  };
@@ -20,12 +20,32 @@ Handlebars.VM = {
20
20
  }
21
21
  },
22
22
  programWithDepth: Handlebars.VM.programWithDepth,
23
- noop: Handlebars.VM.noop
23
+ noop: Handlebars.VM.noop,
24
+ compilerInfo: null
24
25
  };
25
26
 
26
27
  return function(context, options) {
27
28
  options = options || {};
28
- return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
29
+ var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
30
+
31
+ var compilerInfo = container.compilerInfo || [],
32
+ compilerRevision = compilerInfo[0] || 1,
33
+ currentRevision = Handlebars.COMPILER_REVISION;
34
+
35
+ if (compilerRevision !== currentRevision) {
36
+ if (compilerRevision < currentRevision) {
37
+ var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
38
+ compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
39
+ throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
40
+ "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
41
+ } else {
42
+ // Use the embedded version info since the runtime doesn't know about this revision yet
43
+ throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
44
+ "Please update your runtime to a newer version ("+compilerInfo[1]+").";
45
+ }
46
+ }
47
+
48
+ return result;
29
49
  };
30
50
  },
31
51
 
@@ -56,7 +76,7 @@ Handlebars.VM = {
56
76
  } else if (!Handlebars.compile) {
57
77
  throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
58
78
  } else {
59
- partials[name] = Handlebars.compile(partial);
79
+ partials[name] = Handlebars.compile(partial, {data: data !== undefined});
60
80
  return partials[name](context, options);
61
81
  }
62
82
  }
@@ -1,14 +1,16 @@
1
1
  var Handlebars = require("./base");
2
2
 
3
3
  // BEGIN(BROWSER)
4
+
5
+ var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
6
+
4
7
  Handlebars.Exception = function(message) {
5
8
  var tmp = Error.prototype.constructor.apply(this, arguments);
6
9
 
7
- for (var p in tmp) {
8
- if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
10
+ // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
11
+ for (var idx = 0; idx < errorProps.length; idx++) {
12
+ this[errorProps[idx]] = tmp[errorProps[idx]];
9
13
  }
10
-
11
- this.message = tmp.message;
12
14
  };
13
15
  Handlebars.Exception.prototype = new Error();
14
16
 
@@ -22,6 +24,7 @@ Handlebars.SafeString.prototype.toString = function() {
22
24
 
23
25
  (function() {
24
26
  var escape = {
27
+ "&": "&amp;",
25
28
  "<": "&lt;",
26
29
  ">": "&gt;",
27
30
  '"': "&quot;",
@@ -29,7 +32,7 @@ Handlebars.SafeString.prototype.toString = function() {
29
32
  "`": "&#x60;"
30
33
  };
31
34
 
32
- var badChars = /&(?!\w+;)|[<>"'`]/g;
35
+ var badChars = /[&<>"'`]/g;
33
36
  var possible = /[&<>"'`]/;
34
37
 
35
38
  var escapeChar = function(chr) {
@@ -50,11 +53,7 @@ Handlebars.SafeString.prototype.toString = function() {
50
53
  },
51
54
 
52
55
  isEmpty: function(value) {
53
- if (typeof value === "undefined") {
54
- return true;
55
- } else if (value === null) {
56
- return true;
57
- } else if (value === false) {
56
+ if (!value && value !== 0) {
58
57
  return true;
59
58
  } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
60
59
  return true;
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "handlebars",
3
3
  "description": "Extension of the Mustache logicless template language",
4
- "version": "1.0.5beta",
4
+ "version": "1.0.8",
5
5
  "homepage": "http://www.handlebarsjs.com/",
6
6
  "keywords": [
7
7
  "handlebars mustache template html"
8
8
  ],
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "git://github.com/kpdecker/handlebars.js.git"
11
+ "url": "git://github.com/wycats/handlebars.js.git"
12
12
  },
13
13
  "engines": {
14
14
  "node": ">=0.4.7"
@@ -17,9 +17,19 @@
17
17
  "optimist": "~0.3",
18
18
  "uglify-js": "~1.2"
19
19
  },
20
- "devDependencies": {},
20
+ "devDependencies": {
21
+ "benchmark": "~1.0",
22
+ "dust": "~0.3",
23
+ "jison": "~0.3",
24
+ "mocha": "*",
25
+ "mustache": "~0.7.2"
26
+ },
21
27
  "main": "lib/handlebars.js",
22
28
  "bin": {
23
29
  "handlebars": "bin/handlebars"
24
- }
25
- }
30
+ },
31
+ "scripts": {
32
+ "test": "node_modules/.bin/mocha -u qunit spec/qunit_spec.js"
33
+ },
34
+ "optionalDependencies": {}
35
+ }
@@ -39,11 +39,11 @@ Module.new do
39
39
  end
40
40
  end
41
41
 
42
- js_context["p"] = proc do |str|
42
+ js_context["p"] = proc do |this, str|
43
43
  p str
44
44
  end
45
45
 
46
- js_context["ok"] = proc do |ok, message|
46
+ js_context["ok"] = proc do |this, ok, message|
47
47
  js_context["$$RSPEC1$$"] = ok
48
48
 
49
49
  result = js_context.eval("!!$$RSPEC1$$")
@@ -58,7 +58,7 @@ Module.new do
58
58
  assert result, message
59
59
  end
60
60
 
61
- js_context["equals"] = proc do |first, second, message|
61
+ js_context["equals"] = proc do |this, first, second, message|
62
62
  js_context["$$RSPEC1$$"] = first
63
63
  js_context["$$RSPEC2$$"] = second
64
64
 
@@ -77,11 +77,11 @@ Module.new do
77
77
 
78
78
  js_context["equal"] = js_context["equals"]
79
79
 
80
- js_context["module"] = proc do |name|
80
+ js_context["suite"] = proc do |this, name|
81
81
  test_context.module(name)
82
82
  end
83
83
 
84
- js_context["test"] = proc do |name, function|
84
+ js_context["test"] = proc do |this, name, function|
85
85
  test_context.test(name, function)
86
86
  end
87
87
 
@@ -7,11 +7,9 @@ describe "Parser" do
7
7
  @compiles = true
8
8
  end
9
9
 
10
- def program(&block)
10
+ def root(&block)
11
11
  ASTBuilder.build do
12
- program do
13
- instance_eval(&block)
14
- end
12
+ instance_eval(&block)
15
13
  end
16
14
  end
17
15
 
@@ -112,115 +110,143 @@ describe "Parser" do
112
110
  "ID:#{id}"
113
111
  end
114
112
 
113
+ def data(id)
114
+ "@#{id}"
115
+ end
116
+
117
+ def partial_name(name)
118
+ "PARTIAL:#{name}"
119
+ end
120
+
115
121
  def path(*parts)
116
122
  "PATH:#{parts.join("/")}"
117
123
  end
118
124
  end
119
125
 
120
126
  it "parses simple mustaches" do
121
- ast_for("{{foo}}").should == program { mustache id("foo") }
127
+ ast_for("{{foo}}").should == root { mustache id("foo") }
128
+ end
129
+
130
+ it "parses simple mustaches with data" do
131
+ ast_for("{{@foo}}").should == root { mustache data("foo") }
122
132
  end
123
133
 
124
134
  it "parses mustaches with paths" do
125
- ast_for("{{foo/bar}}").should == program { mustache path("foo", "bar") }
135
+ ast_for("{{foo/bar}}").should == root { mustache path("foo", "bar") }
126
136
  end
127
137
 
128
138
  it "parses mustaches with this/foo" do
129
- ast_for("{{this/foo}}").should == program { mustache id("foo") }
139
+ ast_for("{{this/foo}}").should == root { mustache id("foo") }
130
140
  end
131
141
 
132
142
  it "parses mustaches with - in a path" do
133
- ast_for("{{foo-bar}}").should == program { mustache id("foo-bar") }
143
+ ast_for("{{foo-bar}}").should == root { mustache id("foo-bar") }
134
144
  end
135
145
 
136
146
  it "parses mustaches with parameters" do
137
- ast_for("{{foo bar}}").should == program { mustache id("foo"), [id("bar")] }
147
+ ast_for("{{foo bar}}").should == root { mustache id("foo"), [id("bar")] }
138
148
  end
139
149
 
140
150
  it "parses mustaches with hash arguments" do
141
- ast_for("{{foo bar=baz}}").should == program do
151
+ ast_for("{{foo bar=baz}}").should == root do
142
152
  mustache id("foo"), [], hash(["bar", id("baz")])
143
153
  end
144
154
 
145
- ast_for("{{foo bar=1}}").should == program do
155
+ ast_for("{{foo bar=1}}").should == root do
146
156
  mustache id("foo"), [], hash(["bar", integer("1")])
147
157
  end
148
158
 
149
- ast_for("{{foo bar=true}}").should == program do
159
+ ast_for("{{foo bar=true}}").should == root do
150
160
  mustache id("foo"), [], hash(["bar", boolean("true")])
151
161
  end
152
162
 
153
- ast_for("{{foo bar=false}}").should == program do
163
+ ast_for("{{foo bar=false}}").should == root do
154
164
  mustache id("foo"), [], hash(["bar", boolean("false")])
155
165
  end
156
166
 
157
- ast_for("{{foo bar=baz bat=bam}}").should == program do
167
+ ast_for("{{foo bar=@baz}}").should == root do
168
+ mustache id("foo"), [], hash(["bar", data("baz")])
169
+ end
170
+
171
+ ast_for("{{foo bar=baz bat=bam}}").should == root do
158
172
  mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "ID:bam"])
159
173
  end
160
174
 
161
- ast_for("{{foo bar=baz bat=\"bam\"}}").should == program do
175
+ ast_for("{{foo bar=baz bat=\"bam\"}}").should == root do
162
176
  mustache id("foo"), [], hash(["bar", "ID:baz"], ["bat", "\"bam\""])
163
177
  end
164
178
 
165
- ast_for("{{foo omg bar=baz bat=\"bam\"}}").should == program do
179
+ ast_for("{{foo bat='bam'}}").should == root do
180
+ mustache id("foo"), [], hash(["bat", "\"bam\""])
181
+ end
182
+
183
+ ast_for("{{foo omg bar=baz bat=\"bam\"}}").should == root do
166
184
  mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")])
167
185
  end
168
186
 
169
- ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should == program do
187
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}").should == root do
170
188
  mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", integer("1")])
171
189
  end
172
190
 
173
- ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should == program do
191
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}").should == root do
174
192
  mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("true")])
175
193
  end
176
194
 
177
- ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should == program do
195
+ ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}").should == root do
178
196
  mustache id("foo"), [id("omg")], hash(["bar", id("baz")], ["bat", string("bam")], ["baz", boolean("false")])
179
197
  end
180
198
  end
181
199
 
182
200
  it "parses mustaches with string parameters" do
183
- ast_for("{{foo bar \"baz\" }}").should == program { mustache id("foo"), [id("bar"), string("baz")] }
201
+ ast_for("{{foo bar \"baz\" }}").should == root { mustache id("foo"), [id("bar"), string("baz")] }
184
202
  end
185
203
 
186
204
  it "parses mustaches with INTEGER parameters" do
187
- ast_for("{{foo 1}}").should == program { mustache id("foo"), [integer("1")] }
205
+ ast_for("{{foo 1}}").should == root { mustache id("foo"), [integer("1")] }
188
206
  end
189
207
 
190
208
  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")] }
209
+ ast_for("{{foo true}}").should == root { mustache id("foo"), [boolean("true")] }
210
+ ast_for("{{foo false}}").should == root { mustache id("foo"), [boolean("false")] }
211
+ end
212
+
213
+ it "parses mutaches with DATA parameters" do
214
+ ast_for("{{foo @bar}}").should == root { mustache id("foo"), [data("bar")] }
193
215
  end
194
216
 
195
217
  it "parses contents followed by a mustache" do
196
- ast_for("foo bar {{baz}}").should == program do
218
+ ast_for("foo bar {{baz}}").should == root do
197
219
  content "foo bar "
198
220
  mustache id("baz")
199
221
  end
200
222
  end
201
223
 
202
224
  it "parses a partial" do
203
- ast_for("{{> foo }}").should == program { partial id("foo") }
225
+ ast_for("{{> foo }}").should == root { partial partial_name("foo") }
204
226
  end
205
227
 
206
228
  it "parses a partial with context" do
207
- ast_for("{{> foo bar}}").should == program { partial id("foo"), id("bar") }
229
+ ast_for("{{> foo bar}}").should == root { partial partial_name("foo"), id("bar") }
230
+ end
231
+
232
+ it "parses a partial with a complex name" do
233
+ ast_for("{{> shared/partial}}").should == root { partial partial_name("shared/partial") }
208
234
  end
209
235
 
210
236
  it "parses a comment" do
211
- ast_for("{{! this is a comment }}").should == program do
237
+ ast_for("{{! this is a comment }}").should == root do
212
238
  comment " this is a comment "
213
239
  end
214
240
  end
215
241
 
216
242
  it "parses a multi-line comment" do
217
- ast_for("{{!\nthis is a multi-line comment\n}}").should == program do
243
+ ast_for("{{!\nthis is a multi-line comment\n}}").should == root do
218
244
  multiline_comment "this is a multi-line comment"
219
245
  end
220
246
  end
221
247
 
222
248
  it "parses an inverse section" do
223
- ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should == program do
249
+ ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should == root do
224
250
  block do
225
251
  mustache id("foo")
226
252
 
@@ -235,12 +261,136 @@ describe "Parser" do
235
261
  end
236
262
  end
237
263
 
238
- it "parses a standalone inverse section" do
239
- ast_for("{{^foo}}bar{{/foo}}").should == program do
240
- inverted_block do
264
+ it "parses an inverse ('else'-style) section" do
265
+ ast_for("{{#foo}} bar {{else}} baz {{/foo}}").should == root do
266
+ block do
267
+ mustache id("foo")
268
+
269
+ program do
270
+ content " bar "
271
+ end
272
+
273
+ inverse do
274
+ content " baz "
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ it "parses empty blocks" do
281
+ ast_for("{{#foo}}{{/foo}}").should == root do
282
+ block do
241
283
  mustache id("foo")
242
284
 
243
285
  program do
286
+ # empty program
287
+ end
288
+ end
289
+ end
290
+ end
291
+
292
+ it "parses empty blocks with empty inverse section" do
293
+ ast_for("{{#foo}}{{^}}{{/foo}}").should == root do
294
+ block do
295
+ mustache id("foo")
296
+
297
+ program do
298
+ # empty program
299
+ end
300
+
301
+ inverse do
302
+ # empty inverse
303
+ end
304
+ end
305
+ end
306
+ end
307
+
308
+ it "parses empty blocks with empty inverse ('else'-style) section" do
309
+ ast_for("{{#foo}}{{else}}{{/foo}}").should == root do
310
+ block do
311
+ mustache id("foo")
312
+
313
+ program do
314
+ # empty program
315
+ end
316
+
317
+ inverse do
318
+ # empty inverse
319
+ end
320
+ end
321
+ end
322
+ end
323
+
324
+ it "parses non-empty blocks with empty inverse section" do
325
+ ast_for("{{#foo}} bar {{^}}{{/foo}}").should == root do
326
+ block do
327
+ mustache id("foo")
328
+
329
+ program do
330
+ content " bar "
331
+ end
332
+
333
+ inverse do
334
+ # empty inverse
335
+ end
336
+ end
337
+ end
338
+ end
339
+
340
+ it "parses non-empty blocks with empty inverse ('else'-style) section" do
341
+ ast_for("{{#foo}} bar {{else}}{{/foo}}").should == root do
342
+ block do
343
+ mustache id("foo")
344
+
345
+ program do
346
+ content " bar "
347
+ end
348
+
349
+ inverse do
350
+ # empty inverse
351
+ end
352
+ end
353
+ end
354
+ end
355
+
356
+ it "parses empty blocks with non-empty inverse section" do
357
+ ast_for("{{#foo}}{{^}} bar {{/foo}}").should == root do
358
+ block do
359
+ mustache id("foo")
360
+
361
+ program do
362
+ # empty program
363
+ end
364
+
365
+ inverse do
366
+ content " bar "
367
+ end
368
+ end
369
+ end
370
+ end
371
+
372
+ it "parses empty blocks with non-empty inverse ('else'-style) section" do
373
+ ast_for("{{#foo}}{{else}} bar {{/foo}}").should == root do
374
+ block do
375
+ mustache id("foo")
376
+
377
+ program do
378
+ # empty program
379
+ end
380
+
381
+ inverse do
382
+ content " bar "
383
+ end
384
+ end
385
+ end
386
+ end
387
+
388
+ it "parses a standalone inverse section" do
389
+ ast_for("{{^foo}}bar{{/foo}}").should == root do
390
+ block do
391
+ mustache id("foo")
392
+
393
+ inverse do
244
394
  content "bar"
245
395
  end
246
396
  end
@@ -261,4 +411,23 @@ describe "Parser" do
261
411
  it "knows how to report the correct line number in errors when the first character is a newline" do
262
412
  lambda { ast_for("\n\nhello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 7/m)
263
413
  end
414
+
415
+ context "externally compiled AST" do
416
+
417
+ it "can pass through an already-compiled AST" do
418
+ ast_for(@context.eval('new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]);')).should == root do
419
+ content "Hello"
420
+ end
421
+ end
422
+
423
+ it "can pass through an already-compiled AST via compile/precompile" do
424
+ @context = Handlebars::Spec::FULL_CONTEXT
425
+
426
+ code = 'Handlebars.compile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))();'
427
+ @context.eval(code).should == "Hello"
428
+
429
+ code = @context.eval 'Handlebars.precompile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))'
430
+ @context.eval("(#{code})(this)").should == "Hello"
431
+ end
432
+ end
264
433
  end