tilt 1.2.2 → 1.3

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.
@@ -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