dust-rails 0.3.0 → 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.
data/dust-rails.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.email = ["thefron@wafflestudio.com"]
10
10
  s.homepage = "https://github.com/thefron/dust-rails"
11
11
  s.summary = %q{Use dust.js with rails}
12
- s.description = %q{This gem makes you dust.js easy to use with rails.}
12
+ s.description = %q{This gem makes it easy to use dust.js with rails.}
13
13
 
14
14
  s.rubyforge_project = "dust-rails"
15
15
 
@@ -6,7 +6,7 @@ module Dust
6
6
 
7
7
  module Source
8
8
  def self.path
9
- @path ||= File.expand_path('../../../../vendor/assets/javascripts/dust-full-for-compile.js', __FILE__)
9
+ @path ||= File.expand_path('../../../../vendor/dustjs/lib/dust.js', __FILE__)
10
10
  end
11
11
 
12
12
  def self.contents
@@ -1,5 +1,5 @@
1
1
  module Dust
2
2
  module Rails
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  //
2
- // Dust - Asynchronous Templating v0.3.0
2
+ // Dust - Asynchronous Templating v1.0.0
3
3
  // http://akdubya.github.com/dustjs
4
4
  //
5
5
  // Copyright (c) 2010, Aleksander Williams
@@ -8,6 +8,12 @@
8
8
 
9
9
  var dust = {};
10
10
 
11
+ function getGlobal(){
12
+ return (function(){
13
+ return this.dust;
14
+ }).call(null);
15
+ }
16
+
11
17
  (function(dust) {
12
18
 
13
19
  dust.cache = {};
@@ -75,9 +81,15 @@ if (Array.isArray) {
75
81
  };
76
82
  }
77
83
 
78
- dust.nextTick = function(callback) {
79
- setTimeout(callback, 0);
80
- }
84
+ dust.nextTick = (function() {
85
+ if (typeof process !== "undefined") {
86
+ return process.nextTick;
87
+ } else {
88
+ return function(callback) {
89
+ setTimeout(callback,0);
90
+ }
91
+ }
92
+ } )();
81
93
 
82
94
  dust.isEmpty = function(value) {
83
95
  if (dust.isArray(value) && !value.length) return true;
@@ -106,8 +118,10 @@ dust.filters = {
106
118
  h: function(value) { return dust.escapeHtml(value); },
107
119
  j: function(value) { return dust.escapeJs(value); },
108
120
  u: encodeURI,
109
- uc: encodeURIComponent
110
- }
121
+ uc: encodeURIComponent,
122
+ js: function(value) { if (!JSON) { return value; } return JSON.stringify(value); },
123
+ jp: function(value) { if (!JSON) { return value; } return JSON.parse(value); }
124
+ };
111
125
 
112
126
  function Context(stack, global, blocks) {
113
127
  this.stack = stack;
@@ -117,14 +131,14 @@ function Context(stack, global, blocks) {
117
131
 
118
132
  dust.makeBase = function(global) {
119
133
  return new Context(new Stack(), global);
120
- }
134
+ };
121
135
 
122
136
  Context.wrap = function(context) {
123
137
  if (context instanceof Context) {
124
138
  return context;
125
139
  }
126
140
  return new Context(new Stack(context));
127
- }
141
+ };
128
142
 
129
143
  Context.prototype.get = function(key) {
130
144
  var ctx = this.stack, value;
@@ -146,7 +160,6 @@ Context.prototype.getPath = function(cur, down) {
146
160
  len = down.length;
147
161
 
148
162
  if (cur && len === 0) return ctx.head;
149
- if (!ctx.isObject) return undefined;
150
163
  ctx = ctx.head;
151
164
  var i = 0;
152
165
  while(ctx && i < len) {
@@ -168,7 +181,12 @@ Context.prototype.current = function() {
168
181
  return this.stack.head;
169
182
  };
170
183
 
171
- Context.prototype.getBlock = function(key) {
184
+ Context.prototype.getBlock = function(key, chk, ctx) {
185
+ if (typeof key === "function") {
186
+ key = key(chk, ctx).data;
187
+ chk.data = "";
188
+ }
189
+
172
190
  var blocks = this.blocks;
173
191
 
174
192
  if (!blocks) return;
@@ -177,7 +195,7 @@ Context.prototype.getBlock = function(key) {
177
195
  fn = blocks[len][key];
178
196
  if (fn) return fn;
179
197
  }
180
- }
198
+ };
181
199
 
182
200
  Context.prototype.shiftBlocks = function(locals) {
183
201
  var blocks = this.blocks;
@@ -191,7 +209,7 @@ Context.prototype.shiftBlocks = function(locals) {
191
209
  return new Context(this.stack, this.global, newBlocks);
192
210
  }
193
211
  return this;
194
- }
212
+ };
195
213
 
196
214
  function Stack(head, tail, idx, len) {
197
215
  this.tail = tail;
@@ -224,7 +242,7 @@ Stub.prototype.flush = function() {
224
242
  this.head = chunk;
225
243
  }
226
244
  this.callback(null, this.out);
227
- }
245
+ };
228
246
 
229
247
  function Stream() {
230
248
  this.head = new Chunk(this);
@@ -247,23 +265,46 @@ Stream.prototype.flush = function() {
247
265
  this.head = chunk;
248
266
  }
249
267
  this.emit('end');
250
- }
268
+ };
251
269
 
252
270
  Stream.prototype.emit = function(type, data) {
253
- var events = this.events;
254
-
255
- if (events && events[type]) {
256
- events[type](data);
271
+ if (!this.events) return false;
272
+ var handler = this.events[type];
273
+ if (!handler) return false;
274
+ if (typeof handler == 'function') {
275
+ handler(data);
276
+ } else {
277
+ var listeners = handler.slice(0);
278
+ for (var i = 0, l = listeners.length; i < l; i++) {
279
+ listeners[i](data);
280
+ }
257
281
  }
258
- }
282
+ };
259
283
 
260
284
  Stream.prototype.on = function(type, callback) {
261
285
  if (!this.events) {
262
286
  this.events = {};
263
287
  }
264
- this.events[type] = callback;
288
+ if (!this.events[type]) {
289
+ this.events[type] = callback;
290
+ } else if(typeof this.events[type] === 'function') {
291
+ this.events[type] = [this.events[type], callback];
292
+ } else {
293
+ this.events[type].push(callback);
294
+ }
265
295
  return this;
266
- }
296
+ };
297
+
298
+ Stream.prototype.pipe = function(stream) {
299
+ this.on("data", function(data) {
300
+ stream.write(data, "utf8");
301
+ }).on("end", function() {
302
+ stream.end();
303
+ }).on("error", function(err) {
304
+ stream.error(err);
305
+ });
306
+ return this;
307
+ };
267
308
 
268
309
  function Chunk(root, next, taps) {
269
310
  this.root = root;
@@ -281,7 +322,7 @@ Chunk.prototype.write = function(data) {
281
322
  }
282
323
  this.data += data;
283
324
  return this;
284
- }
325
+ };
285
326
 
286
327
  Chunk.prototype.end = function(data) {
287
328
  if (data) {
@@ -290,7 +331,7 @@ Chunk.prototype.end = function(data) {
290
331
  this.flushable = true;
291
332
  this.root.flush();
292
333
  return this;
293
- }
334
+ };
294
335
 
295
336
  Chunk.prototype.map = function(callback) {
296
337
  var cursor = new Chunk(this.root, this.next, this.taps),
@@ -300,7 +341,7 @@ Chunk.prototype.map = function(callback) {
300
341
  this.flushable = true;
301
342
  callback(branch);
302
343
  return cursor;
303
- }
344
+ };
304
345
 
305
346
  Chunk.prototype.tap = function(tap) {
306
347
  var taps = this.taps;
@@ -311,20 +352,22 @@ Chunk.prototype.tap = function(tap) {
311
352
  this.taps = new Tap(tap);
312
353
  }
313
354
  return this;
314
- }
355
+ };
315
356
 
316
357
  Chunk.prototype.untap = function() {
317
358
  this.taps = this.taps.tail;
318
359
  return this;
319
- }
360
+ };
320
361
 
321
362
  Chunk.prototype.render = function(body, context) {
322
363
  return body(this, context);
323
- }
364
+ };
324
365
 
325
366
  Chunk.prototype.reference = function(elem, context, auto, filters) {
326
367
  if (typeof elem === "function") {
327
- elem = elem(this, context, null, {auto: auto, filters: filters});
368
+ elem.isReference = true;
369
+ // Changed the function calling to use apply with the current context to make sure that "this" is wat we expect it to be inside the function
370
+ elem = elem.apply(context.current(), [this, context, null, {auto: auto, filters: filters}]);
328
371
  if (elem instanceof Chunk) {
329
372
  return elem;
330
373
  }
@@ -338,7 +381,7 @@ Chunk.prototype.reference = function(elem, context, auto, filters) {
338
381
 
339
382
  Chunk.prototype.section = function(elem, context, bodies, params) {
340
383
  if (typeof elem === "function") {
341
- elem = elem(this, context, bodies, params);
384
+ elem = elem.apply(context.current(), [this, context, bodies, params]);
342
385
  if (elem instanceof Chunk) {
343
386
  return elem;
344
387
  }
@@ -354,15 +397,26 @@ Chunk.prototype.section = function(elem, context, bodies, params) {
354
397
  if (dust.isArray(elem)) {
355
398
  if (body) {
356
399
  var len = elem.length, chunk = this;
400
+ context.stack.head['$len'] = len;
357
401
  for (var i=0; i<len; i++) {
402
+ context.stack.head['$idx'] = i;
358
403
  chunk = body(chunk, context.push(elem[i], i, len));
359
404
  }
405
+ context.stack.head['$idx'] = undefined;
406
+ context.stack.head['$len'] = undefined;
360
407
  return chunk;
361
408
  }
362
409
  } else if (elem === true) {
363
410
  if (body) return body(this, context);
364
411
  } else if (elem || elem === 0) {
365
- if (body) return body(this, context.push(elem));
412
+ if (body) {
413
+ context.stack.head['$idx'] = 0;
414
+ context.stack.head['$len'] = 1;
415
+ chunk = body(this, context.push(elem));
416
+ context.stack.head['$idx'] = undefined;
417
+ context.stack.head['$len'] = undefined;
418
+ return chunk;
419
+ }
366
420
  } else if (skip) {
367
421
  return skip(this, context);
368
422
  }
@@ -379,7 +433,7 @@ Chunk.prototype.exists = function(elem, context, bodies) {
379
433
  return skip(this, context);
380
434
  }
381
435
  return this;
382
- }
436
+ };
383
437
 
384
438
  Chunk.prototype.notexists = function(elem, context, bodies) {
385
439
  var body = bodies.block,
@@ -391,7 +445,7 @@ Chunk.prototype.notexists = function(elem, context, bodies) {
391
445
  return skip(this, context);
392
446
  }
393
447
  return this;
394
- }
448
+ };
395
449
 
396
450
  Chunk.prototype.block = function(elem, context, bodies) {
397
451
  var body = bodies.block;
@@ -406,7 +460,17 @@ Chunk.prototype.block = function(elem, context, bodies) {
406
460
  return this;
407
461
  };
408
462
 
409
- Chunk.prototype.partial = function(elem, context) {
463
+ Chunk.prototype.partial = function(elem, context, params) {
464
+ var ctx = context.stack, tempHead = ctx.head;
465
+ if (params){
466
+ //put the params context second to match what section does. {.} matches the current context without parameters
467
+ //remove head
468
+ context = context.rebase(ctx.tail);
469
+ //put params on
470
+ context = context.push(params);
471
+ //reattach the head
472
+ context = context.push(tempHead);
473
+ }
410
474
  if (typeof elem === "function") {
411
475
  return this.capture(elem, context, function(name, chunk) {
412
476
  dust.load(name, chunk, context).end();
@@ -438,19 +502,6 @@ Chunk.prototype.setError = function(err) {
438
502
  return this;
439
503
  };
440
504
 
441
- dust.helpers = {
442
- sep: function(chunk, context, bodies) {
443
- if (context.stack.index === context.stack.of - 1) {
444
- return chunk;
445
- }
446
- return bodies.block(chunk, context);
447
- },
448
-
449
- idx: function(chunk, context, bodies) {
450
- return bodies.block(chunk, context.push(context.stack.index));
451
- }
452
- }
453
-
454
505
  function Tap(head, tail) {
455
506
  this.head = head;
456
507
  this.tail = tail;
@@ -470,18 +521,19 @@ Tap.prototype.go = function(value) {
470
521
  return value;
471
522
  };
472
523
 
473
- var HCHARS = new RegExp(/[&<>\"]/),
524
+ var HCHARS = new RegExp(/[&<>\"\']/),
474
525
  AMP = /&/g,
475
526
  LT = /</g,
476
527
  GT = />/g,
477
- QUOT = /\"/g;
528
+ QUOT = /\"/g,
529
+ SQUOT = /\'/g;
478
530
 
479
531
  dust.escapeHtml = function(s) {
480
532
  if (typeof s === "string") {
481
533
  if (!HCHARS.test(s)) {
482
534
  return s;
483
535
  }
484
- return s.replace(AMP,'&amp;').replace(LT,'&lt;').replace(GT,'&gt;').replace(QUOT,'&quot;');
536
+ return s.replace(AMP,'&amp;').replace(LT,'&lt;').replace(GT,'&gt;').replace(QUOT,'&quot;').replace(SQUOT, '&#39;');
485
537
  }
486
538
  return s;
487
539
  };
@@ -515,8 +567,201 @@ dust.escapeJs = function(s) {
515
567
  })(dust);
516
568
 
517
569
  if (typeof exports !== "undefined") {
570
+ //TODO: Remove the helpers from dust core in the next release.
571
+ dust.helpers = require("./dust-helpers").helpers;
518
572
  if (typeof process !== "undefined") {
519
573
  require('./server')(dust);
520
574
  }
521
575
  module.exports = dust;
522
576
  }
577
+ (function(dust){
578
+
579
+ /* make a safe version of console if it is not available
580
+ * currently supporting:
581
+ * _console.log
582
+ * */
583
+ var _console = (typeof console !== 'undefined')? console: {
584
+ log: function(){
585
+ /* a noop*/
586
+ }
587
+ };
588
+
589
+ function isSelect(context) {
590
+ var value = context.current();
591
+ return typeof value === "object" && value.isSelect === true;
592
+ }
593
+
594
+ function filter(chunk, context, bodies, params, filter) {
595
+ var params = params || {},
596
+ actual,
597
+ expected;
598
+ if (params.key) {
599
+ actual = helpers.tap(params.key, chunk, context);
600
+ } else if (isSelect(context)) {
601
+ actual = context.current().selectKey;
602
+ if (context.current().isResolved) {
603
+ filter = function() { return false; };
604
+ }
605
+ } else {
606
+ throw "No key specified for filter and no key found in context from select statement";
607
+ }
608
+ expected = helpers.tap(params.value, chunk, context);
609
+ if (filter(expected, coerce(actual, params.type, context))) {
610
+ if (isSelect(context)) {
611
+ context.current().isResolved = true;
612
+ }
613
+ return chunk.render(bodies.block, context);
614
+ } else if (bodies['else']) {
615
+ return chunk.render(bodies['else'], context);
616
+ }
617
+
618
+ return chunk.write('');
619
+ }
620
+
621
+ function coerce (value, type, context) {
622
+ if (value) {
623
+ switch (type || typeof(value)) {
624
+ case 'number': return +value;
625
+ case 'string': return String(value);
626
+ case 'boolean': return Boolean(value);
627
+ case 'date': return new Date(value);
628
+ case 'context': return context.get(value);
629
+ }
630
+ }
631
+
632
+ return value;
633
+ }
634
+
635
+ var helpers = {
636
+
637
+ sep: function(chunk, context, bodies) {
638
+ if (context.stack.index === context.stack.of - 1) {
639
+ return chunk;
640
+ }
641
+ return bodies.block(chunk, context);
642
+ },
643
+
644
+ idx: function(chunk, context, bodies) {
645
+ return bodies.block(chunk, context.push(context.stack.index));
646
+ },
647
+
648
+ contextDump: function(chunk, context, bodies) {
649
+ _console.log(JSON.stringify(context.stack));
650
+ return chunk;
651
+ },
652
+
653
+ // Utility helping to resolve dust references in the given chunk
654
+ tap: function( input, chunk, context ){
655
+ // return given input if there is no dust reference to resolve
656
+ var output = input;
657
+ // dust compiles a string to function, if there are references
658
+ if( typeof input === "function"){
659
+ if( ( typeof input.isReference !== "undefined" ) && ( input.isReference === true ) ){ // just a plain function, not a dust `body` function
660
+ output = input();
661
+ } else {
662
+ output = '';
663
+ chunk.tap(function(data){
664
+ output += data;
665
+ return '';
666
+ }).render(input, context).untap();
667
+ if( output === '' ){
668
+ output = false;
669
+ }
670
+ }
671
+ }
672
+ return output;
673
+ },
674
+
675
+ /**
676
+ if helper
677
+ @param cond, either a string literal value or a dust reference
678
+ a string literal value, is enclosed in double quotes, e.g. cond="2>3"
679
+ a dust reference is also enclosed in double quotes, e.g. cond="'{val}'' > 3"
680
+ cond argument should evaluate to a valid javascript expression
681
+ **/
682
+
683
+ "if": function( chunk, context, bodies, params ){
684
+ if( params && params.cond ){
685
+ var cond = params.cond;
686
+ cond = this.tap(cond, chunk, context);
687
+ // eval expressions with given dust references
688
+ if( eval( cond ) ){
689
+ return chunk.render( bodies.block, context );
690
+ }
691
+ if( bodies['else'] ){
692
+ return chunk.render( bodies['else'], context );
693
+ }
694
+ }
695
+ // no condition
696
+ else {
697
+ _console.log( "No condition given in the if helper!" );
698
+ }
699
+ return chunk;
700
+ },
701
+
702
+ /**
703
+ select/eq/lt/lte/gt/gte/default helper
704
+ @param key, either a string literal value or a dust reference
705
+ a string literal value, is enclosed in double quotes, e.g. key="foo"
706
+ a dust reference may or may not be enclosed in double quotes, e.g. key="{val}" and key=val are both valid
707
+ @param type (optiona), supported types are number, boolean, string, date, context, defaults to string
708
+ **/
709
+ select: function(chunk, context, bodies, params) {
710
+ if( params && params.key){
711
+ // returns given input as output, if the input is not a dust reference, else does a context lookup
712
+ var key = this.tap(params.key, chunk, context);
713
+ return chunk.render(bodies.block, context.push({ isSelect: true, isResolved: false, selectKey: key }));
714
+ }
715
+ // no key
716
+ else {
717
+ _console.log( "No key given in the select helper!" );
718
+ }
719
+ return chunk;
720
+ },
721
+
722
+ eq: function(chunk, context, bodies, params) {
723
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual === expected; });
724
+ },
725
+
726
+ lt: function(chunk, context, bodies, params) {
727
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual < expected; });
728
+ },
729
+
730
+ lte: function(chunk, context, bodies, params) {
731
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual <= expected; });
732
+ },
733
+
734
+ gt: function(chunk, context, bodies, params) {
735
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual > expected; });
736
+ },
737
+
738
+ gte: function(chunk, context, bodies, params) {
739
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual >= expected; });
740
+ },
741
+
742
+ "default": function(chunk, context, bodies, params) {
743
+ return filter(chunk, context, bodies, params, function(expected, actual) { return true; });
744
+ },
745
+ size: function( chunk, context, bodies, params ) {
746
+ var subject = params.subject;
747
+ var value = 0;
748
+ if (!subject) { //undefined, "", 0
749
+ value = 0;
750
+ } else if(dust.isArray(subject)) { //array
751
+ value = subject.length;
752
+ } else if (!isNaN(subject)) { //numeric values
753
+ value = subject;
754
+ } else if (Object(subject) === subject) { //object test
755
+ var nr = 0;
756
+ for(var k in subject) if(Object.hasOwnProperty.call(subject,k)) nr++;
757
+ value = nr;
758
+ } else {
759
+ value = (subject + '').length; //any other value (strings etc.)
760
+ }
761
+ return chunk.write(value);
762
+ }
763
+ };
764
+
765
+ dust.helpers = helpers;
766
+
767
+ })(typeof exports !== 'undefined' ? exports : getGlobal());