handlebars 0.3.2 → 0.4.0

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