tilt 0.6 → 0.9

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/tilt.rb CHANGED
@@ -1,10 +1,11 @@
1
+ require 'digest/md5'
2
+
1
3
  module Tilt
2
- VERSION = '0.6'
4
+ VERSION = '0.9'
3
5
 
4
6
  @template_mappings = {}
5
7
 
6
- # Hash of template path pattern => template implementation
7
- # class mappings.
8
+ # Hash of template path pattern => template implementation class mappings.
8
9
  def self.mappings
9
10
  @template_mappings
10
11
  end
@@ -15,6 +16,11 @@ module Tilt
15
16
  mappings[ext.downcase] = template_class
16
17
  end
17
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
+
18
24
  # Create a new template for the given file using the file's extension
19
25
  # to determine the the template mapping.
20
26
  def self.new(file, line=nil, options={}, &block)
@@ -25,28 +31,35 @@ module Tilt
25
31
  end
26
32
  end
27
33
 
28
- # Lookup a template class given for the given filename or file
34
+ # Lookup a template class for the given filename or file
29
35
  # extension. Return nil when no implementation is found.
30
36
  def self.[](file)
31
- if @template_mappings.key?(pattern = file.to_s.downcase)
32
- @template_mappings[pattern]
33
- elsif @template_mappings.key?(pattern = File.basename(pattern))
34
- @template_mappings[pattern]
35
- else
36
- while !pattern.empty?
37
- if @template_mappings.key?(pattern)
38
- return @template_mappings[pattern]
39
- else
40
- pattern = pattern.sub(/^[^.]*\.?/, '')
41
- end
42
- end
43
- nil
37
+ pattern = file.to_s.downcase
38
+ unless registered?(pattern)
39
+ pattern = File.basename(pattern)
40
+ pattern.sub!(/^[^.]*\.?/, '') until (pattern.empty? || registered?(pattern))
44
41
  end
42
+ @template_mappings[pattern]
45
43
  end
46
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
47
60
 
48
61
  # Base class for template implementations. Subclasses must implement
49
- # the #compile! method and one of the #evaluate or #template_source
62
+ # the #prepare method and one of the #evaluate or #template_source
50
63
  # methods.
51
64
  class Template
52
65
  # Template source; loaded from a file or given directly.
@@ -63,52 +76,55 @@ module Tilt
63
76
  # interface.
64
77
  attr_reader :options
65
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
+
66
87
  # Create a new template with the file, line, and options specified. By
67
- # default, template data is read from the file specified. When a block
68
- # is given, it should read template data and return as a String. When
69
- # file is nil, a block is required.
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.
70
91
  #
71
- # The #initialize_engine method is called if this is the very first
72
- # time this template subclass has been initialized.
92
+ # All arguments are optional.
73
93
  def initialize(file=nil, line=1, options={}, &block)
74
- raise ArgumentError, "file or block required" if file.nil? && block.nil?
75
- options, line = line, 1 if line.is_a?(Hash)
76
- @file = file
77
- @line = line || 1
78
- @options = options || {}
79
- @reader = block || lambda { |t| File.read(file) }
80
- @data = nil
81
-
82
- if !self.class.engine_initialized
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?
83
110
  initialize_engine
84
111
  self.class.engine_initialized = true
85
112
  end
86
- end
87
-
88
- # Called once and only once for each template subclass the first time
89
- # the template class is initialized. This should be used to require the
90
- # underlying template library and perform any initial setup.
91
- def initialize_engine
92
- end
93
- @engine_initialized = false
94
- class << self ; attr_accessor :engine_initialized ; end
95
113
 
114
+ # used to generate unique method names for template compilation
115
+ @stamp = (Time.now.to_f * 10000).to_i
116
+ @compiled_method_names = {}
96
117
 
97
- # Load template source and compile the template. The template is
98
- # loaded and compiled the first time this method is called; subsequent
99
- # calls are no-ops.
100
- def compile
101
- if @data.nil?
102
- @data = @reader.call(self)
103
- compile!
104
- end
118
+ # load template data and prepare
119
+ @reader = block || lambda { |t| File.read(@file) }
120
+ @data = @reader.call(self)
121
+ prepare
105
122
  end
106
123
 
107
124
  # Render the template in the given scope with the locals specified. If a
108
125
  # block is given, it is typically available within the template via
109
126
  # +yield+.
110
127
  def render(scope=Object.new, locals={}, &block)
111
- compile
112
128
  evaluate scope, locals || {}, &block
113
129
  end
114
130
 
@@ -128,43 +144,167 @@ module Tilt
128
144
  end
129
145
 
130
146
  protected
131
- # Do whatever preparation is necessary to "compile" the template.
132
- # Called immediately after template #data is loaded. Instance variables
133
- # set in this method are available when #evaluate is called.
147
+ # Called once and only once for each template subclass the first time
148
+ # the template class is initialized. This should be used to require the
149
+ # underlying template library and perform any initial setup.
150
+ def initialize_engine
151
+ end
152
+
153
+ # Like Kernel::require but issues a warning urging a manual require when
154
+ # running under a threaded environment.
155
+ def require_template_library(name)
156
+ if Thread.list.size > 1
157
+ warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
158
+ "explicit require '#{name}' suggested."
159
+ end
160
+ require name
161
+ end
162
+
163
+ # Do whatever preparation is necessary to setup the underlying template
164
+ # engine. Called immediately after template data is loaded. Instance
165
+ # variables set in this method are available when #evaluate is called.
134
166
  #
135
167
  # Subclasses must provide an implementation of this method.
136
- def compile!
137
- raise NotImplementedError
168
+ def prepare
169
+ if respond_to?(:compile!)
170
+ # backward compat with tilt < 0.6; just in case
171
+ warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.'
172
+ compile!
173
+ else
174
+ raise NotImplementedError
175
+ end
138
176
  end
139
177
 
140
- # Process the template and return the result. Subclasses should override
141
- # this method unless they implement the #template_source.
178
+ # Process the template and return the result. When the scope mixes in
179
+ # the Tilt::CompileSite module, the template is compiled to a method and
180
+ # reused given identical locals keys. When the scope object
181
+ # does not mix in the CompileSite module, the template source is
182
+ # evaluated with instance_eval. In any case, template executation
183
+ # is guaranteed to be performed in the scope object with the locals
184
+ # specified and with support for yielding to the block.
142
185
  def evaluate(scope, locals, &block)
143
- source, offset = local_assignment_code(locals)
144
- source = [source, template_source].join("\n")
145
- scope.instance_eval source, eval_file, line - offset
186
+ if scope.respond_to?(:__tilt__)
187
+ method_name = compiled_method_name(locals.keys)
188
+ if scope.respond_to?(method_name)
189
+ scope.send(method_name, locals, &block)
190
+ else
191
+ compile_template_method(method_name, locals)
192
+ scope.send(method_name, locals, &block)
193
+ end
194
+ else
195
+ evaluate_source(scope, locals, &block)
196
+ end
146
197
  end
147
198
 
148
- # Return a string containing the (Ruby) source code for the template. The
149
- # default Template#evaluate implementation requires this method be
150
- # defined.
151
- def template_source
199
+ # Generates all template source by combining the preamble, template, and
200
+ # postamble and returns a two-tuple of the form: [source, offset], where
201
+ # source is the string containing (Ruby) source code for the template and
202
+ # offset is the integer line offset where line reporting should begin.
203
+ #
204
+ # Template subclasses may override this method when they need complete
205
+ # control over source generation or want to adjust the default line
206
+ # offset. In most cases, overriding the #precompiled_template method is
207
+ # easier and more appropriate.
208
+ def precompiled(locals)
209
+ preamble = precompiled_preamble(locals)
210
+ parts = [
211
+ preamble,
212
+ precompiled_template(locals),
213
+ precompiled_postamble(locals)
214
+ ]
215
+ [parts.join("\n"), preamble.count("\n") + 1]
216
+ end
217
+
218
+ # A string containing the (Ruby) source code for the template. The
219
+ # default Template#evaluate implementation requires either this method
220
+ # or the #precompiled method be overridden. When defined, the base
221
+ # Template guarantees correct file/line handling, locals support, custom
222
+ # scopes, and support for template compilation when the scope object
223
+ # allows it.
224
+ def precompiled_template(locals)
152
225
  raise NotImplementedError
153
226
  end
154
227
 
228
+ # Generates preamble code for initializing template state, and performing
229
+ # locals assignment. The default implementation performs locals
230
+ # assignment only. Lines included in the preamble are subtracted from the
231
+ # source line offset, so adding code to the preamble does not effect line
232
+ # reporting in Kernel::caller and backtraces.
233
+ def precompiled_preamble(locals)
234
+ locals.map { |k,v| "#{k} = locals[:#{k}]" }.join("\n")
235
+ end
236
+
237
+ # Generates postamble code for the precompiled template source. The
238
+ # string returned from this method is appended to the precompiled
239
+ # template source.
240
+ def precompiled_postamble(locals)
241
+ ''
242
+ end
243
+
244
+ # The unique compiled method name for the locals keys provided.
245
+ def compiled_method_name(locals_keys)
246
+ @compiled_method_names[locals_keys] ||=
247
+ generate_compiled_method_name(locals_keys)
248
+ end
249
+
155
250
  private
156
- def local_assignment_code(locals)
157
- return ['', 1] if locals.empty?
158
- source = locals.collect { |k,v| "#{k} = locals[:#{k}]" }
159
- [source.join("\n"), source.length]
251
+ # Evaluate the template source in the context of the scope object.
252
+ def evaluate_source(scope, locals, &block)
253
+ source, offset = precompiled(locals)
254
+ scope.instance_eval(source, eval_file, line - offset)
160
255
  end
161
256
 
162
- def require_template_library(name)
163
- if Thread.list.size > 1
164
- warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
165
- "explicit require '#{name}' suggested."
257
+ # JRuby doesn't allow Object#instance_eval to yield to the block it's
258
+ # closed over. This is by design and (ostensibly) something that will
259
+ # change in MRI, though no current MRI version tested (1.8.6 - 1.9.2)
260
+ # exhibits the behavior. More info here:
261
+ #
262
+ # http://jira.codehaus.org/browse/JRUBY-2599
263
+ #
264
+ # Additionally, JRuby's eval line reporting is off by one compared to
265
+ # all MRI versions tested.
266
+ #
267
+ # We redefine evaluate_source to work around both issues.
268
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
269
+ undef evaluate_source
270
+ def evaluate_source(scope, locals, &block)
271
+ source, offset = precompiled(locals)
272
+ file, lineno = eval_file, (line - offset) - 1
273
+ scope.instance_eval { Kernel::eval(source, binding, file, lineno) }
274
+ end
275
+ end
276
+
277
+ def generate_compiled_method_name(locals_keys)
278
+ parts = [object_id, @stamp] + locals_keys.map { |k| k.to_s }.sort
279
+ digest = Digest::MD5.hexdigest(parts.join(':'))
280
+ "__tilt_#{digest}"
281
+ end
282
+
283
+ def compile_template_method(method_name, locals)
284
+ source, offset = precompiled(locals)
285
+ offset += 1
286
+ CompileSite.module_eval <<-RUBY, eval_file, line - offset
287
+ def #{method_name}(locals)
288
+ #{source}
289
+ end
290
+ RUBY
291
+
292
+ ObjectSpace.define_finalizer self,
293
+ Template.compiled_template_method_remover(CompileSite, method_name)
294
+ end
295
+
296
+ def self.compiled_template_method_remover(site, method_name)
297
+ proc { |oid| garbage_collect_compiled_template_method(site, method_name) }
298
+ end
299
+
300
+ def self.garbage_collect_compiled_template_method(site, method_name)
301
+ site.module_eval do
302
+ begin
303
+ remove_method(method_name)
304
+ rescue NameError
305
+ # method was already removed (ruby >= 1.9)
306
+ end
166
307
  end
167
- require name
168
308
  end
169
309
  end
170
310
 
@@ -174,7 +314,7 @@ module Tilt
174
314
  # cache = Tilt::Cache.new
175
315
  # cache.fetch(path, line, options) { Tilt.new(path, line, options) }
176
316
  #
177
- # Subsequent invocations return the already compiled template object.
317
+ # Subsequent invocations return the already loaded template object.
178
318
  class Cache
179
319
  def initialize
180
320
  @cache = {}
@@ -196,11 +336,11 @@ module Tilt
196
336
  # The template source is evaluated as a Ruby string. The #{} interpolation
197
337
  # syntax can be used to generated dynamic output.
198
338
  class StringTemplate < Template
199
- def compile!
339
+ def prepare
200
340
  @code = "%Q{#{data}}"
201
341
  end
202
342
 
203
- def template_source
343
+ def precompiled_template(locals)
204
344
  @code
205
345
  end
206
346
  end
@@ -211,65 +351,87 @@ module Tilt
211
351
  # http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
212
352
  class ERBTemplate < Template
213
353
  def initialize_engine
214
- require_template_library 'erb' unless defined? ::ERB
354
+ return if defined? ::ERB
355
+ require_template_library 'erb'
215
356
  end
216
357
 
217
- def compile!
218
- @engine = ::ERB.new(data, options[:safe], options[:trim], '@_out_buf')
358
+ def prepare
359
+ @outvar = (options[:outvar] || '_erbout').to_s
360
+ @engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
219
361
  end
220
362
 
221
- def template_source
363
+ def precompiled_template(locals)
222
364
  @engine.src
223
365
  end
224
366
 
225
- def evaluate(scope, locals, &block)
226
- source, offset = local_assignment_code(locals)
227
- source = [source, template_source].join("\n")
228
-
229
- original_out_buf =
230
- scope.instance_variables.any? { |var| var.to_sym == :@_out_buf } &&
231
- scope.instance_variable_get(:@_out_buf)
232
-
233
- scope.instance_eval source, eval_file, line - offset
234
-
235
- output = scope.instance_variable_get(:@_out_buf)
236
- scope.instance_variable_set(:@_out_buf, original_out_buf)
237
-
238
- output
367
+ def precompiled_preamble(locals)
368
+ <<-RUBY
369
+ begin
370
+ __original_outvar = #{@outvar} if defined?(#{@outvar})
371
+ #{super}
372
+ RUBY
239
373
  end
240
374
 
241
- private
375
+ def precompiled_postamble(locals)
376
+ <<-RUBY
377
+ #{super}
378
+ ensure
379
+ #{@outvar} = __original_outvar
380
+ end
381
+ RUBY
382
+ end
242
383
 
243
384
  # ERB generates a line to specify the character coding of the generated
244
385
  # source in 1.9. Account for this in the line offset.
245
386
  if RUBY_VERSION >= '1.9.0'
246
- def local_assignment_code(locals)
387
+ def precompiled(locals)
247
388
  source, offset = super
248
389
  [source, offset + 1]
249
390
  end
250
391
  end
251
392
  end
393
+
252
394
  %w[erb rhtml].each { |ext| register ext, ERBTemplate }
253
395
 
254
396
 
255
397
  # Erubis template implementation. See:
256
398
  # http://www.kuwata-lab.com/erubis/
399
+ #
400
+ # ErubisTemplate supports the following additional options, which are not
401
+ # passed down to the Erubis engine:
402
+ #
403
+ # :engine_class allows you to specify a custom engine class to use
404
+ # instead of the default (which is ::Erubis::Eruby).
405
+ #
406
+ # :escape_html when true, ::Erubis::EscapedEruby will be used as
407
+ # the engine class instead of the default. All content
408
+ # within <%= %> blocks will be automatically html escaped.
257
409
  class ErubisTemplate < ERBTemplate
258
410
  def initialize_engine
259
- require_template_library 'erubis' unless defined? ::Erubis
411
+ return if defined? ::Erubis
412
+ require_template_library 'erubis'
260
413
  end
261
414
 
262
- def compile!
263
- Erubis::Eruby.class_eval(%Q{def add_preamble(src) src << "@_out_buf = _buf = '';" end})
264
- @engine = ::Erubis::Eruby.new(data, options)
415
+ def prepare
416
+ @options.merge!(:preamble => false, :postamble => false)
417
+ @outvar = (options.delete(:outvar) || '_erbout').to_s
418
+ engine_class = options.delete(:engine_class)
419
+ engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
420
+ @engine = (engine_class || ::Erubis::Eruby).new(data, options)
265
421
  end
266
422
 
267
- private
423
+ def precompiled_preamble(locals)
424
+ [super, "#{@outvar} = _buf = ''"].join("\n")
425
+ end
268
426
 
269
- # Erubis doesn't have ERB's line-off-by-one under 1.9 problem. Override
270
- # and adjust back.
427
+ def precompiled_postamble(locals)
428
+ ["_buf", super].join("\n")
429
+ end
430
+
431
+ # Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
432
+ # Override and adjust back.
271
433
  if RUBY_VERSION >= '1.9.0'
272
- def local_assignment_code(locals)
434
+ def precompiled(locals)
273
435
  source, offset = super
274
436
  [source, offset - 1]
275
437
  end
@@ -282,20 +444,54 @@ module Tilt
282
444
  # http://haml.hamptoncatlin.com/
283
445
  class HamlTemplate < Template
284
446
  def initialize_engine
285
- require_template_library 'haml' unless defined? ::Haml::Engine
447
+ return if defined? ::Haml::Engine
448
+ require_template_library 'haml'
286
449
  end
287
450
 
288
- def compile!
289
- @engine = ::Haml::Engine.new(data, haml_options)
451
+ def prepare
452
+ options = @options.merge(:filename => eval_file, :line => line)
453
+ @engine = ::Haml::Engine.new(data, options)
290
454
  end
291
455
 
292
456
  def evaluate(scope, locals, &block)
293
- @engine.render(scope, locals, &block)
457
+ if @engine.respond_to?(:precompiled_method_return_value, true)
458
+ super
459
+ else
460
+ @engine.render(scope, locals, &block)
461
+ end
294
462
  end
295
463
 
296
- private
297
- def haml_options
298
- options.merge(:filename => eval_file, :line => line)
464
+ # Precompiled Haml source. Taken from the precompiled_with_ambles
465
+ # method in Haml::Precompiler:
466
+ # http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126
467
+ def precompiled_template(locals)
468
+ @engine.precompiled
469
+ end
470
+
471
+ def precompiled_preamble(locals)
472
+ local_assigns = super
473
+ @engine.instance_eval do
474
+ <<-RUBY
475
+ begin
476
+ extend Haml::Helpers
477
+ _hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})
478
+ _erbout = _hamlout.buffer
479
+ __in_erb_template = true
480
+ _haml_locals = locals
481
+ #{local_assigns}
482
+ RUBY
483
+ end
484
+ end
485
+
486
+ def precompiled_postamble(locals)
487
+ @engine.instance_eval do
488
+ <<-RUBY
489
+ #{precompiled_method_return_value}
490
+ ensure
491
+ @haml_buffer = @haml_buffer.upper
492
+ end
493
+ RUBY
494
+ end
299
495
  end
300
496
  end
301
497
  register 'haml', HamlTemplate
@@ -307,15 +503,16 @@ module Tilt
307
503
  # Sass templates do not support object scopes, locals, or yield.
308
504
  class SassTemplate < Template
309
505
  def initialize_engine
310
- require_template_library 'sass' unless defined? ::Sass::Engine
506
+ return if defined? ::Sass::Engine
507
+ require_template_library 'sass'
311
508
  end
312
509
 
313
- def compile!
510
+ def prepare
314
511
  @engine = ::Sass::Engine.new(data, sass_options)
315
512
  end
316
513
 
317
514
  def evaluate(scope, locals, &block)
318
- @engine.render
515
+ @output ||= @engine.render
319
516
  end
320
517
 
321
518
  private
@@ -332,10 +529,11 @@ module Tilt
332
529
  # Less templates do not support object scopes, locals, or yield.
333
530
  class LessTemplate < Template
334
531
  def initialize_engine
335
- require_template_library 'less' unless defined? ::Less::Engine
532
+ return if defined? ::Less::Engine
533
+ require_template_library 'less'
336
534
  end
337
535
 
338
- def compile!
536
+ def prepare
339
537
  @engine = ::Less::Engine.new(data)
340
538
  end
341
539
 
@@ -345,14 +543,16 @@ module Tilt
345
543
  end
346
544
  register 'less', LessTemplate
347
545
 
546
+
348
547
  # Builder template implementation. See:
349
548
  # http://builder.rubyforge.org/
350
549
  class BuilderTemplate < Template
351
550
  def initialize_engine
352
- require_template_library 'builder' unless defined?(::Builder)
551
+ return if defined?(::Builder)
552
+ require_template_library 'builder'
353
553
  end
354
554
 
355
- def compile!
555
+ def prepare
356
556
  end
357
557
 
358
558
  def evaluate(scope, locals, &block)
@@ -366,7 +566,7 @@ module Tilt
366
566
  xml.target!
367
567
  end
368
568
 
369
- def template_source
569
+ def precompiled_template(locals)
370
570
  data.to_str
371
571
  end
372
572
  end
@@ -388,10 +588,11 @@ module Tilt
388
588
  # time when using this template engine.
389
589
  class LiquidTemplate < Template
390
590
  def initialize_engine
391
- require_template_library 'liquid' unless defined? ::Liquid::Template
591
+ return if defined? ::Liquid::Template
592
+ require_template_library 'liquid'
392
593
  end
393
594
 
394
- def compile!
595
+ def prepare
395
596
  @engine = ::Liquid::Template.parse(data)
396
597
  end
397
598
 
@@ -421,15 +622,17 @@ module Tilt
421
622
  end
422
623
 
423
624
  def initialize_engine
424
- require_template_library 'rdiscount' unless defined? ::RDiscount
625
+ return if defined? ::RDiscount
626
+ require_template_library 'rdiscount'
425
627
  end
426
628
 
427
- def compile!
629
+ def prepare
428
630
  @engine = RDiscount.new(data, *flags)
631
+ @output = nil
429
632
  end
430
633
 
431
634
  def evaluate(scope, locals, &block)
432
- @engine.to_html
635
+ @output ||= @engine.to_html
433
636
  end
434
637
  end
435
638
  register 'markdown', RDiscountTemplate
@@ -441,15 +644,17 @@ module Tilt
441
644
  # http://redcloth.org/
442
645
  class RedClothTemplate < Template
443
646
  def initialize_engine
444
- require_template_library 'redcloth' unless defined? ::RedCloth
647
+ return if defined? ::RedCloth
648
+ require_template_library 'redcloth'
445
649
  end
446
650
 
447
- def compile!
651
+ def prepare
448
652
  @engine = RedCloth.new(data)
653
+ @output = nil
449
654
  end
450
655
 
451
656
  def evaluate(scope, locals, &block)
452
- @engine.to_html
657
+ @output ||= @engine.to_html
453
658
  end
454
659
  end
455
660
  register 'textile', RedClothTemplate
@@ -465,10 +670,11 @@ module Tilt
465
670
  attr_reader :engine
466
671
 
467
672
  def initialize_engine
468
- require_template_library 'mustache' unless defined? ::Mustache
673
+ return if defined? ::Mustache
674
+ require_template_library 'mustache'
469
675
  end
470
676
 
471
- def compile!
677
+ def prepare
472
678
  Mustache.view_namespace = options[:namespace]
473
679
  Mustache.view_path = options[:view_path] || options[:mustaches]
474
680
  @engine = options[:view] || Mustache.view_class(name)
@@ -502,6 +708,7 @@ module Tilt
502
708
  end
503
709
  register 'mustache', MustacheTemplate
504
710
 
711
+
505
712
  # RDoc template. See:
506
713
  # http://rdoc.rubyforge.org/
507
714
  #
@@ -510,36 +717,38 @@ module Tilt
510
717
  # engine.
511
718
  class RDocTemplate < Template
512
719
  def initialize_engine
513
- unless defined?(::RDoc::Markup)
514
- require_template_library 'rdoc/markup'
515
- require_template_library 'rdoc/markup/to_html'
516
- end
720
+ return if defined?(::RDoc::Markup)
721
+ require_template_library 'rdoc/markup'
722
+ require_template_library 'rdoc/markup/to_html'
517
723
  end
518
724
 
519
- def compile!
725
+ def prepare
520
726
  markup = RDoc::Markup::ToHtml.new
521
727
  @engine = markup.convert(data)
728
+ @output = nil
522
729
  end
523
730
 
524
731
  def evaluate(scope, locals, &block)
525
- @engine.to_s
732
+ @output ||= @engine.to_s
526
733
  end
527
734
  end
528
735
  register 'rdoc', RDocTemplate
529
736
 
737
+
530
738
  # CoffeeScript info:
531
739
  # http://jashkenas.github.com/coffee-script/
532
740
  class CoffeeTemplate < Template
533
741
  def initialize_engine
534
- require_template_library 'coffee-script' unless defined? ::CoffeeScript
742
+ return if defined? ::CoffeeScript
743
+ require_template_library 'coffee-script'
535
744
  end
536
745
 
537
- def compile!
538
- @engine = ::CoffeeScript::compile(data, options)
746
+ def prepare
747
+ @output = nil
539
748
  end
540
749
 
541
750
  def evaluate(scope, locals, &block)
542
- @engine
751
+ @output ||= ::CoffeeScript::compile(data, options)
543
752
  end
544
753
  end
545
754
  register 'coffee', CoffeeTemplate