tilt 1.2.2 → 1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,23 +1,59 @@
1
1
  module Tilt
2
- TOPOBJECT = defined?(BasicObject) ? BasicObject : Object
3
- VERSION = '1.2.2'
2
+ VERSION = '1.3'
4
3
 
5
- @template_mappings = {}
4
+ @preferred_mappings = Hash.new
5
+ @template_mappings = Hash.new { |h, k| h[k] = [] }
6
6
 
7
7
  # Hash of template path pattern => template implementation class mappings.
8
8
  def self.mappings
9
9
  @template_mappings
10
10
  end
11
11
 
12
+ def self.normalize(ext)
13
+ ext.to_s.downcase.sub(/^\./, '')
14
+ end
15
+
12
16
  # Register a template implementation by file extension.
13
- def self.register(ext, template_class)
14
- ext = ext.to_s.sub(/^\./, '')
15
- mappings[ext.downcase] = template_class
17
+ def self.register(template_class, *extensions)
18
+ if template_class.respond_to?(:to_str)
19
+ # Support register(ext, template_class) too
20
+ ext = template_class
21
+ template_class = extensions[0]
22
+ extensions = [ext]
23
+ end
24
+
25
+ extensions.each do |ext|
26
+ ext = normalize(ext)
27
+ mappings[ext].unshift(template_class).uniq!
28
+ end
29
+ end
30
+
31
+ # Makes a template class preferred for the given file extensions. If you
32
+ # don't provide any extensions, it will be preferred for all its already
33
+ # registered extensions:
34
+ #
35
+ # # Prefer RDiscount for its registered file extensions:
36
+ # Tilt.prefer(Tilt::RDiscountTemplate)
37
+ #
38
+ # # Prefer RDiscount only for the .md extensions:
39
+ # Tilt.prefer(Tilt::RDiscountTemplate, '.md')
40
+ def self.prefer(template_class, *extensions)
41
+ if extensions.empty?
42
+ mappings.each do |ext, klasses|
43
+ @preferred_mappings[ext] = template_class if klasses.include? template_class
44
+ end
45
+ else
46
+ extensions.each do |ext|
47
+ ext = normalize(ext)
48
+ register(template_class, ext)
49
+ @preferred_mappings[ext] = template_class
50
+ end
51
+ end
16
52
  end
17
53
 
18
54
  # Returns true when a template exists on an exact match of the provided file extension
19
55
  def self.registered?(ext)
20
- mappings.key?(ext.downcase)
56
+ mappings.key?(ext.downcase) && !mappings[ext.downcase].empty?
21
57
  end
22
58
 
23
59
  # Create a new template for the given file using the file's extension
@@ -34,298 +70,47 @@ module Tilt
34
70
  # extension. Return nil when no implementation is found.
35
71
  def self.[](file)
36
72
  pattern = file.to_s.downcase
37
- unless registered?(pattern)
73
+ until pattern.empty? || registered?(pattern)
38
74
  pattern = File.basename(pattern)
39
- pattern.sub!(/^[^.]*\.?/, '') until (pattern.empty? || registered?(pattern))
40
- end
41
- @template_mappings[pattern]
42
- end
43
-
44
- # Deprecated module.
45
- module CompileSite
46
- end
47
-
48
- # Base class for template implementations. Subclasses must implement
49
- # the #prepare method and one of the #evaluate or #precompiled_template
50
- # methods.
51
- class Template
52
- # Template source; loaded from a file or given directly.
53
- attr_reader :data
54
-
55
- # The name of the file where the template data was loaded from.
56
- attr_reader :file
57
-
58
- # The line number in #file where template data was loaded from.
59
- attr_reader :line
60
-
61
- # A Hash of template engine specific options. This is passed directly
62
- # to the underlying engine and is not used by the generic template
63
- # interface.
64
- attr_reader :options
65
-
66
- # Used to determine if this class's initialize_engine method has
67
- # been called yet.
68
- @engine_initialized = false
69
- class << self
70
- attr_accessor :engine_initialized
71
- alias engine_initialized? engine_initialized
75
+ pattern.sub!(/^[^.]*\.?/, '')
72
76
  end
73
77
 
74
- # Create a new template with the file, line, and options specified. By
75
- # default, template data is read from the file. When a block is given,
76
- # it should read template data and return as a String. When file is nil,
77
- # a block is required.
78
- #
79
- # All arguments are optional.
80
- def initialize(file=nil, line=1, options={}, &block)
81
- @file, @line, @options = nil, 1, {}
82
-
83
- [options, line, file].compact.each do |arg|
84
- case
85
- when arg.respond_to?(:to_str) ; @file = arg.to_str
86
- when arg.respond_to?(:to_int) ; @line = arg.to_int
87
- when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
88
- else raise TypeError
89
- end
90
- end
78
+ # Try to find a preferred engine.
79
+ klass = @preferred_mappings[pattern]
80
+ return klass if klass
91
81
 
92
- raise ArgumentError, "file or block required" if (@file || block).nil?
82
+ # Fall back to the general list of mappings.
83
+ klasses = @template_mappings[pattern]
93
84
 
94
- # call the initialize_engine method if this is the very first time
95
- # an instance of this class has been created.
96
- if !self.class.engine_initialized?
97
- initialize_engine
98
- self.class.engine_initialized = true
85
+ # Try to find an engine which is already loaded.
86
+ template = klasses.detect do |klass|
87
+ if klass.respond_to?(:engine_initialized?)
88
+ klass.engine_initialized?
99
89
  end
100
-
101
- # used to hold compiled template methods
102
- @compiled_method = {}
103
-
104
- # used on 1.9 to set the encoding if it is not set elsewhere (like a magic comment)
105
- # currently only used if template compiles to ruby
106
- @default_encoding = @options.delete :default_encoding
107
-
108
- # load template data and prepare (uses binread to avoid encoding issues)
109
- @reader = block || lambda { |t| File.respond_to?(:binread) ? File.binread(@file) : File.read(@file) }
110
- @data = @reader.call(self)
111
- prepare
112
90
  end
113
91
 
114
- # Render the template in the given scope with the locals specified. If a
115
- # block is given, it is typically available within the template via
116
- # +yield+.
117
- def render(scope=Object.new, locals={}, &block)
118
- evaluate scope, locals || {}, &block
119
- end
120
-
121
- # The basename of the template file.
122
- def basename(suffix='')
123
- File.basename(file, suffix) if file
124
- end
125
-
126
- # The template file's basename with all extensions chomped off.
127
- def name
128
- basename.split('.', 2).first if basename
129
- end
130
-
131
- # The filename used in backtraces to describe the template.
132
- def eval_file
133
- file || '(__TEMPLATE__)'
134
- end
92
+ return template if template
135
93
 
136
- protected
137
- # Called once and only once for each template subclass the first time
138
- # the template class is initialized. This should be used to require the
139
- # underlying template library and perform any initial setup.
140
- def initialize_engine
141
- end
94
+ # Try each of the classes until one succeeds. If all of them fails,
95
+ # we'll raise the error of the first class.
96
+ first_failure = nil
142
97
 
143
- # Like Kernel::require but issues a warning urging a manual require when
144
- # running under a threaded environment.
145
- def require_template_library(name)
146
- if Thread.list.size > 1
147
- warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
148
- "explicit require '#{name}' suggested."
149
- end
150
- require name
151
- end
152
-
153
- # Do whatever preparation is necessary to setup the underlying template
154
- # engine. Called immediately after template data is loaded. Instance
155
- # variables set in this method are available when #evaluate is called.
156
- #
157
- # Subclasses must provide an implementation of this method.
158
- def prepare
159
- if respond_to?(:compile!)
160
- # backward compat with tilt < 0.6; just in case
161
- warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.'
162
- compile!
98
+ klasses.each do |klass|
99
+ begin
100
+ klass.new { '' }
101
+ rescue Exception => ex
102
+ first_failure ||= ex
103
+ next
163
104
  else
164
- raise NotImplementedError
165
- end
166
- end
167
-
168
- def evaluate(scope, locals, &block)
169
- cached_evaluate(scope, locals, &block)
170
- end
171
-
172
- # Process the template and return the result. The first time this
173
- # method is called, the template source is evaluated with instance_eval.
174
- # On the sequential method calls it will compile the template to an
175
- # unbound method which will lead to better performance. In any case,
176
- # template executation is guaranteed to be performed in the scope object
177
- # with the locals specified and with support for yielding to the block.
178
- def cached_evaluate(scope, locals, &block)
179
- # Redefine itself to use method compilation the next time:
180
- def self.cached_evaluate(scope, locals, &block)
181
- method = compiled_method(locals.keys)
182
- method.bind(scope).call(locals, &block)
183
- end
184
-
185
- # Use instance_eval the first time:
186
- evaluate_source(scope, locals, &block)
187
- end
188
-
189
- # Generates all template source by combining the preamble, template, and
190
- # postamble and returns a two-tuple of the form: [source, offset], where
191
- # source is the string containing (Ruby) source code for the template and
192
- # offset is the integer line offset where line reporting should begin.
193
- #
194
- # Template subclasses may override this method when they need complete
195
- # control over source generation or want to adjust the default line
196
- # offset. In most cases, overriding the #precompiled_template method is
197
- # easier and more appropriate.
198
- def precompiled(locals)
199
- preamble = precompiled_preamble(locals)
200
- template = precompiled_template(locals)
201
- magic_comment = extract_magic_comment(template)
202
- if magic_comment
203
- # Magic comment e.g. "# coding: utf-8" has to be in the first line.
204
- # So we copy the magic comment to the first line.
205
- preamble = magic_comment + "\n" + preamble
105
+ return klass
206
106
  end
207
- parts = [
208
- preamble,
209
- template,
210
- precompiled_postamble(locals)
211
- ]
212
- [parts.join("\n"), preamble.count("\n") + 1]
213
- end
214
-
215
- # A string containing the (Ruby) source code for the template. The
216
- # default Template#evaluate implementation requires either this method
217
- # or the #precompiled method be overridden. When defined, the base
218
- # Template guarantees correct file/line handling, locals support, custom
219
- # scopes, and support for template compilation when the scope object
220
- # allows it.
221
- def precompiled_template(locals)
222
- raise NotImplementedError
223
- end
224
-
225
- # Generates preamble code for initializing template state, and performing
226
- # locals assignment. The default implementation performs locals
227
- # assignment only. Lines included in the preamble are subtracted from the
228
- # source line offset, so adding code to the preamble does not effect line
229
- # reporting in Kernel::caller and backtraces.
230
- def precompiled_preamble(locals)
231
- locals.map { |k,v| "#{k} = locals[:#{k}]" }.join("\n")
232
107
  end
233
108
 
234
- # Generates postamble code for the precompiled template source. The
235
- # string returned from this method is appended to the precompiled
236
- # template source.
237
- def precompiled_postamble(locals)
238
- ''
239
- end
240
-
241
- # The compiled method for the locals keys provided.
242
- def compiled_method(locals_keys)
243
- @compiled_method[locals_keys] ||=
244
- compile_template_method(locals_keys)
245
- end
246
-
247
- private
248
- # Evaluate the template source in the context of the scope object.
249
- def evaluate_source(scope, locals, &block)
250
- source, offset = precompiled(locals)
251
- scope.instance_eval(source, eval_file, line - offset)
252
- end
253
-
254
- # JRuby doesn't allow Object#instance_eval to yield to the block it's
255
- # closed over. This is by design and (ostensibly) something that will
256
- # change in MRI, though no current MRI version tested (1.8.6 - 1.9.2)
257
- # exhibits the behavior. More info here:
258
- #
259
- # http://jira.codehaus.org/browse/JRUBY-2599
260
- #
261
- # Additionally, JRuby's eval line reporting is off by one compared to
262
- # all MRI versions tested.
263
- #
264
- # We redefine evaluate_source to work around both issues.
265
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
266
- undef evaluate_source
267
- def evaluate_source(scope, locals, &block)
268
- source, offset = precompiled(locals)
269
- file, lineno = eval_file, (line - offset) - 1
270
- scope.instance_eval { Kernel::eval(source, binding, file, lineno) }
271
- end
272
- end
273
-
274
- def compile_template_method(locals)
275
- source, offset = precompiled(locals)
276
- offset += 5
277
- method_name = "__tilt_#{Thread.current.object_id.abs}"
278
- Object.class_eval <<-RUBY, eval_file, line - offset
279
- #{extract_magic_comment source}
280
- TOPOBJECT.class_eval do
281
- def #{method_name}(locals)
282
- Thread.current[:tilt_vars] = [self, locals]
283
- class << self
284
- this, locals = Thread.current[:tilt_vars]
285
- this.instance_eval do
286
- #{source}
287
- end
288
- end
289
- end
290
- end
291
- RUBY
292
- unbind_compiled_method(method_name)
293
- end
294
-
295
- def unbind_compiled_method(method_name)
296
- method = TOPOBJECT.instance_method(method_name)
297
- TOPOBJECT.class_eval { remove_method(method_name) }
298
- method
299
- end
300
-
301
- def extract_magic_comment(script)
302
- comment = script.slice(/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/)
303
- return comment if comment and not %w[ascii-8bit binary].include?($1.downcase)
304
- "# coding: #{@default_encoding}" if @default_encoding
305
- end
109
+ raise first_failure if first_failure
110
+ end
306
111
 
307
- # Special case Ruby 1.9.1's broken yield.
308
- #
309
- # http://github.com/rtomayko/tilt/commit/20c01a5
310
- # http://redmine.ruby-lang.org/issues/show/3601
311
- #
312
- # Remove when 1.9.2 dominates 1.9.1 installs in the wild.
313
- if RUBY_VERSION =~ /^1.9.1/
314
- undef compile_template_method
315
- def compile_template_method(locals)
316
- source, offset = precompiled(locals)
317
- offset += 1
318
- method_name = "__tilt_#{Thread.current.object_id}"
319
- Object.class_eval <<-RUBY, eval_file, line - offset
320
- TOPOBJECT.class_eval do
321
- def #{method_name}(locals)
322
- #{source}
323
- end
324
- end
325
- RUBY
326
- unbind_compiled_method(method_name)
327
- end
328
- end
112
+ # Deprecated module.
113
+ module CompileSite
329
114
  end
330
115
 
331
116
  # Extremely simple template cache implementation. Calling applications
@@ -352,550 +137,52 @@ module Tilt
352
137
 
353
138
  # Template Implementations ================================================
354
139
 
140
+ require 'tilt/string'
141
+ register StringTemplate, 'str'
355
142
 
356
- # The template source is evaluated as a Ruby string. The #{} interpolation
357
- # syntax can be used to generated dynamic output.
358
- class StringTemplate < Template
359
- def prepare
360
- @code = "%Q{#{data}}"
361
- end
362
-
363
- def precompiled_template(locals)
364
- @code
365
- end
366
- end
367
- register 'str', StringTemplate
368
-
369
-
370
- # ERB template implementation. See:
371
- # http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
372
- class ERBTemplate < Template
373
- @@default_output_variable = '_erbout'
374
-
375
- def self.default_output_variable
376
- @@default_output_variable
377
- end
378
-
379
- def self.default_output_variable=(name)
380
- @@default_output_variable = name
381
- end
382
-
383
- def initialize_engine
384
- return if defined? ::ERB
385
- require_template_library 'erb'
386
- end
387
-
388
- def prepare
389
- @outvar = options[:outvar] || self.class.default_output_variable
390
- @engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
391
- end
392
-
393
- def precompiled_template(locals)
394
- source = @engine.src
395
- source
396
- end
397
-
398
- def precompiled_preamble(locals)
399
- <<-RUBY
400
- begin
401
- __original_outvar = #{@outvar} if defined?(#{@outvar})
402
- #{super}
403
- RUBY
404
- end
405
-
406
- def precompiled_postamble(locals)
407
- <<-RUBY
408
- #{super}
409
- ensure
410
- #{@outvar} = __original_outvar
411
- end
412
- RUBY
413
- end
414
-
415
- # ERB generates a line to specify the character coding of the generated
416
- # source in 1.9. Account for this in the line offset.
417
- if RUBY_VERSION >= '1.9.0'
418
- def precompiled(locals)
419
- source, offset = super
420
- [source, offset + 1]
421
- end
422
- end
423
- end
424
-
425
- %w[erb rhtml].each { |ext| register ext, ERBTemplate }
426
-
427
-
428
- # Erubis template implementation. See:
429
- # http://www.kuwata-lab.com/erubis/
430
- #
431
- # ErubisTemplate supports the following additional options, which are not
432
- # passed down to the Erubis engine:
433
- #
434
- # :engine_class allows you to specify a custom engine class to use
435
- # instead of the default (which is ::Erubis::Eruby).
436
- #
437
- # :escape_html when true, ::Erubis::EscapedEruby will be used as
438
- # the engine class instead of the default. All content
439
- # within <%= %> blocks will be automatically html escaped.
440
- class ErubisTemplate < ERBTemplate
441
- def initialize_engine
442
- return if defined? ::Erubis
443
- require_template_library 'erubis'
444
- end
445
-
446
- def prepare
447
- @options.merge!(:preamble => false, :postamble => false)
448
- @outvar = options.delete(:outvar) || self.class.default_output_variable
449
- engine_class = options.delete(:engine_class)
450
- engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
451
- @engine = (engine_class || ::Erubis::Eruby).new(data, options)
452
- end
453
-
454
- def precompiled_preamble(locals)
455
- [super, "#{@outvar} = _buf = ''"].join("\n")
456
- end
457
-
458
- def precompiled_postamble(locals)
459
- ["_buf", super].join("\n")
460
- end
461
-
462
- # Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
463
- # Override and adjust back.
464
- if RUBY_VERSION >= '1.9.0'
465
- def precompiled(locals)
466
- source, offset = super
467
- [source, offset - 1]
468
- end
469
- end
470
- end
471
- register 'erubis', ErubisTemplate
472
-
473
-
474
- # Haml template implementation. See:
475
- # http://haml.hamptoncatlin.com/
476
- class HamlTemplate < Template
477
- def initialize_engine
478
- return if defined? ::Haml::Engine
479
- require_template_library 'haml'
480
- end
481
-
482
- def prepare
483
- options = @options.merge(:filename => eval_file, :line => line)
484
- @engine = ::Haml::Engine.new(data, options)
485
- end
486
-
487
- def evaluate(scope, locals, &block)
488
- if @engine.respond_to?(:precompiled_method_return_value, true)
489
- super
490
- else
491
- @engine.render(scope, locals, &block)
492
- end
493
- end
494
-
495
- # Precompiled Haml source. Taken from the precompiled_with_ambles
496
- # method in Haml::Precompiler:
497
- # http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126
498
- def precompiled_template(locals)
499
- @engine.precompiled
500
- end
501
-
502
- def precompiled_preamble(locals)
503
- local_assigns = super
504
- @engine.instance_eval do
505
- <<-RUBY
506
- begin
507
- extend Haml::Helpers
508
- _hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})
509
- _erbout = _hamlout.buffer
510
- __in_erb_template = true
511
- _haml_locals = locals
512
- #{local_assigns}
513
- RUBY
514
- end
515
- end
516
-
517
- def precompiled_postamble(locals)
518
- @engine.instance_eval do
519
- <<-RUBY
520
- #{precompiled_method_return_value}
521
- ensure
522
- @haml_buffer = @haml_buffer.upper
523
- end
524
- RUBY
525
- end
526
- end
527
- end
528
- register 'haml', HamlTemplate
529
-
530
-
531
- # Sass template implementation. See:
532
- # http://haml.hamptoncatlin.com/
533
- #
534
- # Sass templates do not support object scopes, locals, or yield.
535
- class SassTemplate < Template
536
- def initialize_engine
537
- return if defined? ::Sass::Engine
538
- require_template_library 'sass'
539
- end
540
-
541
- def prepare
542
- @engine = ::Sass::Engine.new(data, sass_options)
543
- end
544
-
545
- def evaluate(scope, locals, &block)
546
- @output ||= @engine.render
547
- end
548
-
549
- private
550
- def sass_options
551
- options.merge(:filename => eval_file, :line => line, :syntax => :sass)
552
- end
553
- end
554
- register 'sass', SassTemplate
555
-
556
- # Sass's new .scss type template implementation.
557
- class ScssTemplate < SassTemplate
558
- private
559
- def sass_options
560
- options.merge(:filename => eval_file, :line => line, :syntax => :scss)
561
- end
562
- end
563
- register 'scss', ScssTemplate
564
-
565
- # Lessscss template implementation. See:
566
- # http://lesscss.org/
567
- #
568
- # Less templates do not support object scopes, locals, or yield.
569
- class LessTemplate < Template
570
- def initialize_engine
571
- return if defined? ::Less::Engine
572
- require_template_library 'less'
573
- end
574
-
575
- def prepare
576
- @engine = ::Less::Engine.new(data)
577
- end
578
-
579
- def evaluate(scope, locals, &block)
580
- @engine.to_css
581
- end
582
- end
583
- register 'less', LessTemplate
143
+ require 'tilt/erb'
144
+ register ERBTemplate, 'erb', 'rhtml'
145
+ register ErubisTemplate, 'erb', 'rhtml', 'erubis'
584
146
 
147
+ require 'tilt/haml'
148
+ register HamlTemplate, 'haml'
585
149
 
586
- # CoffeeScript template implementation. See:
587
- # http://coffeescript.org/
588
- #
589
- # CoffeeScript templates do not support object scopes, locals, or yield.
590
- class CoffeeScriptTemplate < Template
591
- @@default_no_wrap = false
150
+ require 'tilt/css'
151
+ register SassTemplate, 'sass'
152
+ register ScssTemplate, 'scss'
153
+ register LessTemplate, 'less'
592
154
 
593
- def self.default_no_wrap
594
- @@default_no_wrap
595
- end
155
+ require 'tilt/coffee'
156
+ register CoffeeScriptTemplate, 'coffee'
596
157
 
597
- def self.default_no_wrap=(value)
598
- @@default_no_wrap = value
599
- end
158
+ require 'tilt/nokogiri'
159
+ register NokogiriTemplate, 'nokogiri'
600
160
 
601
- def initialize_engine
602
- return if defined? ::CoffeeScript
603
- require_template_library 'coffee_script'
604
- end
161
+ require 'tilt/builder'
162
+ register BuilderTemplate, 'builder'
605
163
 
606
- def prepare
607
- @no_wrap = options.key?(:no_wrap) ? options[:no_wrap] :
608
- self.class.default_no_wrap
609
- end
164
+ require 'tilt/markaby'
165
+ register MarkabyTemplate, 'mab'
610
166
 
611
- def evaluate(scope, locals, &block)
612
- @output ||= CoffeeScript.compile(data, :no_wrap => @no_wrap)
613
- end
614
- end
615
- register 'coffee', CoffeeScriptTemplate
167
+ require 'tilt/liquid'
168
+ register LiquidTemplate, 'liquid'
616
169
 
170
+ require 'tilt/radius'
171
+ register RadiusTemplate, 'radius'
617
172
 
618
- # Nokogiri template implementation. See:
619
- # http://nokogiri.org/
620
- class NokogiriTemplate < Template
621
- def initialize_engine
622
- return if defined?(::Nokogiri)
623
- require_template_library 'nokogiri'
624
- end
173
+ require 'tilt/markdown'
174
+ register MarukuTemplate, 'markdown', 'mkd', 'md'
175
+ register KramdownTemplate, 'markdown', 'mkd', 'md'
176
+ register BlueClothTemplate, 'markdown', 'mkd', 'md'
177
+ register RedcarpetTemplate, 'markdown', 'mkd', 'md'
178
+ register RDiscountTemplate, 'markdown', 'mkd', 'md'
625
179
 
626
- def prepare; end
627
-
628
- def evaluate(scope, locals, &block)
629
- block &&= proc { yield.gsub(/^<\?xml version=\"1\.0\"\?>\n?/, "") }
630
-
631
- if data.respond_to?(:to_str)
632
- super(scope, locals, &block)
633
- else
634
- ::Nokogiri::XML::Builder.new.tap(&data).to_xml
635
- end
636
- end
637
-
638
- def precompiled_preamble(locals)
639
- return super if locals.include? :xml
640
- "xml = ::Nokogiri::XML::Builder.new\n#{super}"
641
- end
642
-
643
- def precompiled_postamble(locals)
644
- "xml.to_xml"
645
- end
646
-
647
- def precompiled_template(locals)
648
- data.to_str
649
- end
650
- end
651
- register 'nokogiri', NokogiriTemplate
652
-
653
- # Builder template implementation. See:
654
- # http://builder.rubyforge.org/
655
- class BuilderTemplate < Template
656
- def initialize_engine
657
- return if defined?(::Builder)
658
- require_template_library 'builder'
659
- end
660
-
661
- def prepare; end
662
-
663
- def evaluate(scope, locals, &block)
664
- return super(scope, locals, &block) if data.respond_to?(:to_str)
665
- xml = ::Builder::XmlMarkup.new(:indent => 2)
666
- data.call(xml)
667
- xml.target!
668
- end
669
-
670
- def precompiled_preamble(locals)
671
- return super if locals.include? :xml
672
- "xml = ::Builder::XmlMarkup.new(:indent => 2)\n#{super}"
673
- end
674
-
675
- def precompiled_postamble(locals)
676
- "xml.target!"
677
- end
678
-
679
- def precompiled_template(locals)
680
- data.to_str
681
- end
682
- end
683
- register 'builder', BuilderTemplate
684
-
685
-
686
- # Liquid template implementation. See:
687
- # http://liquid.rubyforge.org/
688
- #
689
- # Liquid is designed to be a *safe* template system and threfore
690
- # does not provide direct access to execuatable scopes. In order to
691
- # support a +scope+, the +scope+ must be able to represent itself
692
- # as a hash by responding to #to_h. If the +scope+ does not respond
693
- # to #to_h it will be ignored.
694
- #
695
- # LiquidTemplate does not support yield blocks.
696
- #
697
- # It's suggested that your program require 'liquid' at load
698
- # time when using this template engine.
699
- class LiquidTemplate < Template
700
- def initialize_engine
701
- return if defined? ::Liquid::Template
702
- require_template_library 'liquid'
703
- end
704
-
705
- def prepare
706
- @engine = ::Liquid::Template.parse(data)
707
- end
708
-
709
- def evaluate(scope, locals, &block)
710
- locals = locals.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
711
- if scope.respond_to?(:to_h)
712
- scope = scope.to_h.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
713
- locals = scope.merge(locals)
714
- end
715
- locals['yield'] = block.nil? ? '' : yield
716
- locals['content'] = locals['yield']
717
- @engine.render(locals)
718
- end
719
- end
720
- register 'liquid', LiquidTemplate
180
+ require 'tilt/textile'
181
+ register RedClothTemplate, 'textile'
721
182
 
183
+ require 'tilt/rdoc'
184
+ register RDocTemplate, 'rdoc'
722
185
 
723
- # Discount Markdown implementation. See:
724
- # http://github.com/rtomayko/rdiscount
725
- #
726
- # RDiscount is a simple text filter. It does not support +scope+ or
727
- # +locals+. The +:smart+ and +:filter_html+ options may be set true
728
- # to enable those flags on the underlying RDiscount object.
729
- class RDiscountTemplate < Template
730
- def flags
731
- [:smart, :filter_html].select { |flag| options[flag] }
732
- end
733
-
734
- def initialize_engine
735
- return if defined? ::RDiscount
736
- require_template_library 'rdiscount'
737
- end
738
-
739
- def prepare
740
- @engine = RDiscount.new(data, *flags)
741
- @output = nil
742
- end
743
-
744
- def evaluate(scope, locals, &block)
745
- @output ||= @engine.to_html
746
- end
747
- end
748
- register 'markdown', RDiscountTemplate
749
- register 'mkd', RDiscountTemplate
750
- register 'md', RDiscountTemplate
751
-
752
-
753
- # BlueCloth Markdown implementation. See:
754
- # http://deveiate.org/projects/BlueCloth/
755
- #
756
- # RDiscount is a simple text filter. It does not support +scope+ or
757
- # +locals+. The +:smartypants+ and +:escape_html+ options may be set true
758
- # to enable those flags on the underlying BlueCloth object.
759
- class BlueClothTemplate < Template
760
- def initialize_engine
761
- return if defined? ::BlueCloth
762
- require_template_library 'bluecloth'
763
- end
764
-
765
- def prepare
766
- @engine = BlueCloth.new(data, options)
767
- @output = nil
768
- end
769
-
770
- def evaluate(scope, locals, &block)
771
- @output ||= @engine.to_html
772
- end
773
- end
774
-
775
-
776
- # RedCloth implementation. See:
777
- # http://redcloth.org/
778
- class RedClothTemplate < Template
779
- def initialize_engine
780
- return if defined? ::RedCloth
781
- require_template_library 'redcloth'
782
- end
783
-
784
- def prepare
785
- @engine = RedCloth.new(data)
786
- @output = nil
787
- end
788
-
789
- def evaluate(scope, locals, &block)
790
- @output ||= @engine.to_html
791
- end
792
- end
793
- register 'textile', RedClothTemplate
794
-
795
-
796
- # RDoc template. See:
797
- # http://rdoc.rubyforge.org/
798
- #
799
- # It's suggested that your program require 'rdoc/markup' and
800
- # 'rdoc/markup/to_html' at load time when using this template
801
- # engine.
802
- class RDocTemplate < Template
803
- def initialize_engine
804
- return if defined?(::RDoc::Markup)
805
- require_template_library 'rdoc/markup'
806
- require_template_library 'rdoc/markup/to_html'
807
- end
808
-
809
- def prepare
810
- markup = RDoc::Markup::ToHtml.new
811
- @engine = markup.convert(data)
812
- @output = nil
813
- end
814
-
815
- def evaluate(scope, locals, &block)
816
- @output ||= @engine.to_s
817
- end
818
- end
819
- register 'rdoc', RDocTemplate
820
-
821
-
822
- # Radius Template
823
- # http://github.com/jlong/radius/
824
- class RadiusTemplate < Template
825
- def initialize_engine
826
- return if defined? ::Radius
827
- require_template_library 'radius'
828
- end
829
-
830
- def prepare
831
- end
832
-
833
- def evaluate(scope, locals, &block)
834
- context = Class.new(Radius::Context).new
835
- context.define_tag("yield") do
836
- block.call
837
- end
838
- locals.each do |tag, value|
839
- context.define_tag(tag) do
840
- value
841
- end
842
- end
843
- (class << context; self; end).class_eval do
844
- define_method :tag_missing do |tag, attr|
845
- scope.__send__(tag) # any way to support attr as args?
846
- end
847
- end
848
- options = {:tag_prefix => 'r'}.merge(@options)
849
- parser = Radius::Parser.new(context, options)
850
- parser.parse(data)
851
- end
852
- end
853
- register 'radius', RadiusTemplate
854
-
855
-
856
- # Markaby
857
- # http://github.com/markaby/markaby
858
- class MarkabyTemplate < Template
859
- def self.builder_class
860
- @builder_class ||= Class.new(Markaby::Builder) do
861
- def __capture_markaby_tilt__(&block)
862
- __run_markaby_tilt__ do
863
- text capture(&block)
864
- end
865
- end
866
- end
867
- end
868
-
869
- def initialize_engine
870
- return if defined? ::Markaby
871
- require_template_library 'markaby'
872
- end
873
-
874
- def prepare
875
- end
876
-
877
- def evaluate(scope, locals, &block)
878
- builder = self.class.builder_class.new({}, scope)
879
- builder.locals = locals
880
-
881
- if data.kind_of? Proc
882
- (class << builder; self end).send(:define_method, :__run_markaby_tilt__, &data)
883
- else
884
- builder.instance_eval <<-CODE, __FILE__, __LINE__
885
- def __run_markaby_tilt__
886
- #{data}
887
- end
888
- CODE
889
- end
890
-
891
- if block
892
- builder.__capture_markaby_tilt__(&block)
893
- else
894
- builder.__run_markaby_tilt__
895
- end
896
-
897
- builder.to_s
898
- end
899
- end
900
- register 'mab', MarkabyTemplate
186
+ require 'tilt/creole'
187
+ register CreoleTemplate, 'creole'
901
188
  end