frank 0.3.2 → 0.4.0

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