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.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +33 -26
  4. data/README.md +21 -48
  5. data/Rakefile +21 -30
  6. data/{TEMPLATES.md → docs/TEMPLATES.md} +10 -4
  7. data/docs/common.css +14 -0
  8. data/lib/tilt.rb +85 -154
  9. data/lib/tilt/asciidoc.rb +1 -8
  10. data/lib/tilt/bluecloth.rb +24 -0
  11. data/lib/tilt/builder.rb +1 -8
  12. data/lib/tilt/coffee.rb +1 -8
  13. data/lib/tilt/creole.rb +25 -0
  14. data/lib/tilt/csv.rb +7 -13
  15. data/lib/tilt/erb.rb +2 -55
  16. data/lib/tilt/erubis.rb +43 -0
  17. data/lib/tilt/haml.rb +1 -8
  18. data/lib/tilt/kramdown.rb +33 -0
  19. data/lib/tilt/less.rb +38 -0
  20. data/lib/tilt/liquid.rb +1 -8
  21. data/lib/tilt/mapping.rb +247 -0
  22. data/lib/tilt/markaby.rb +1 -8
  23. data/lib/tilt/maruku.rb +22 -0
  24. data/lib/tilt/nokogiri.rb +1 -8
  25. data/lib/tilt/radius.rb +1 -8
  26. data/lib/tilt/rdiscount.rb +39 -0
  27. data/lib/tilt/rdoc.rb +3 -10
  28. data/lib/tilt/redcarpet.rb +104 -0
  29. data/lib/tilt/{textile.rb → redcloth.rb} +1 -8
  30. data/lib/tilt/sass.rb +41 -0
  31. data/lib/tilt/template.rb +85 -92
  32. data/lib/tilt/wikicloth.rb +22 -0
  33. data/lib/tilt/yajl.rb +1 -8
  34. data/test/{contest.rb → test_helper.rb} +5 -11
  35. data/test/tilt_asciidoctor_test.rb +6 -6
  36. data/test/tilt_blueclothtemplate_test.rb +3 -15
  37. data/test/tilt_buildertemplate_test.rb +3 -3
  38. data/test/tilt_cache_test.rb +2 -2
  39. data/test/tilt_coffeescripttemplate_test.rb +8 -18
  40. data/test/tilt_compilesite_test.rb +2 -2
  41. data/test/tilt_creoletemplate_test.rb +3 -7
  42. data/test/tilt_csv_test.rb +5 -9
  43. data/test/tilt_erbtemplate_test.rb +7 -7
  44. data/test/tilt_erubistemplate_test.rb +7 -7
  45. data/test/tilt_etannitemplate_test.rb +4 -3
  46. data/test/tilt_hamltemplate_test.rb +4 -4
  47. data/test/tilt_kramdown_test.rb +5 -27
  48. data/test/tilt_lesstemplate_test.rb +3 -3
  49. data/test/tilt_liquidtemplate_test.rb +3 -3
  50. data/test/tilt_mapping.rb +215 -0
  51. data/test/tilt_markaby_test.rb +4 -4
  52. data/test/tilt_markdown_test.rb +13 -14
  53. data/test/tilt_marukutemplate_test.rb +6 -18
  54. data/test/tilt_metadata_test.rb +42 -0
  55. data/test/tilt_nokogiritemplate_test.rb +3 -3
  56. data/test/tilt_radiustemplate_test.rb +3 -3
  57. data/test/tilt_rdiscounttemplate_test.rb +6 -18
  58. data/test/tilt_rdoctemplate_test.rb +3 -5
  59. data/test/tilt_redcarpettemplate_test.rb +11 -23
  60. data/test/tilt_redclothtemplate_test.rb +3 -3
  61. data/test/tilt_sasstemplate_test.rb +4 -4
  62. data/test/tilt_stringtemplate_test.rb +4 -3
  63. data/test/tilt_template_test.rb +35 -49
  64. data/test/tilt_test.rb +10 -15
  65. data/test/tilt_wikiclothtemplate_test.rb +3 -3
  66. data/test/tilt_yajltemplate_test.rb +3 -3
  67. data/tilt.gemspec +19 -32
  68. metadata +26 -386
  69. data/lib/tilt/css.rb +0 -80
  70. data/lib/tilt/markdown.rb +0 -214
  71. data/lib/tilt/wiki.rb +0 -58
  72. data/test/tilt_fallback_test.rb +0 -122
@@ -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
+
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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
 
@@ -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
@@ -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
+
@@ -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
+
@@ -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
@@ -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