frank 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.
data/lib/frank/tilt.rb DELETED
@@ -1,794 +0,0 @@
1
- require 'digest/md5'
2
-
3
- module Tilt
4
- VERSION = '0.9'
5
-
6
- @template_mappings = {}
7
-
8
- # Hash of template path pattern => template implementation class mappings.
9
- def self.mappings
10
- @template_mappings
11
- end
12
-
13
- # Register a template implementation by file extension.
14
- def self.register(ext, template_class)
15
- ext = ext.to_s.sub(/^\./, '')
16
- mappings[ext.downcase] = template_class
17
- end
18
-
19
- # Returns true when a template exists on an exact match of the provided file extension
20
- def self.registered?(ext)
21
- mappings.key?(ext.downcase)
22
- end
23
-
24
- # Create a new template for the given file using the file's extension
25
- # to determine the the template mapping.
26
- def self.new(file, line=nil, options={}, &block)
27
- if template_class = self[file]
28
- template_class.new(file, line, options, &block)
29
- else
30
- fail "No template engine registered for #{File.basename(file)}"
31
- end
32
- end
33
-
34
- # Lookup a template class for the given filename or file
35
- # extension. Return nil when no implementation is found.
36
- def self.[](file)
37
- pattern = file.to_s.downcase
38
- unless registered?(pattern)
39
- pattern = File.basename(pattern)
40
- pattern.sub!(/^[^.]*\.?/, '') until (pattern.empty? || registered?(pattern))
41
- end
42
- @template_mappings[pattern]
43
- end
44
-
45
- # Mixin allowing template compilation on scope objects.
46
- #
47
- # Including this module in scope objects passed to Template#render
48
- # causes template source to be compiled to methods the first time they're
49
- # used. This can yield significant (5x-10x) performance increases for
50
- # templates that support it (ERB, Erubis, Builder).
51
- #
52
- # It's also possible (though not recommended) to include this module in
53
- # Object to enable template compilation globally. The downside is that
54
- # the template methods will polute the global namespace and could lead to
55
- # unexpected behavior.
56
- module CompileSite
57
- def __tilt__
58
- end
59
- end
60
-
61
- # Base class for template implementations. Subclasses must implement
62
- # the #prepare method and one of the #evaluate or #template_source
63
- # methods.
64
- class Template
65
- # Template source; loaded from a file or given directly.
66
- attr_reader :data
67
-
68
- # The name of the file where the template data was loaded from.
69
- attr_reader :file
70
-
71
- # The line number in #file where template data was loaded from.
72
- attr_reader :line
73
-
74
- # A Hash of template engine specific options. This is passed directly
75
- # to the underlying engine and is not used by the generic template
76
- # interface.
77
- attr_reader :options
78
-
79
- # Used to determine if this class's initialize_engine method has
80
- # been called yet.
81
- @engine_initialized = false
82
- class << self
83
- attr_accessor :engine_initialized
84
- alias engine_initialized? engine_initialized
85
- end
86
-
87
- # Create a new template with the file, line, and options specified. By
88
- # default, template data is read from the file. When a block is given,
89
- # it should read template data and return as a String. When file is nil,
90
- # a block is required.
91
- #
92
- # All arguments are optional.
93
- def initialize(file=nil, line=1, options={}, &block)
94
- @file, @line, @options = nil, 1, {}
95
-
96
- [options, line, file].compact.each do |arg|
97
- case
98
- when arg.respond_to?(:to_str) ; @file = arg.to_str
99
- when arg.respond_to?(:to_int) ; @line = arg.to_int
100
- when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
101
- else raise TypeError
102
- end
103
- end
104
-
105
- raise ArgumentError, "file or block required" if (@file || block).nil?
106
-
107
- # call the initialize_engine method if this is the very first time
108
- # an instance of this class has been created.
109
- if !self.class.engine_initialized?
110
- initialize_engine
111
- self.class.engine_initialized = true
112
- end
113
-
114
- # used to generate unique method names for template compilation
115
- @stamp = (Time.now.to_f * 10000).to_i
116
- @compiled_method_names = {}
117
-
118
- # load template data and prepare
119
- if @file.match(/^[^\n]+$/) && File.exist?(@file)
120
- @reader = block || lambda { |t| File.read(@file) }
121
- else
122
- @reader = block || lambda { |t| @file }
123
- end
124
-
125
- @data = @reader.call(self)
126
- prepare
127
- end
128
-
129
- # Render the template in the given scope with the locals specified. If a
130
- # block is given, it is typically available within the template via
131
- # +yield+.
132
- def render(scope=Object.new, locals={}, &block)
133
- evaluate scope, locals || {}, &block
134
- end
135
-
136
- # The basename of the template file.
137
- def basename(suffix='')
138
- File.basename(file, suffix) if file
139
- end
140
-
141
- # The template file's basename with all extensions chomped off.
142
- def name
143
- basename.split('.', 2).first if basename
144
- end
145
-
146
- # The filename used in backtraces to describe the template.
147
- def eval_file
148
- file || '(__TEMPLATE__)'
149
- end
150
-
151
- protected
152
- # Called once and only once for each template subclass the first time
153
- # the template class is initialized. This should be used to require the
154
- # underlying template library and perform any initial setup.
155
- def initialize_engine
156
- end
157
-
158
- # Like Kernel::require but issues a warning urging a manual require when
159
- # running under a threaded environment.
160
- def require_template_library(name)
161
- # if Thread.list.size > 1
162
- # warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
163
- # "explicit require '#{name}' suggested."
164
- # end
165
- require name
166
- end
167
-
168
- # Do whatever preparation is necessary to setup the underlying template
169
- # engine. Called immediately after template data is loaded. Instance
170
- # variables set in this method are available when #evaluate is called.
171
- #
172
- # Subclasses must provide an implementation of this method.
173
- def prepare
174
- if respond_to?(:compile!)
175
- # backward compat with tilt < 0.6; just in case
176
- warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.'
177
- compile!
178
- else
179
- raise NotImplementedError
180
- end
181
- end
182
-
183
- # Process the template and return the result. When the scope mixes in
184
- # the Tilt::CompileSite module, the template is compiled to a method and
185
- # reused given identical locals keys. When the scope object
186
- # does not mix in the CompileSite module, the template source is
187
- # evaluated with instance_eval. In any case, template executation
188
- # is guaranteed to be performed in the scope object with the locals
189
- # specified and with support for yielding to the block.
190
- def evaluate(scope, locals, &block)
191
- if scope.respond_to?(:__tilt__)
192
- method_name = compiled_method_name(locals.keys)
193
- if scope.respond_to?(method_name)
194
- scope.send(method_name, locals, &block)
195
- else
196
- compile_template_method(method_name, locals)
197
- scope.send(method_name, locals, &block)
198
- end
199
- else
200
- evaluate_source(scope, locals, &block)
201
- end
202
- end
203
-
204
- # Generates all template source by combining the preamble, template, and
205
- # postamble and returns a two-tuple of the form: [source, offset], where
206
- # source is the string containing (Ruby) source code for the template and
207
- # offset is the integer line offset where line reporting should begin.
208
- #
209
- # Template subclasses may override this method when they need complete
210
- # control over source generation or want to adjust the default line
211
- # offset. In most cases, overriding the #precompiled_template method is
212
- # easier and more appropriate.
213
- def precompiled(locals)
214
- preamble = precompiled_preamble(locals)
215
- parts = [
216
- preamble,
217
- precompiled_template(locals),
218
- precompiled_postamble(locals)
219
- ]
220
- [parts.join("\n"), preamble.count("\n") + 1]
221
- end
222
-
223
- # A string containing the (Ruby) source code for the template. The
224
- # default Template#evaluate implementation requires either this method
225
- # or the #precompiled method be overridden. When defined, the base
226
- # Template guarantees correct file/line handling, locals support, custom
227
- # scopes, and support for template compilation when the scope object
228
- # allows it.
229
- def precompiled_template(locals)
230
- raise NotImplementedError
231
- end
232
-
233
- # Generates preamble code for initializing template state, and performing
234
- # locals assignment. The default implementation performs locals
235
- # assignment only. Lines included in the preamble are subtracted from the
236
- # source line offset, so adding code to the preamble does not effect line
237
- # reporting in Kernel::caller and backtraces.
238
- def precompiled_preamble(locals)
239
- locals.map { |k,v| "#{k} = locals[:#{k}]" }.join("\n")
240
- end
241
-
242
- # Generates postamble code for the precompiled template source. The
243
- # string returned from this method is appended to the precompiled
244
- # template source.
245
- def precompiled_postamble(locals)
246
- ''
247
- end
248
-
249
- # The unique compiled method name for the locals keys provided.
250
- def compiled_method_name(locals_keys)
251
- @compiled_method_names[locals_keys] ||=
252
- generate_compiled_method_name(locals_keys)
253
- end
254
-
255
- private
256
- # Evaluate the template source in the context of the scope object.
257
- def evaluate_source(scope, locals, &block)
258
- source, offset = precompiled(locals)
259
- scope.instance_eval(source, eval_file, line - offset)
260
- end
261
-
262
- # JRuby doesn't allow Object#instance_eval to yield to the block it's
263
- # closed over. This is by design and (ostensibly) something that will
264
- # change in MRI, though no current MRI version tested (1.8.6 - 1.9.2)
265
- # exhibits the behavior. More info here:
266
- #
267
- # http://jira.codehaus.org/browse/JRUBY-2599
268
- #
269
- # Additionally, JRuby's eval line reporting is off by one compared to
270
- # all MRI versions tested.
271
- #
272
- # We redefine evaluate_source to work around both issues.
273
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
274
- undef evaluate_source
275
- def evaluate_source(scope, locals, &block)
276
- source, offset = precompiled(locals)
277
- file, lineno = eval_file, (line - offset) - 1
278
- scope.instance_eval { Kernel::eval(source, binding, file, lineno) }
279
- end
280
- end
281
-
282
- def generate_compiled_method_name(locals_keys)
283
- parts = [object_id, @stamp] + locals_keys.map { |k| k.to_s }.sort
284
- digest = Digest::MD5.hexdigest(parts.join(':'))
285
- "__tilt_#{digest}"
286
- end
287
-
288
- def compile_template_method(method_name, locals)
289
- source, offset = precompiled(locals)
290
- offset += 1
291
- CompileSite.module_eval <<-RUBY, eval_file, line - offset
292
- def #{method_name}(locals)
293
- #{source}
294
- end
295
- RUBY
296
-
297
- ObjectSpace.define_finalizer self,
298
- Template.compiled_template_method_remover(CompileSite, method_name)
299
- end
300
-
301
- def self.compiled_template_method_remover(site, method_name)
302
- proc { |oid| garbage_collect_compiled_template_method(site, method_name) }
303
- end
304
-
305
- def self.garbage_collect_compiled_template_method(site, method_name)
306
- site.module_eval do
307
- begin
308
- remove_method(method_name)
309
- rescue NameError
310
- # method was already removed (ruby >= 1.9)
311
- end
312
- end
313
- end
314
- end
315
-
316
- # Extremely simple template cache implementation. Calling applications
317
- # create a Tilt::Cache instance and use #fetch with any set of hashable
318
- # arguments (such as those to Tilt.new):
319
- # cache = Tilt::Cache.new
320
- # cache.fetch(path, line, options) { Tilt.new(path, line, options) }
321
- #
322
- # Subsequent invocations return the already loaded template object.
323
- class Cache
324
- def initialize
325
- @cache = {}
326
- end
327
-
328
- def fetch(*key)
329
- @cache[key] ||= yield
330
- end
331
-
332
- def clear
333
- @cache = {}
334
- end
335
- end
336
-
337
-
338
- # Template Implementations ================================================
339
-
340
-
341
- # The template source is evaluated as a Ruby string. The #{} interpolation
342
- # syntax can be used to generated dynamic output.
343
- class StringTemplate < Template
344
- def prepare
345
- @code = "%Q{#{data}}"
346
- end
347
-
348
- def precompiled_template(locals)
349
- @code
350
- end
351
- end
352
- register 'str', StringTemplate
353
-
354
-
355
- # ERB template implementation. See:
356
- # http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
357
- class ERBTemplate < Template
358
- def initialize_engine
359
- return if defined? ::ERB
360
- require_template_library 'erb'
361
- end
362
-
363
- def prepare
364
- @outvar = (options[:outvar] || '_erbout').to_s
365
- @engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
366
- end
367
-
368
- def precompiled_template(locals)
369
- @engine.src
370
- end
371
-
372
- def precompiled_preamble(locals)
373
- <<-RUBY
374
- begin
375
- __original_outvar = #{@outvar} if defined?(#{@outvar})
376
- #{super}
377
- RUBY
378
- end
379
-
380
- def precompiled_postamble(locals)
381
- <<-RUBY
382
- #{super}
383
- ensure
384
- #{@outvar} = __original_outvar
385
- end
386
- RUBY
387
- end
388
-
389
- # ERB generates a line to specify the character coding of the generated
390
- # source in 1.9. Account for this in the line offset.
391
- if RUBY_VERSION >= '1.9.0'
392
- def precompiled(locals)
393
- source, offset = super
394
- [source, offset + 1]
395
- end
396
- end
397
- end
398
-
399
- %w[erb rhtml].each { |ext| register ext, ERBTemplate }
400
-
401
-
402
- # Erubis template implementation. See:
403
- # http://www.kuwata-lab.com/erubis/
404
- #
405
- # ErubisTemplate supports the following additional options, which are not
406
- # passed down to the Erubis engine:
407
- #
408
- # :engine_class allows you to specify a custom engine class to use
409
- # instead of the default (which is ::Erubis::Eruby).
410
- #
411
- # :escape_html when true, ::Erubis::EscapedEruby will be used as
412
- # the engine class instead of the default. All content
413
- # within <%= %> blocks will be automatically html escaped.
414
- class ErubisTemplate < ERBTemplate
415
- def initialize_engine
416
- return if defined? ::Erubis
417
- require_template_library 'erubis'
418
- end
419
-
420
- def prepare
421
- @options.merge!(:preamble => false, :postamble => false)
422
- @outvar = (options.delete(:outvar) || '_erbout').to_s
423
- engine_class = options.delete(:engine_class)
424
- engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
425
- @engine = (engine_class || ::Erubis::Eruby).new(data, options)
426
- end
427
-
428
- def precompiled_preamble(locals)
429
- [super, "#{@outvar} = _buf = ''"].join("\n")
430
- end
431
-
432
- def precompiled_postamble(locals)
433
- ["_buf", super].join("\n")
434
- end
435
-
436
- # Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
437
- # Override and adjust back.
438
- if RUBY_VERSION >= '1.9.0'
439
- def precompiled(locals)
440
- source, offset = super
441
- [source, offset - 1]
442
- end
443
- end
444
- end
445
- register 'erubis', ErubisTemplate
446
-
447
-
448
- # Haml template implementation. See:
449
- # http://haml.hamptoncatlin.com/
450
- class HamlTemplate < Template
451
- def initialize_engine
452
- return if defined? ::Haml::Engine
453
- require_template_library 'haml'
454
- end
455
-
456
- def prepare
457
- options = @options.merge(:filename => eval_file, :line => line)
458
- @engine = ::Haml::Engine.new(data, options)
459
- end
460
-
461
- def evaluate(scope, locals, &block)
462
- if @engine.respond_to?(:precompiled_method_return_value, true)
463
- super
464
- else
465
- @engine.render(scope, locals, &block)
466
- end
467
- end
468
-
469
- # Precompiled Haml source. Taken from the precompiled_with_ambles
470
- # method in Haml::Precompiler:
471
- # http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126
472
- def precompiled_template(locals)
473
- @engine.precompiled
474
- end
475
-
476
- def precompiled_preamble(locals)
477
- local_assigns = super
478
- @engine.instance_eval do
479
- <<-RUBY
480
- begin
481
- extend Haml::Helpers
482
- _hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})
483
- _erbout = _hamlout.buffer
484
- __in_erb_template = true
485
- _haml_locals = locals
486
- #{local_assigns}
487
- RUBY
488
- end
489
- end
490
-
491
- def precompiled_postamble(locals)
492
- @engine.instance_eval do
493
- <<-RUBY
494
- #{precompiled_method_return_value}
495
- ensure
496
- @haml_buffer = @haml_buffer.upper
497
- end
498
- RUBY
499
- end
500
- end
501
- end
502
- register 'haml', HamlTemplate
503
-
504
-
505
- # Sass template implementation. See:
506
- # http://haml.hamptoncatlin.com/
507
- #
508
- # Sass templates do not support object scopes, locals, or yield.
509
- class SassTemplate < Template
510
- def initialize_engine
511
- return if defined? ::Sass::Engine
512
- require_template_library 'sass'
513
- end
514
-
515
- def prepare
516
- @engine = ::Sass::Engine.new(data, sass_options)
517
- end
518
-
519
- def evaluate(scope, locals, &block)
520
- @output ||= @engine.render
521
- end
522
-
523
- private
524
- def sass_options
525
- options.merge(:filename => eval_file, :line => line)
526
- end
527
- end
528
- register 'sass', SassTemplate
529
- register 'scss', SassTemplate
530
-
531
-
532
- # Lessscss template implementation. See:
533
- # http://lesscss.org/
534
- #
535
- # Less templates do not support object scopes, locals, or yield.
536
- class LessTemplate < Template
537
- def initialize_engine
538
- return if defined? ::Less::Engine
539
- require_template_library 'less'
540
- end
541
-
542
- def prepare
543
- @engine = ::Less::Engine.new(data)
544
- end
545
-
546
- def evaluate(scope, locals, &block)
547
- @engine.to_css
548
- end
549
- end
550
- register 'less', LessTemplate
551
-
552
-
553
- # Builder template implementation. See:
554
- # http://builder.rubyforge.org/
555
- class BuilderTemplate < Template
556
- def initialize_engine
557
- return if defined?(::Builder)
558
- require_template_library 'builder'
559
- end
560
-
561
- def prepare
562
- end
563
-
564
- def evaluate(scope, locals, &block)
565
- xml = ::Builder::XmlMarkup.new(:indent => 2)
566
- if data.respond_to?(:to_str)
567
- locals[:xml] = xml
568
- super(scope, locals, &block)
569
- elsif data.kind_of?(Proc)
570
- data.call(xml)
571
- end
572
- xml.target!
573
- end
574
-
575
- def precompiled_template(locals)
576
- data.to_str
577
- end
578
- end
579
- register 'builder', BuilderTemplate
580
-
581
-
582
- # Liquid template implementation. See:
583
- # http://liquid.rubyforge.org/
584
- #
585
- # Liquid is designed to be a *safe* template system and threfore
586
- # does not provide direct access to execuatable scopes. In order to
587
- # support a +scope+, the +scope+ must be able to represent itself
588
- # as a hash by responding to #to_h. If the +scope+ does not respond
589
- # to #to_h it will be ignored.
590
- #
591
- # LiquidTemplate does not support yield blocks.
592
- #
593
- # It's suggested that your program require 'liquid' at load
594
- # time when using this template engine.
595
- class LiquidTemplate < Template
596
- def initialize_engine
597
- return if defined? ::Liquid::Template
598
- require_template_library 'liquid'
599
- end
600
-
601
- def prepare
602
- @engine = ::Liquid::Template.parse(data)
603
- end
604
-
605
- def evaluate(scope, locals, &block)
606
- locals = locals.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
607
- if scope.respond_to?(:to_h)
608
- scope = scope.to_h.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
609
- locals = scope.merge(locals)
610
- end
611
- locals['yield'] = block.nil? ? '' : yield
612
- locals['content'] = locals['yield']
613
- @engine.render(locals)
614
- end
615
- end
616
- register 'liquid', LiquidTemplate
617
-
618
-
619
- # Discount Markdown implementation. See:
620
- # http://github.com/rtomayko/rdiscount
621
- #
622
- # RDiscount is a simple text filter. It does not support +scope+ or
623
- # +locals+. The +:smart+ and +:filter_html+ options may be set true
624
- # to enable those flags on the underlying RDiscount object.
625
- class RDiscountTemplate < Template
626
- def flags
627
- [:smart, :filter_html].select { |flag| options[flag] }
628
- end
629
-
630
- def initialize_engine
631
- return if defined? ::RDiscount
632
- require_template_library 'rdiscount'
633
- end
634
-
635
- def prepare
636
- @engine = RDiscount.new(data, *flags)
637
- @output = nil
638
- end
639
-
640
- def evaluate(scope, locals, &block)
641
- @output ||= @engine.to_html
642
- end
643
- end
644
- register 'markdown', RDiscountTemplate
645
- register 'mkd', RDiscountTemplate
646
- register 'md', RDiscountTemplate
647
-
648
-
649
- # RedCloth implementation. See:
650
- # http://redcloth.org/
651
- class RedClothTemplate < Template
652
- def initialize_engine
653
- return if defined? ::RedCloth
654
- require_template_library 'redcloth'
655
- end
656
-
657
- def prepare
658
- @engine = RedCloth.new(data)
659
- @output = nil
660
- end
661
-
662
- def evaluate(scope, locals, &block)
663
- @output ||= @engine.to_html
664
- end
665
- end
666
- register 'textile', RedClothTemplate
667
-
668
-
669
- # Mustache is written and maintained by Chris Wanstrath. See:
670
- # http://github.com/defunkt/mustache
671
- #
672
- # When a scope argument is provided to MustacheTemplate#render, the
673
- # instance variables are copied from the scope object to the Mustache
674
- # view.
675
- class MustacheTemplate < Template
676
- attr_reader :engine
677
-
678
- def initialize_engine
679
- return if defined? ::Mustache
680
- require_template_library 'mustache'
681
- end
682
-
683
- def prepare
684
- Mustache.view_namespace = options[:namespace]
685
- Mustache.view_path = options[:view_path] || options[:mustaches]
686
- @engine = options[:view] || Mustache.view_class(name)
687
- options.each do |key, value|
688
- next if %w[view view_path namespace mustaches].include?(key.to_s)
689
- @engine.send("#{key}=", value) if @engine.respond_to? "#{key}="
690
- end
691
- end
692
-
693
- def evaluate(scope=nil, locals={}, &block)
694
- instance = @engine.new
695
-
696
- # copy instance variables from scope to the view
697
- scope.instance_variables.each do |name|
698
- instance.instance_variable_set(name, scope.instance_variable_get(name))
699
- end
700
-
701
- # locals get added to the view's context
702
- locals.each do |local, value|
703
- instance[local] = value
704
- end
705
-
706
- # if we're passed a block it's a subview. Sticking it in yield
707
- # lets us use {{yield}} in layout.html to render the actual page.
708
- instance[:yield] = block.call if block
709
-
710
- instance.template = data unless instance.compiled?
711
-
712
- instance.to_html
713
- end
714
- end
715
- register 'mustache', MustacheTemplate
716
-
717
-
718
- # RDoc template. See:
719
- # http://rdoc.rubyforge.org/
720
- #
721
- # It's suggested that your program require 'rdoc/markup' and
722
- # 'rdoc/markup/to_html' at load time when using this template
723
- # engine.
724
- class RDocTemplate < Template
725
- def initialize_engine
726
- return if defined?(::RDoc::Markup)
727
- require_template_library 'rdoc/markup'
728
- require_template_library 'rdoc/markup/to_html'
729
- end
730
-
731
- def prepare
732
- markup = RDoc::Markup::ToHtml.new
733
- @engine = markup.convert(data)
734
- @output = nil
735
- end
736
-
737
- def evaluate(scope, locals, &block)
738
- @output ||= @engine.to_s
739
- end
740
- end
741
- register 'rdoc', RDocTemplate
742
-
743
-
744
- # CoffeeScript info:
745
- # http://jashkenas.github.com/coffee-script/
746
- class CoffeeTemplate < Template
747
- def initialize_engine
748
- return if defined? ::CoffeeScript
749
- require_template_library 'coffee-script'
750
- end
751
-
752
- def prepare
753
- @output = nil
754
- end
755
-
756
- def evaluate(scope, locals, &block)
757
- @output ||= ::CoffeeScript::compile(data, options)
758
- end
759
- end
760
- register 'coffee', CoffeeTemplate
761
-
762
- # Radius Template
763
- # http://github.com/jlong/radius/
764
- class RadiusTemplate < Template
765
- def initialize_engine
766
- return if defined? ::Radius
767
- require_template_library 'radius'
768
- end
769
-
770
- def prepare
771
- @context = Class.new(Radius::Context).new
772
- end
773
-
774
- def evaluate(scope, locals, &block)
775
- @context.define_tag("yield") do
776
- block.call
777
- end
778
- (class << @context; self; end).class_eval do
779
- define_method :tag_missing do |tag, attr, &block|
780
- if locals.key?(tag.to_sym)
781
- locals[tag.to_sym]
782
- else
783
- scope.__send__(tag) # any way to support attr as args?
784
- end
785
- end
786
- end
787
- # TODO: how to config tag prefix?
788
- parser = Radius::Parser.new(@context, :tag_prefix => 'r')
789
- parser.parse(data)
790
- end
791
- end
792
- register 'radius', RadiusTemplate
793
-
794
- end