tilt 1.4.1 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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