hogan_assets 1.0.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.
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use @hogan_assets
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hogan_assets.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hogan_assets (1.0.0)
5
+ execjs (>= 1.2.9)
6
+ sprockets (>= 2.0.3)
7
+ tilt (>= 1.3.3)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ execjs (1.2.13)
13
+ multi_json (~> 1.0)
14
+ hike (1.2.1)
15
+ multi_json (1.0.4)
16
+ rack (1.3.5)
17
+ sprockets (2.1.2)
18
+ hike (~> 1.2)
19
+ rack (~> 1.0)
20
+ tilt (~> 1.1, != 1.3.0)
21
+ tilt (1.3.3)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ hogan_assets!
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Les Hill
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # HoganAssets
2
+
3
+ **HoganAssets** compiles your [mustache](http://mustache.github.com/) templates with [hogan.js](http://twitter.github.com/hogan.js/) on **sprockets** and the Rails asset pipeline.
4
+
5
+ **hogan.js** is a templating engine developed at [Twitter](http://twitter.com) that follows the **mustache** spec and compiles the templates to JavaScript. The first bit is *cool*, since `mustache` is *cool*. The second bit is **awesome and full of win** because we can now compile our **mustache** templates on the server using the asset pipeline/sprockets.
6
+
7
+ This gem contains **hogan.js v1.0.2**
8
+
9
+ ## Installation
10
+
11
+ ### Installation with Rails 3.1+
12
+
13
+ Add this to your `Gemfile` as part of the `assets` group
14
+
15
+ group :assets do
16
+ gem 'hogan_assets'
17
+ end
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Require `hogan.js` somewhere in your JavaScript manifest, for example in `application.js` if you are using Rails 3.1+:
24
+
25
+ //= require hogan.js
26
+
27
+ Locate your `.mustache` templates with your other JavaScript assets, usually in `app/assets/templates` or `app/assets/javascripts/templates`.
28
+ Require your templates with `require_tree`:
29
+
30
+ //= require_tree templates
31
+
32
+ Templates are named for the sub-path below `require_tree`. For example, the file `app/assets/javascripts/templates/pages/person.mustache` will be named `pages/person`.
33
+
34
+ ### Installation with sprockets
35
+
36
+ Add this line to your `Gemfile`:
37
+
38
+ gem 'hogan_assets'
39
+
40
+ And then execute:
41
+
42
+ $ bundle
43
+
44
+ Require `hogan.js` somewhere in your JavaScript.
45
+
46
+ *TODO* Templates?
47
+
48
+ ## Usage
49
+
50
+ Templates are compiled to a global JavaScript object named `HoganTemplates`. To render `pages/person`:
51
+
52
+ HoganTemplates['pages/person'].render(context, partials);
53
+
54
+ # Author
55
+
56
+ I made this because I <3 **mustache** and want to use it in Rails. Follow me on [Github](https://github.com/leshill) and [Twitter](https://twitter.com/leshill).
57
+
58
+ ## Contributing
59
+
60
+ 1. Fork it
61
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
62
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
63
+ 4. Push to the branch (`git push origin my-new-feature`)
64
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/hogan_assets/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Les Hill"]
6
+ gem.email = ["leshill@gmail.com"]
7
+ gem.description = %q{Use compiled hogan.js (mustache) JavaScript templates with sprockets and the Rails asset pipeline.}
8
+ gem.summary = %q{Use compiled hogan.js (mustache) JavaScript templates with sprockets and the Rails asset pipeline.}
9
+ gem.homepage = ""
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "hogan_assets"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = HoganAssets::VERSION
17
+
18
+ gem.add_runtime_dependency "execjs", ">= 1.2.9"
19
+ gem.add_runtime_dependency "tilt", ">= 1.3.3"
20
+ gem.add_runtime_dependency "sprockets", ">= 2.0.3"
21
+ end
@@ -0,0 +1,13 @@
1
+ require 'hogan_assets/version'
2
+
3
+ module HoganAssets
4
+ autoload(:Hogan, 'hogan_assets/hogan')
5
+ autoload(:Tilt, 'hogan_assets/tilt')
6
+
7
+ if defined?(Rails)
8
+ require 'hogan_assets/engine'
9
+ else
10
+ require 'sprockets'
11
+ Sprockets.register_engine '.mustache', Tilt
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module HoganAssets
2
+ class Engine < ::Rails::Engine
3
+ initializer "sprockets.hogan", :after => "sprockets.environment", :group => :all do |app|
4
+ next unless app.assets
5
+ app.assets.register_engine('.mustache', Tilt)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,31 @@
1
+ # Based on https://github.com/josh/ruby-coffee-script
2
+ require 'execjs'
3
+ require 'pathname'
4
+
5
+ module HoganAssets
6
+ class Hogan
7
+ class << self
8
+ def compile(source)
9
+ context.eval("Hogan.compile(#{source.inspect}, {asString: true})")
10
+ end
11
+
12
+ private
13
+
14
+ def context
15
+ @context ||= ExecJS.compile(source)
16
+ end
17
+
18
+ def source
19
+ @source ||= path.read
20
+ end
21
+
22
+ def path
23
+ @path ||= assets_path.join('javascripts', 'hogan.js')
24
+ end
25
+
26
+ def assets_path
27
+ @assets_path ||= Pathname(__FILE__).dirname.join('..','..','vendor','assets')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ require 'tilt'
2
+
3
+ module HoganAssets
4
+ class Tilt < Tilt::Template
5
+ self.default_mime_type = 'application/javascript'
6
+
7
+ def evaluate(scope, locals, &block)
8
+ compiled_template = Hogan.compile(data)
9
+ code = data.inspect
10
+ template_name = scope.logical_path.inspect
11
+ <<-TEMPLATE
12
+ (function() {
13
+ this.HoganTemplates || (this.HoganTemplates = {});
14
+ this.HoganTemplates[#{template_name}] = new HoganTemplate(#{code});
15
+ this.HoganTemplates[#{template_name}].r = #{compiled_template};
16
+ return HoganTemplates[#{template_name}];
17
+ }).call(this);
18
+ TEMPLATE
19
+ end
20
+
21
+ protected
22
+
23
+ def prepare; end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module HoganAssets
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ module HoganAssets
4
+ class TiltTest < Test::Unit::TestCase
5
+ def test_mime_type
6
+ assert_equal 'application/javascript', HoganAssets::Tilt.default_mime_type
7
+ end
8
+
9
+ def test_render
10
+ scope = Class.new do
11
+ def logical_path ; 'path/to/template' ; end
12
+ end.new
13
+
14
+ template = HoganAssets::Tilt.new('/myapp/app/assets/templates/path/to/template.mustache') { "This is {{mustache}}" }
15
+ assert_equal <<END_EXPECTED, template.render(scope, {})
16
+ (function() {
17
+ this.HoganTemplates || (this.HoganTemplates = {});
18
+ this.HoganTemplates["path/to/template"] = new HoganTemplate("This is {{mustache}}");
19
+ this.HoganTemplates["path/to/template"].r = function(cx,p){var c = [cx];var b = "";var _ = this;b += "This is ";b += (_.v(_.f("mustache",c,p,0)));return b;;};
20
+ return HoganTemplates["path/to/template"];
21
+ }).call(this);
22
+ END_EXPECTED
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ require 'hogan_assets/tilt'
2
+ require 'hogan_assets/hogan'
3
+
4
+ require 'test/unit'
@@ -0,0 +1,514 @@
1
+ /*
2
+ * Copyright 2011 Twitter, Inc.
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ var HoganTemplate = (function () {
17
+
18
+ function constructor(text) {
19
+ this.text = text;
20
+ }
21
+
22
+ constructor.prototype = {
23
+ // render: replaced by generated code.
24
+ r: function (context, partials) { return ''; },
25
+
26
+ // variable escaping
27
+ v: hoganEscape,
28
+
29
+ render: function render(context, partials) {
30
+ return this.r(context, partials);
31
+ },
32
+
33
+ // tries to find a partial in the curent scope and render it
34
+ rp: function(name, context, partials, indent) {
35
+ var partial = partials[name];
36
+
37
+ if (!partial) {
38
+ return '';
39
+ }
40
+
41
+ return partial.render(context, partials);
42
+ },
43
+
44
+ // render a section
45
+ rs: function(context, partials, section) {
46
+ var buf = '',
47
+ tail = context[context.length - 1];
48
+
49
+ if (!isArray(tail)) {
50
+ buf = section(context, partials);
51
+ return buf;
52
+ }
53
+
54
+ for (var i = 0; i < tail.length; i++) {
55
+ context.push(tail[i]);
56
+ buf += section(context, partials);
57
+ context.pop();
58
+ }
59
+ return buf;
60
+ },
61
+
62
+ // maybe start a section
63
+ s: function(val, ctx, partials, inverted, start, end) {
64
+ var pass;
65
+
66
+ if (isArray(val) && val.length === 0) {
67
+ return false;
68
+ }
69
+
70
+ if (!inverted && typeof val == 'function') {
71
+ val = this.ls(val, ctx, partials, start, end);
72
+ }
73
+
74
+ pass = (val === '') || !!val;
75
+
76
+ if (!inverted && pass && ctx) {
77
+ ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
78
+ }
79
+
80
+ return pass;
81
+ },
82
+
83
+ // find values with dotted names
84
+ d: function(key, ctx, partials, returnFound) {
85
+
86
+ var names = key.split('.'),
87
+ val = this.f(names[0], ctx, partials, returnFound),
88
+ cx = null;
89
+
90
+ if (key === '.' && isArray(ctx[ctx.length - 2])) {
91
+ return ctx[ctx.length - 1];
92
+ }
93
+
94
+ for (var i = 1; i < names.length; i++) {
95
+ if (val && typeof val == 'object' && names[i] in val) {
96
+ cx = val;
97
+ val = val[names[i]];
98
+ } else {
99
+ val = '';
100
+ }
101
+ }
102
+
103
+ if (returnFound && !val) {
104
+ return false;
105
+ }
106
+
107
+ if (!returnFound && typeof val == 'function') {
108
+ ctx.push(cx);
109
+ val = this.lv(val, ctx, partials);
110
+ ctx.pop();
111
+ }
112
+
113
+ return val;
114
+ },
115
+
116
+ // find values with normal names
117
+ f: function(key, ctx, partials, returnFound) {
118
+ var val = false,
119
+ v = null,
120
+ found = false;
121
+
122
+ for (var i = ctx.length - 1; i >= 0; i--) {
123
+ v = ctx[i];
124
+ if (v && typeof v == 'object' && key in v) {
125
+ val = v[key];
126
+ found = true;
127
+ break;
128
+ }
129
+ }
130
+
131
+ if (!found) {
132
+ return (returnFound) ? false : "";
133
+ }
134
+
135
+ if (!returnFound && typeof val == 'function') {
136
+ val = this.lv(val, ctx, partials);
137
+ }
138
+
139
+ return val;
140
+ },
141
+
142
+ // higher order templates
143
+ ho: function(val, cx, partials, text) {
144
+ var t = val.call(cx, text, function(t) {
145
+ return Hogan.compile(t).render(cx);
146
+ });
147
+ var s = Hogan.compile(t.toString()).render(cx, partials);
148
+ this.b = s;
149
+ return false;
150
+ },
151
+
152
+ // higher order template result buffer
153
+ b: '',
154
+
155
+ // lambda replace section
156
+ ls: function(val, ctx, partials, start, end) {
157
+ var cx = ctx[ctx.length - 1],
158
+ t = val.call(cx);
159
+
160
+ if (val.length > 0) {
161
+ return this.ho(val, cx, partials, this.text.substring(start, end));
162
+ }
163
+
164
+ if (typeof t == 'function') {
165
+ return this.ho(t, cx, partials, this.text.substring(start, end));
166
+ }
167
+ return t;
168
+ },
169
+
170
+ // lambda replace variable
171
+ lv: function(val, ctx, partials) {
172
+ var cx = ctx[ctx.length - 1];
173
+ return Hogan.compile(val.call(cx).toString()).render(cx, partials);
174
+ }
175
+ };
176
+
177
+ var rAmp = /&/g, rLt = /</g, rGt = />/g, rApos =/\'/g,
178
+ rQuot = /\"/g, hChars =/[&<>\"\']/;
179
+ function hoganEscape(str) {
180
+ var s = String(str === null ? '' : str);
181
+ return hChars.test(s) ? s.replace(rAmp,'&amp;')
182
+ .replace(rLt,'&lt;').replace(rGt,'&gt;')
183
+ .replace(rApos,'&#39;').replace(rQuot, '&quot;') : s;
184
+ }
185
+
186
+ var isArray = Array.isArray || function(a) {
187
+ return Object.prototype.toString.call(a) === '[object Array]';
188
+ };
189
+
190
+ return constructor;
191
+ })();
192
+
193
+ var Hogan = (function () {
194
+
195
+ // Setup regex assignments
196
+ // remove whitespace according to Mustache spec
197
+ var rIsWhitespace = /\S/,
198
+ rQuot = /\"/g,
199
+ rNewline = /\n/g,
200
+ rCr = /\r/g,
201
+ rSlash = /\\/g,
202
+ tagTypes = {
203
+ '#': 1, '^': 2, '/': 3, '!': 4, '>': 5,
204
+ '<': 6, '=': 7, '_v': 8, '{': 9, '&': 10
205
+ };
206
+
207
+ function scan(text) {
208
+ var len = text.length,
209
+ IN_TEXT = 0,
210
+ IN_TAG_TYPE = 1,
211
+ IN_TAG = 2,
212
+ state = IN_TEXT,
213
+ tagType = null,
214
+ tag = null,
215
+ buf = '',
216
+ tokens = [],
217
+ seenTag = false,
218
+ i = 0,
219
+ lineStart = 0,
220
+ otag = '{{',
221
+ ctag = '}}';
222
+
223
+ function addBuf() {
224
+ if (buf.length > 0) {
225
+ tokens.push(new String(buf));
226
+ buf = '';
227
+ }
228
+ }
229
+
230
+ function lineIsWhitespace() {
231
+ var isAllWhitespace = true;
232
+ for (var j = lineStart; j < tokens.length; j++) {
233
+ isAllWhitespace =
234
+ (tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) ||
235
+ (!tokens[j].tag && tokens[j].match(rIsWhitespace) === null);
236
+ if (!isAllWhitespace) {
237
+ return false;
238
+ }
239
+ }
240
+
241
+ return isAllWhitespace;
242
+ }
243
+
244
+ function filterLine(haveSeenTag, noNewLine) {
245
+ addBuf();
246
+ if (haveSeenTag && lineIsWhitespace()) {
247
+ for (var j = lineStart; j < tokens.length; j++) {
248
+ if (!tokens[j].tag) {
249
+ tokens.splice(j, 1);
250
+ }
251
+ }
252
+ } else if (!noNewLine) {
253
+ tokens.push({tag:'\n'});
254
+ }
255
+
256
+ seenTag = false;
257
+ lineStart = tokens.length;
258
+ }
259
+
260
+ function changeDelimiters(text, index) {
261
+ var close = '=' + ctag,
262
+ closeIndex = text.indexOf(close, index),
263
+ delimiters = trim(text.substring(text.indexOf('=', index) + 1,
264
+ closeIndex)).split(' ');
265
+ otag = delimiters[0];
266
+ ctag = delimiters[1];
267
+ return closeIndex + close.length - 1;
268
+ }
269
+
270
+ for (i = 0; i < len; i++) {
271
+ if (state == IN_TEXT) {
272
+ if (tagChange(otag, text, i)) {
273
+ --i;
274
+ addBuf();
275
+ state = IN_TAG_TYPE;
276
+ } else {
277
+ if (text.charAt(i) == '\n') {
278
+ filterLine(seenTag);
279
+ } else {
280
+ buf += text.charAt(i);
281
+ }
282
+ }
283
+ } else if (state == IN_TAG_TYPE) {
284
+ i += otag.length - 1;
285
+ tag = tagTypes[text.charAt(i + 1)];
286
+ tagType = tag ? text.charAt(i + 1) : '_v';
287
+ seenTag = i;
288
+ if (tagType == '=') {
289
+ i = changeDelimiters(text, i);
290
+ state = IN_TEXT;
291
+ } else {
292
+ if (tag) {
293
+ i++;
294
+ }
295
+ state = IN_TAG;
296
+ }
297
+ } else {
298
+ if (tagChange(ctag, text, i)) {
299
+ i += ctag.length - 1;
300
+ tokens.push({tag: tagType, n: trim(buf),
301
+ i: (tagType == '/') ? seenTag - 1 : i + 1});
302
+ buf = '';
303
+ state = IN_TEXT;
304
+ if (tagType == '{') {
305
+ i++;
306
+ }
307
+ } else {
308
+ buf += text.charAt(i);
309
+ }
310
+ }
311
+ }
312
+
313
+ filterLine(seenTag, true);
314
+
315
+ return tokens;
316
+ }
317
+
318
+ function trim(s) {
319
+ if (s.trim) {
320
+ return s.trim();
321
+ }
322
+
323
+ return s.replace(/^\s*|\s*$/g, '');
324
+ }
325
+
326
+ function tagChange(tag, text, index) {
327
+ if (text.charAt(index) != tag.charAt(0)) {
328
+ return false;
329
+ }
330
+
331
+ for (var i = 1, l = tag.length; i < l; i++) {
332
+ if (text.charAt(index + i) != tag.charAt(i)) {
333
+ return false;
334
+ }
335
+ }
336
+
337
+ return true;
338
+ }
339
+
340
+ function buildTree(tokens, kind, stack, customTags) {
341
+ var instructions = [],
342
+ opener = null,
343
+ token = null;
344
+
345
+ while (tokens.length > 0) {
346
+ token = tokens.shift();
347
+ if (token.tag == '#' || token.tag == '^' ||
348
+ isOpener(token, customTags)) {
349
+ stack.push(token);
350
+ token.nodes = buildTree(tokens, token.tag, stack, customTags);
351
+ instructions.push(token);
352
+ } else if (token.tag == '/') {
353
+ if (stack.length === 0) {
354
+ throw new Error('Closing tag without opener: /' + token.n);
355
+ }
356
+ opener = stack.pop();
357
+ if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
358
+ throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
359
+ }
360
+ opener.end = token.i;
361
+ return instructions;
362
+ } else {
363
+ instructions.push(token);
364
+ }
365
+ }
366
+
367
+ if (stack.length > 0) {
368
+ throw new Error('missing closing tag: ' + stack.pop().n);
369
+ }
370
+
371
+ return instructions;
372
+ }
373
+
374
+ function isOpener(token, tags) {
375
+ for (var i = 0, l = tags.length; i < l; i++) {
376
+ if (tags[i].o == token.n) {
377
+ token.tag = '#';
378
+ return true;
379
+ }
380
+ }
381
+ }
382
+
383
+ function isCloser(close, open, tags) {
384
+ for (var i = 0, l = tags.length; i < l; i++) {
385
+ if (tags[i].c == close && tags[i].o == open) {
386
+ return true;
387
+ }
388
+ }
389
+ }
390
+
391
+ function generate(tree, text, options) {
392
+ var code = 'var c = [cx];var b = "";var _ = this;' +
393
+ walk(tree) + 'return b;';
394
+ if (options.asString) {
395
+ return 'function(cx,p){' + code + ';}';
396
+ }
397
+
398
+ var template = new HoganTemplate(text);
399
+ template.r = new Function('cx', 'p', code);
400
+ return template;
401
+ }
402
+
403
+ function esc(s) {
404
+ return s.replace(rSlash, '\\\\')
405
+ .replace(rQuot, '\\\"')
406
+ .replace(rNewline, '\\n')
407
+ .replace(rCr, '\\r');
408
+ }
409
+
410
+ function chooseMethod(s) {
411
+ return (~s.indexOf('.')) ? 'd' : 'f';
412
+ }
413
+
414
+ function walk(tree) {
415
+ var code = '';
416
+ for (var i = 0, l = tree.length; i < l; i++) {
417
+ var tag = tree[i].tag;
418
+ if (tag == '#') {
419
+ code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n),
420
+ tree[i].i, tree[i].end);
421
+ } else if (tag == '^') {
422
+ code += invertedSection(tree[i].nodes, tree[i].n,
423
+ chooseMethod(tree[i].n));
424
+ } else if (tag == '<' || tag == '>') {
425
+ code += partial(tree[i].n);
426
+ } else if (tag == '{' || tag == '&') {
427
+ code += tripleStache(tree[i].n, chooseMethod(tree[i].n));
428
+ } else if (tag == '\n') {
429
+ code += text('\n');
430
+ } else if (tag == '_v') {
431
+ code += variable(tree[i].n, chooseMethod(tree[i].n));
432
+ } else if (tag === undefined) {
433
+ code += text(tree[i]);
434
+ }
435
+ }
436
+ return code;
437
+ }
438
+
439
+ function section(nodes, id, method, start, end) {
440
+ return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' +
441
+ 'c,p,0,' + start + ',' + end + ')){' +
442
+ 'b += _.rs(c,p,' +
443
+ 'function(c,p){ var b = "";' +
444
+ walk(nodes) +
445
+ 'return b;});c.pop();}' +
446
+ 'else{b += _.b; _.b = ""};';
447
+ }
448
+
449
+ function invertedSection(nodes, id, method) {
450
+ return 'if (!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0)){' +
451
+ walk(nodes) +
452
+ '};';
453
+ }
454
+
455
+ function partial(id) {
456
+ return 'b += _.rp("' + esc(id) + '",c[c.length - 1],p);';
457
+ }
458
+
459
+ function tripleStache(id, method) {
460
+ return 'b += (_.' + method + '("' + esc(id) + '",c,p,0));';
461
+ }
462
+
463
+ function variable(id, method) {
464
+ return 'b += (_.v(_.' + method + '("' + esc(id) + '",c,p,0)));';
465
+ }
466
+
467
+ function text(id) {
468
+ return 'b += "' + esc(id) + '";';
469
+ }
470
+
471
+ return ({
472
+ scan: scan,
473
+
474
+ parse: function(tokens, options) {
475
+ options = options || {};
476
+ return buildTree(tokens, '', [], options.sectionTags || []);
477
+ },
478
+
479
+ cache: {},
480
+
481
+ compile: function(text, options) {
482
+ // options
483
+ //
484
+ // asString: false (default)
485
+ //
486
+ // sectionTags: [{o: '_foo', c: 'foo'}]
487
+ // An array of object with o and c fields that indicate names for custom
488
+ // section tags. The example above allows parsing of {{_foo}}{{/foo}}.
489
+ //
490
+ options = options || {};
491
+
492
+ var t = this.cache[text];
493
+ if (t) {
494
+ return t;
495
+ }
496
+ t = generate(this.parse(scan(text), options), text, options);
497
+ return this.cache[text] = t;
498
+ }
499
+ });
500
+ })();
501
+
502
+ // Export the hogan constructor for Node.js and CommonJS.
503
+ if (typeof module !== 'undefined' && module.exports) {
504
+ module.exports = Hogan;
505
+ module.exports.Template = HoganTemplate;
506
+ } else if (typeof exports !== 'undefined') {
507
+ exports.Hogan = Hogan;
508
+ exports.HoganTemplate = HoganTemplate;
509
+ }
510
+
511
+ // Expose Hogan as an AMD module.
512
+ if (typeof define === 'function' && define.amd) {
513
+ define(function () { return Hogan; });
514
+ }
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hogan_assets
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Les Hill
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-25 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: execjs
16
+ requirement: &70131289224740 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.9
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70131289224740
25
+ - !ruby/object:Gem::Dependency
26
+ name: tilt
27
+ requirement: &70131289224240 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.3.3
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70131289224240
36
+ - !ruby/object:Gem::Dependency
37
+ name: sprockets
38
+ requirement: &70131289223780 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 2.0.3
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70131289223780
47
+ description: Use compiled hogan.js (mustache) JavaScript templates with sprockets
48
+ and the Rails asset pipeline.
49
+ email:
50
+ - leshill@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - .rvmrc
57
+ - Gemfile
58
+ - Gemfile.lock
59
+ - LICENSE
60
+ - README.md
61
+ - Rakefile
62
+ - hogan_assets.gemspec
63
+ - lib/hogan_assets.rb
64
+ - lib/hogan_assets/engine.rb
65
+ - lib/hogan_assets/hogan.rb
66
+ - lib/hogan_assets/tilt.rb
67
+ - lib/hogan_assets/version.rb
68
+ - test/hogan_assets/tilt_test.rb
69
+ - test/test_helper.rb
70
+ - vendor/assets/javascripts/hogan.js
71
+ homepage: ''
72
+ licenses: []
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.10
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Use compiled hogan.js (mustache) JavaScript templates with sprockets and
95
+ the Rails asset pipeline.
96
+ test_files:
97
+ - test/hogan_assets/tilt_test.rb
98
+ - test/test_helper.rb