tilt 1.4.1 → 2.0.0.beta1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +33 -26
- data/README.md +21 -48
- data/Rakefile +21 -30
- data/{TEMPLATES.md → docs/TEMPLATES.md} +10 -4
- data/docs/common.css +14 -0
- data/lib/tilt.rb +85 -154
- data/lib/tilt/asciidoc.rb +1 -8
- data/lib/tilt/bluecloth.rb +24 -0
- data/lib/tilt/builder.rb +1 -8
- data/lib/tilt/coffee.rb +1 -8
- data/lib/tilt/creole.rb +25 -0
- data/lib/tilt/csv.rb +7 -13
- data/lib/tilt/erb.rb +2 -55
- data/lib/tilt/erubis.rb +43 -0
- data/lib/tilt/haml.rb +1 -8
- data/lib/tilt/kramdown.rb +33 -0
- data/lib/tilt/less.rb +38 -0
- data/lib/tilt/liquid.rb +1 -8
- data/lib/tilt/mapping.rb +247 -0
- data/lib/tilt/markaby.rb +1 -8
- data/lib/tilt/maruku.rb +22 -0
- data/lib/tilt/nokogiri.rb +1 -8
- data/lib/tilt/radius.rb +1 -8
- data/lib/tilt/rdiscount.rb +39 -0
- data/lib/tilt/rdoc.rb +3 -10
- data/lib/tilt/redcarpet.rb +104 -0
- data/lib/tilt/{textile.rb → redcloth.rb} +1 -8
- data/lib/tilt/sass.rb +41 -0
- data/lib/tilt/template.rb +85 -92
- data/lib/tilt/wikicloth.rb +22 -0
- data/lib/tilt/yajl.rb +1 -8
- data/test/{contest.rb → test_helper.rb} +5 -11
- data/test/tilt_asciidoctor_test.rb +6 -6
- data/test/tilt_blueclothtemplate_test.rb +3 -15
- data/test/tilt_buildertemplate_test.rb +3 -3
- data/test/tilt_cache_test.rb +2 -2
- data/test/tilt_coffeescripttemplate_test.rb +8 -18
- data/test/tilt_compilesite_test.rb +2 -2
- data/test/tilt_creoletemplate_test.rb +3 -7
- data/test/tilt_csv_test.rb +5 -9
- data/test/tilt_erbtemplate_test.rb +7 -7
- data/test/tilt_erubistemplate_test.rb +7 -7
- data/test/tilt_etannitemplate_test.rb +4 -3
- data/test/tilt_hamltemplate_test.rb +4 -4
- data/test/tilt_kramdown_test.rb +5 -27
- data/test/tilt_lesstemplate_test.rb +3 -3
- data/test/tilt_liquidtemplate_test.rb +3 -3
- data/test/tilt_mapping.rb +215 -0
- data/test/tilt_markaby_test.rb +4 -4
- data/test/tilt_markdown_test.rb +13 -14
- data/test/tilt_marukutemplate_test.rb +6 -18
- data/test/tilt_metadata_test.rb +42 -0
- data/test/tilt_nokogiritemplate_test.rb +3 -3
- data/test/tilt_radiustemplate_test.rb +3 -3
- data/test/tilt_rdiscounttemplate_test.rb +6 -18
- data/test/tilt_rdoctemplate_test.rb +3 -5
- data/test/tilt_redcarpettemplate_test.rb +11 -23
- data/test/tilt_redclothtemplate_test.rb +3 -3
- data/test/tilt_sasstemplate_test.rb +4 -4
- data/test/tilt_stringtemplate_test.rb +4 -3
- data/test/tilt_template_test.rb +35 -49
- data/test/tilt_test.rb +10 -15
- data/test/tilt_wikiclothtemplate_test.rb +3 -3
- data/test/tilt_yajltemplate_test.rb +3 -3
- data/tilt.gemspec +19 -32
- metadata +26 -386
- data/lib/tilt/css.rb +0 -80
- data/lib/tilt/markdown.rb +0 -214
- data/lib/tilt/wiki.rb +0 -58
- data/test/tilt_fallback_test.rb +0 -122
data/lib/tilt/asciidoc.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tilt/template'
|
2
|
+
require 'asciidoctor'
|
2
3
|
|
3
4
|
# AsciiDoc see: http://asciidoc.org/
|
4
5
|
module Tilt
|
@@ -11,14 +12,6 @@ module Tilt
|
|
11
12
|
class AsciidoctorTemplate < Template
|
12
13
|
self.default_mime_type = 'text/html'
|
13
14
|
|
14
|
-
def self.engine_initialized?
|
15
|
-
defined? ::Asciidoctor::Document
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize_engine
|
19
|
-
require_template_library 'asciidoctor'
|
20
|
-
end
|
21
|
-
|
22
15
|
def prepare
|
23
16
|
options[:header_footer] = false if options[:header_footer].nil?
|
24
17
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'tilt/template'
|
2
|
+
require 'bluecloth'
|
3
|
+
|
4
|
+
module Tilt
|
5
|
+
# BlueCloth Markdown implementation. See:
|
6
|
+
# http://deveiate.org/projects/BlueCloth/
|
7
|
+
class BlueClothTemplate < Template
|
8
|
+
self.default_mime_type = 'text/html'
|
9
|
+
|
10
|
+
def prepare
|
11
|
+
@engine = BlueCloth.new(data, options)
|
12
|
+
@output = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate(scope, locals, &block)
|
16
|
+
@output ||= @engine.to_html
|
17
|
+
end
|
18
|
+
|
19
|
+
def allows_script?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/lib/tilt/builder.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tilt/template'
|
2
|
+
require 'builder'
|
2
3
|
|
3
4
|
module Tilt
|
4
5
|
# Builder template implementation. See:
|
@@ -6,14 +7,6 @@ module Tilt
|
|
6
7
|
class BuilderTemplate < Template
|
7
8
|
self.default_mime_type = 'text/xml'
|
8
9
|
|
9
|
-
def self.engine_initialized?
|
10
|
-
defined? ::Builder
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize_engine
|
14
|
-
require_template_library 'builder'
|
15
|
-
end
|
16
|
-
|
17
10
|
def prepare; end
|
18
11
|
|
19
12
|
def evaluate(scope, locals, &block)
|
data/lib/tilt/coffee.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tilt/template'
|
2
|
+
require 'coffee_script'
|
2
3
|
|
3
4
|
module Tilt
|
4
5
|
# CoffeeScript template implementation. See:
|
@@ -28,14 +29,6 @@ module Tilt
|
|
28
29
|
@@default_bare = value
|
29
30
|
end
|
30
31
|
|
31
|
-
def self.engine_initialized?
|
32
|
-
defined? ::CoffeeScript
|
33
|
-
end
|
34
|
-
|
35
|
-
def initialize_engine
|
36
|
-
require_template_library 'coffee_script'
|
37
|
-
end
|
38
|
-
|
39
32
|
def prepare
|
40
33
|
if !options.key?(:bare) and !options.key?(:no_wrap)
|
41
34
|
options[:bare] = self.class.default_bare
|
data/lib/tilt/creole.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'tilt/template'
|
2
|
+
require 'creole'
|
3
|
+
|
4
|
+
module Tilt
|
5
|
+
# Creole implementation. See:
|
6
|
+
# http://www.wikicreole.org/
|
7
|
+
class CreoleTemplate < Template
|
8
|
+
def prepare
|
9
|
+
opts = {}
|
10
|
+
[:allowed_schemes, :extensions, :no_escape].each do |k|
|
11
|
+
opts[k] = options[k] if options[k]
|
12
|
+
end
|
13
|
+
@engine = Creole::Parser.new(data, opts)
|
14
|
+
@output = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def evaluate(scope, locals, &block)
|
18
|
+
@output ||= @engine.to_html
|
19
|
+
end
|
20
|
+
|
21
|
+
def allows_script?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/tilt/csv.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
require 'tilt/template'
|
2
2
|
|
3
|
+
if RUBY_VERSION >= '1.9.0'
|
4
|
+
require 'csv'
|
5
|
+
else
|
6
|
+
require 'fastercsv'
|
7
|
+
end
|
8
|
+
|
3
9
|
module Tilt
|
4
10
|
|
5
11
|
# CSV Template implementation. See:
|
@@ -30,10 +36,6 @@ module Tilt
|
|
30
36
|
class CSVTemplate < Template
|
31
37
|
self.default_mime_type = 'text/csv'
|
32
38
|
|
33
|
-
def self.engine_initialized?
|
34
|
-
engine
|
35
|
-
end
|
36
|
-
|
37
39
|
def self.engine
|
38
40
|
if RUBY_VERSION >= '1.9.0' && defined? ::CSV
|
39
41
|
::CSV
|
@@ -42,14 +44,6 @@ module Tilt
|
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
45
|
-
def initialize_engine
|
46
|
-
if RUBY_VERSION >= '1.9.0'
|
47
|
-
require_template_library 'csv'
|
48
|
-
else
|
49
|
-
require_template_library 'fastercsv'
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
47
|
def prepare
|
54
48
|
@code =<<-RUBY
|
55
49
|
#{self.class.engine}.generate do |csv|
|
@@ -68,4 +62,4 @@ module Tilt
|
|
68
62
|
end
|
69
63
|
|
70
64
|
end
|
71
|
-
end
|
65
|
+
end
|
data/lib/tilt/erb.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tilt/template'
|
2
|
+
require 'erb'
|
2
3
|
|
3
4
|
module Tilt
|
4
5
|
# ERB template implementation. See:
|
@@ -11,17 +12,10 @@ module Tilt
|
|
11
12
|
end
|
12
13
|
|
13
14
|
def self.default_output_variable=(name)
|
15
|
+
warn "#{self}.default_output_variable= has been replaced with the :outvar-option"
|
14
16
|
@@default_output_variable = name
|
15
17
|
end
|
16
18
|
|
17
|
-
def self.engine_initialized?
|
18
|
-
defined? ::ERB
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize_engine
|
22
|
-
require_template_library 'erb'
|
23
|
-
end
|
24
|
-
|
25
19
|
def prepare
|
26
20
|
@outvar = options[:outvar] || self.class.default_output_variable
|
27
21
|
options[:trim] = '<>' if !(options[:trim] == false) && (options[:trim].nil? || options[:trim] == true)
|
@@ -59,52 +53,5 @@ module Tilt
|
|
59
53
|
end
|
60
54
|
end
|
61
55
|
end
|
62
|
-
|
63
|
-
# Erubis template implementation. See:
|
64
|
-
# http://www.kuwata-lab.com/erubis/
|
65
|
-
#
|
66
|
-
# ErubisTemplate supports the following additional options, which are not
|
67
|
-
# passed down to the Erubis engine:
|
68
|
-
#
|
69
|
-
# :engine_class allows you to specify a custom engine class to use
|
70
|
-
# instead of the default (which is ::Erubis::Eruby).
|
71
|
-
#
|
72
|
-
# :escape_html when true, ::Erubis::EscapedEruby will be used as
|
73
|
-
# the engine class instead of the default. All content
|
74
|
-
# within <%= %> blocks will be automatically html escaped.
|
75
|
-
class ErubisTemplate < ERBTemplate
|
76
|
-
def self.engine_initialized?
|
77
|
-
defined? ::Erubis::Eruby
|
78
|
-
end
|
79
|
-
|
80
|
-
def initialize_engine
|
81
|
-
require_template_library 'erubis'
|
82
|
-
end
|
83
|
-
|
84
|
-
def prepare
|
85
|
-
@outvar = options.delete(:outvar) || self.class.default_output_variable
|
86
|
-
@options.merge!(:preamble => false, :postamble => false, :bufvar => @outvar)
|
87
|
-
engine_class = options.delete(:engine_class)
|
88
|
-
engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
|
89
|
-
@engine = (engine_class || ::Erubis::Eruby).new(data, options)
|
90
|
-
end
|
91
|
-
|
92
|
-
def precompiled_preamble(locals)
|
93
|
-
[super, "#{@outvar} = _buf = ''"].join("\n")
|
94
|
-
end
|
95
|
-
|
96
|
-
def precompiled_postamble(locals)
|
97
|
-
[@outvar, super].join("\n")
|
98
|
-
end
|
99
|
-
|
100
|
-
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
|
101
|
-
# Override and adjust back.
|
102
|
-
if RUBY_VERSION >= '1.9.0'
|
103
|
-
def precompiled(locals)
|
104
|
-
source, offset = super
|
105
|
-
[source, offset - 1]
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
56
|
end
|
110
57
|
|
data/lib/tilt/erubis.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'tilt/erb'
|
2
|
+
require 'erubis'
|
3
|
+
|
4
|
+
module Tilt
|
5
|
+
# Erubis template implementation. See:
|
6
|
+
# http://www.kuwata-lab.com/erubis/
|
7
|
+
#
|
8
|
+
# ErubisTemplate supports the following additional options, which are not
|
9
|
+
# passed down to the Erubis engine:
|
10
|
+
#
|
11
|
+
# :engine_class allows you to specify a custom engine class to use
|
12
|
+
# instead of the default (which is ::Erubis::Eruby).
|
13
|
+
#
|
14
|
+
# :escape_html when true, ::Erubis::EscapedEruby will be used as
|
15
|
+
# the engine class instead of the default. All content
|
16
|
+
# within <%= %> blocks will be automatically html escaped.
|
17
|
+
class ErubisTemplate < ERBTemplate
|
18
|
+
def prepare
|
19
|
+
@outvar = options.delete(:outvar) || self.class.default_output_variable
|
20
|
+
@options.merge!(:preamble => false, :postamble => false, :bufvar => @outvar)
|
21
|
+
engine_class = options.delete(:engine_class)
|
22
|
+
engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
|
23
|
+
@engine = (engine_class || ::Erubis::Eruby).new(data, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def precompiled_preamble(locals)
|
27
|
+
[super, "#{@outvar} = _buf = ''"].join("\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
def precompiled_postamble(locals)
|
31
|
+
[@outvar, super].join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
|
35
|
+
# Override and adjust back.
|
36
|
+
if RUBY_VERSION >= '1.9.0'
|
37
|
+
def precompiled(locals)
|
38
|
+
source, offset = super
|
39
|
+
[source, offset - 1]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/tilt/haml.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tilt/template'
|
2
|
+
require 'haml'
|
2
3
|
|
3
4
|
module Tilt
|
4
5
|
# Haml template implementation. See:
|
@@ -6,14 +7,6 @@ module Tilt
|
|
6
7
|
class HamlTemplate < Template
|
7
8
|
self.default_mime_type = 'text/html'
|
8
9
|
|
9
|
-
def self.engine_initialized?
|
10
|
-
defined? ::Haml::Engine
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize_engine
|
14
|
-
require_template_library 'haml'
|
15
|
-
end
|
16
|
-
|
17
10
|
def prepare
|
18
11
|
options = @options.merge(:filename => eval_file, :line => line)
|
19
12
|
@engine = ::Haml::Engine.new(data, options)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'tilt/template'
|
2
|
+
require 'kramdown'
|
3
|
+
|
4
|
+
module Tilt
|
5
|
+
# Kramdown Markdown implementation. See:
|
6
|
+
# http://kramdown.rubyforge.org/
|
7
|
+
class KramdownTemplate < Template
|
8
|
+
DUMB_QUOTES = [39, 39, 34, 34]
|
9
|
+
|
10
|
+
def self.engine_initialized?
|
11
|
+
defined? ::Kramdown
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize_engine
|
15
|
+
require_template_library 'kramdown'
|
16
|
+
end
|
17
|
+
|
18
|
+
def prepare
|
19
|
+
options[:smart_quotes] = DUMB_QUOTES unless options[:smartypants]
|
20
|
+
@engine = Kramdown::Document.new(data, options)
|
21
|
+
@output = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def evaluate(scope, locals, &block)
|
25
|
+
@output ||= @engine.to_html
|
26
|
+
end
|
27
|
+
|
28
|
+
def allows_script?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
data/lib/tilt/less.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'tilt/template'
|
2
|
+
require 'less'
|
3
|
+
|
4
|
+
module Tilt
|
5
|
+
# Lessscss template implementation. See:
|
6
|
+
# http://lesscss.org/
|
7
|
+
#
|
8
|
+
# Less templates do not support object scopes, locals, or yield.
|
9
|
+
class LessTemplate < Template
|
10
|
+
self.default_mime_type = 'text/css'
|
11
|
+
|
12
|
+
def self.engine_initialized?
|
13
|
+
defined? ::Less
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize_engine
|
17
|
+
require_template_library 'less'
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare
|
21
|
+
if ::Less.const_defined? :Engine
|
22
|
+
@engine = ::Less::Engine.new(data)
|
23
|
+
else
|
24
|
+
parser = ::Less::Parser.new(options.merge :filename => eval_file, :line => line)
|
25
|
+
@engine = parser.parse(data)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def evaluate(scope, locals, &block)
|
30
|
+
@output ||= @engine.to_css(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def allows_script?
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
data/lib/tilt/liquid.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tilt/template'
|
2
|
+
require 'liquid'
|
2
3
|
|
3
4
|
module Tilt
|
4
5
|
# Liquid template implementation. See:
|
@@ -15,14 +16,6 @@ module Tilt
|
|
15
16
|
# It's suggested that your program require 'liquid' at load
|
16
17
|
# time when using this template engine.
|
17
18
|
class LiquidTemplate < Template
|
18
|
-
def self.engine_initialized?
|
19
|
-
defined? ::Liquid::Template
|
20
|
-
end
|
21
|
-
|
22
|
-
def initialize_engine
|
23
|
-
require_template_library 'liquid'
|
24
|
-
end
|
25
|
-
|
26
19
|
def prepare
|
27
20
|
@engine = ::Liquid::Template.parse(data)
|
28
21
|
end
|
data/lib/tilt/mapping.rb
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
module Tilt
|
2
|
+
# Tilt::Mapping associates file extensions with template implementations.
|
3
|
+
#
|
4
|
+
# mapping = Tilt::Mapping.new
|
5
|
+
# mapping.register(Tilt::RDocTemplate, 'rdoc')
|
6
|
+
# mapping['index.rdoc'] # => Tilt::RDocTemplate
|
7
|
+
# mapping.new('index.rdoc').render
|
8
|
+
#
|
9
|
+
# You can use {#register} to register a template class by file
|
10
|
+
# extension, {#registered?} to see if a file extension is mapped,
|
11
|
+
# {#[]} to lookup template classes, and {#new} to instantiate template
|
12
|
+
# objects.
|
13
|
+
#
|
14
|
+
# Mapping also supports *lazy* template implementations. Note that regularly
|
15
|
+
# registered template implementations *always* have preference over lazily
|
16
|
+
# registered template implementations. You should use {#register} if you
|
17
|
+
# depend on a specific template implementation and {#register_lazy} if there
|
18
|
+
# are multiple alternatives.
|
19
|
+
#
|
20
|
+
# mapping = Tilt::Mapping.new
|
21
|
+
# mapping.register_lazy('RDiscount::Template', 'rdiscount/template', 'md')
|
22
|
+
# mapping['index.md']
|
23
|
+
# # => RDiscount::Template
|
24
|
+
#
|
25
|
+
# {#register_lazy} takes a class name, a filename, and a list of file
|
26
|
+
# extensions. When you try to lookup a template name that matches the
|
27
|
+
# file extension, Tilt will automatically try to require the filename and
|
28
|
+
# constantize the class name.
|
29
|
+
#
|
30
|
+
# Unlike {#register}, there can be multiple template implementations
|
31
|
+
# registered lazily to the same file extension. Tilt will attempt to load the
|
32
|
+
# template implementations in order (registered *last* would be tried first),
|
33
|
+
# returning the first which doesn't raise LoadError.
|
34
|
+
#
|
35
|
+
# If all of the registered template implementations fails, Tilt will raise
|
36
|
+
# the exception of the first, since that was the most preferred one.
|
37
|
+
#
|
38
|
+
# mapping = Tilt::Mapping.new
|
39
|
+
# mapping.register_lazy('Bluecloth::Template', 'bluecloth/template', 'md')
|
40
|
+
# mapping.register_lazy('RDiscount::Template', 'rdiscount/template', 'md')
|
41
|
+
# mapping['index.md']
|
42
|
+
# # => RDiscount::Template
|
43
|
+
#
|
44
|
+
# In the previous example we say that RDiscount has a *higher priority* than
|
45
|
+
# BlueCloth. Tilt will first try to `require "rdiscount/template"`, falling
|
46
|
+
# back to `require "bluecloth/template"`. If none of these are successful,
|
47
|
+
# the first error will be raised.
|
48
|
+
class Mapping
|
49
|
+
# @private
|
50
|
+
attr_reader :lazy_map, :template_map
|
51
|
+
|
52
|
+
def initialize
|
53
|
+
@template_map = Hash.new
|
54
|
+
@lazy_map = Hash.new { |h, k| h[k] = [] }
|
55
|
+
end
|
56
|
+
|
57
|
+
# @private
|
58
|
+
def initialize_copy(other)
|
59
|
+
@template_map = other.template_map.dup
|
60
|
+
@lazy_map = other.lazy_map.dup
|
61
|
+
end
|
62
|
+
|
63
|
+
# Registrers a lazy template implementation by file extension. You
|
64
|
+
# can have multiple lazy template implementations defined on the
|
65
|
+
# same file extension, in which case the template implementation
|
66
|
+
# defined *last* will be attemted loaded *first*.
|
67
|
+
#
|
68
|
+
# @param class_name [String] Class name of a template class.
|
69
|
+
# @param file [String] Filename where the template class is defined.
|
70
|
+
# @param extensions [Array<String>] List of extensions.
|
71
|
+
# @return [void]
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# mapping.register_lazy 'MyEngine::Template', 'my_engine/template', 'mt'
|
75
|
+
#
|
76
|
+
# defined?(MyEngine::Template) # => false
|
77
|
+
# mapping['index.mt'] # => MyEngine::Template
|
78
|
+
# defined?(MyEngine::Template) # => true
|
79
|
+
def register_lazy(class_name, file, *extensions)
|
80
|
+
# Internal API
|
81
|
+
if class_name.is_a?(Symbol)
|
82
|
+
Tilt.autoload class_name, file
|
83
|
+
class_name = "Tilt::#{class_name}"
|
84
|
+
end
|
85
|
+
|
86
|
+
extensions.each do |ext|
|
87
|
+
@lazy_map[ext].unshift([class_name, file])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Registers a template implementation by file extension. There can only be
|
92
|
+
# one template implementation per file extension, and this method will
|
93
|
+
# override any existing mapping.
|
94
|
+
#
|
95
|
+
# @param template_class
|
96
|
+
# @param extensions [Array<String>] List of extensions.
|
97
|
+
# @return [void]
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# mapping.register MyEngine::Template, 'mt'
|
101
|
+
# mapping['index.mt'] # => MyEngine::Template
|
102
|
+
def register(template_class, *extensions)
|
103
|
+
extensions.each do |ext|
|
104
|
+
@template_map[ext] = template_class
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Checks if a file extension is registered (either eagerly or
|
109
|
+
# lazily) in this mapping.
|
110
|
+
#
|
111
|
+
# @param ext [String] File extension.
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# mapping.registered?('erb') # => true
|
115
|
+
# mapping.registered?('nope') # => false
|
116
|
+
def registered?(ext)
|
117
|
+
@template_map.has_key?(ext.downcase) or lazy?(ext)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Instantiates a new template class based on the file.
|
121
|
+
#
|
122
|
+
# @raise [RuntimeError] if there is no template class registered for the
|
123
|
+
# file name.
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# mapping.new('index.mt') # => instance of MyEngine::Template
|
127
|
+
#
|
128
|
+
# @see Tilt::Template.new
|
129
|
+
def new(file, line=nil, options={}, &block)
|
130
|
+
if template_class = self[file]
|
131
|
+
template_class.new(file, line, options, &block)
|
132
|
+
else
|
133
|
+
fail "No template engine registered for #{File.basename(file)}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Looks up a template class based on file name and/or extension.
|
138
|
+
#
|
139
|
+
# @example
|
140
|
+
# mapping['views/hello.erb'] # => Tilt::ERBTemplate
|
141
|
+
# mapping['hello.erb'] # => Tilt::ERBTemplate
|
142
|
+
# mapping['erb'] # => Tilt::ERBTemplate
|
143
|
+
#
|
144
|
+
# @return [template class]
|
145
|
+
def [](file)
|
146
|
+
prefix, ext = split(file)
|
147
|
+
ext && lookup(ext)
|
148
|
+
end
|
149
|
+
|
150
|
+
alias template_for []
|
151
|
+
|
152
|
+
# Looks up a list of template classes based on file name. If the file name
|
153
|
+
# has multiple extensions, it will return all template classes matching the
|
154
|
+
# extensions from the end.
|
155
|
+
#
|
156
|
+
# @example
|
157
|
+
# mapping.templates_for('views/index.haml.erb')
|
158
|
+
# # => [Tilt::ERBTemplate, Tilt::HamlTemplate]
|
159
|
+
#
|
160
|
+
# @return [Array<template class>]
|
161
|
+
def templates_for(file)
|
162
|
+
templates = []
|
163
|
+
|
164
|
+
while true
|
165
|
+
prefix, ext = split(file)
|
166
|
+
break unless ext
|
167
|
+
templates << lookup(ext)
|
168
|
+
file = prefix
|
169
|
+
end
|
170
|
+
|
171
|
+
templates
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def lazy?(ext)
|
177
|
+
ext = ext.downcase
|
178
|
+
@lazy_map.has_key?(ext) && !@lazy_map[ext].empty?
|
179
|
+
end
|
180
|
+
|
181
|
+
def split(file)
|
182
|
+
pattern = file.to_s.downcase
|
183
|
+
full_pattern = pattern.dup
|
184
|
+
|
185
|
+
until registered?(pattern)
|
186
|
+
return if pattern.empty?
|
187
|
+
pattern = File.basename(pattern)
|
188
|
+
pattern.sub!(/^[^.]*\.?/, '')
|
189
|
+
end
|
190
|
+
|
191
|
+
prefix_size = full_pattern.size - pattern.size
|
192
|
+
[full_pattern[0,prefix_size-1], pattern]
|
193
|
+
end
|
194
|
+
|
195
|
+
def lookup(ext)
|
196
|
+
@template_map[ext] || lazy_load(ext)
|
197
|
+
end
|
198
|
+
|
199
|
+
def lazy_load(pattern)
|
200
|
+
return unless @lazy_map.has_key?(pattern)
|
201
|
+
|
202
|
+
choices = @lazy_map[pattern]
|
203
|
+
|
204
|
+
# Check if a template class is already present
|
205
|
+
choices.each do |class_name, file|
|
206
|
+
template_class = constant_defined?(class_name)
|
207
|
+
if template_class
|
208
|
+
register(template_class, pattern)
|
209
|
+
return template_class
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
first_failure = nil
|
214
|
+
|
215
|
+
# Load in order
|
216
|
+
choices.each do |class_name, file|
|
217
|
+
begin
|
218
|
+
require file
|
219
|
+
|
220
|
+
if Thread.list.size > 1
|
221
|
+
warn "WARN: tilt autoloading '#{file}' in a non thread-safe way; " +
|
222
|
+
"explicit require '#{file}' suggested."
|
223
|
+
end
|
224
|
+
|
225
|
+
# It's safe to eval() here because constant_defined? will
|
226
|
+
# raise NameError on invalid constant names
|
227
|
+
template_class = eval(class_name)
|
228
|
+
rescue LoadError => ex
|
229
|
+
first_failure ||= ex
|
230
|
+
else
|
231
|
+
register(template_class, pattern)
|
232
|
+
return template_class
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
raise first_failure if first_failure
|
237
|
+
end
|
238
|
+
|
239
|
+
def constant_defined?(name)
|
240
|
+
name.split('::').inject(Object) do |scope, n|
|
241
|
+
return false unless scope.const_defined?(n)
|
242
|
+
return false if scope.autoload?(n) # skip autload
|
243
|
+
scope.const_get(n)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|