tilt 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/COPYING +1 -0
  3. data/bin/tilt +2 -120
  4. data/lib/tilt/_emacs_org.rb +2 -0
  5. data/lib/tilt/_handlebars.rb +2 -0
  6. data/lib/tilt/_jbuilder.rb +2 -0
  7. data/lib/tilt/_org.rb +2 -0
  8. data/lib/tilt/asciidoc.rb +11 -23
  9. data/lib/tilt/babel.rb +5 -13
  10. data/lib/tilt/builder.rb +18 -13
  11. data/lib/tilt/cli.rb +134 -0
  12. data/lib/tilt/coffee.rb +12 -31
  13. data/lib/tilt/commonmarker.rb +47 -81
  14. data/lib/tilt/creole.rb +9 -20
  15. data/lib/tilt/csv.rb +5 -4
  16. data/lib/tilt/erb.rb +9 -17
  17. data/lib/tilt/erubi.rb +7 -6
  18. data/lib/tilt/erubis.rb +9 -6
  19. data/lib/tilt/etanni.rb +3 -2
  20. data/lib/tilt/haml.rb +12 -9
  21. data/lib/tilt/kramdown.rb +8 -20
  22. data/lib/tilt/liquid.rb +10 -14
  23. data/lib/tilt/livescript.rb +8 -20
  24. data/lib/tilt/mapping.rb +180 -104
  25. data/lib/tilt/markaby.rb +5 -7
  26. data/lib/tilt/maruku.rb +5 -19
  27. data/lib/tilt/nokogiri.rb +11 -10
  28. data/lib/tilt/pandoc.rb +33 -51
  29. data/lib/tilt/pipeline.rb +1 -0
  30. data/lib/tilt/plain.rb +4 -15
  31. data/lib/tilt/prawn.rb +10 -25
  32. data/lib/tilt/radius.rb +15 -22
  33. data/lib/tilt/rdiscount.rb +17 -33
  34. data/lib/tilt/rdoc.rb +14 -35
  35. data/lib/tilt/redcarpet.rb +20 -75
  36. data/lib/tilt/redcloth.rb +9 -19
  37. data/lib/tilt/rst-pandoc.rb +6 -19
  38. data/lib/tilt/sass.rb +34 -43
  39. data/lib/tilt/slim.rb +5 -0
  40. data/lib/tilt/string.rb +9 -3
  41. data/lib/tilt/template.rb +151 -61
  42. data/lib/tilt/typescript.rb +11 -18
  43. data/lib/tilt/wikicloth.rb +7 -19
  44. data/lib/tilt/yajl.rb +5 -11
  45. data/lib/tilt.rb +56 -40
  46. metadata +10 -7
  47. data/lib/tilt/bluecloth.rb +0 -26
  48. data/lib/tilt/less.rb +0 -32
  49. data/lib/tilt/sigil.rb +0 -36
data/lib/tilt/erb.rb CHANGED
@@ -1,38 +1,30 @@
1
- require 'tilt/template'
1
+ # frozen_string_literal: true
2
+ require_relative 'template'
2
3
  require 'erb'
3
4
 
4
5
  module Tilt
5
6
  # ERB template implementation. See:
6
7
  # http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
7
8
  class ERBTemplate < Template
8
- @@default_output_variable = '_erbout'
9
-
10
9
  SUPPORTS_KVARGS = ::ERB.instance_method(:initialize).parameters.assoc(:key) rescue false
11
10
 
12
- def self.default_output_variable
13
- @@default_output_variable
14
- end
15
-
16
- def self.default_output_variable=(name)
17
- warn "#{self}.default_output_variable= has been replaced with the :outvar-option"
18
- @@default_output_variable = name
19
- end
20
-
21
11
  def prepare
22
12
  @freeze_string_literals = !!@options[:freeze]
23
- @outvar = options[:outvar] || self.class.default_output_variable
24
- trim = case options[:trim]
13
+ @outvar = @options[:outvar] || '_erbout'
14
+ trim = case @options[:trim]
25
15
  when false
26
16
  nil
27
17
  when nil, true
28
18
  '<>'
29
19
  else
30
- options[:trim]
20
+ @options[:trim]
31
21
  end
32
22
  @engine = if SUPPORTS_KVARGS
33
- ::ERB.new(data, trim_mode: trim, eoutvar: @outvar)
23
+ ::ERB.new(@data, trim_mode: trim, eoutvar: @outvar)
24
+ # :nocov:
34
25
  else
35
- ::ERB.new(data, options[:safe], trim, @outvar)
26
+ ::ERB.new(@data, options[:safe], trim, @outvar)
27
+ # :nocov:
36
28
  end
37
29
  end
38
30
 
data/lib/tilt/erubi.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'tilt/template'
1
+ # frozen_string_literal: true
2
+ require_relative 'template'
2
3
  require 'erubi'
3
4
 
4
5
  module Tilt
@@ -12,7 +13,9 @@ module Tilt
12
13
  # instead of the default (which is ::Erubi::Engine).
13
14
  class ErubiTemplate < Template
14
15
  def prepare
15
- @options.merge!(:preamble => false, :postamble => false, :ensure=>true)
16
+ @options[:preamble] = false
17
+ @options[:postamble] = false
18
+ @options[:ensure] = true
16
19
 
17
20
  engine_class = @options[:engine_class] || Erubi::Engine
18
21
 
@@ -34,11 +37,9 @@ module Tilt
34
37
  @options[:freeze_template_literals] = false
35
38
  end
36
39
 
37
- @engine = engine_class.new(data, @options)
40
+ @engine = engine_class.new(@data, @options)
38
41
  @outvar = @engine.bufvar
39
-
40
- # Remove dup after tilt supports frozen source.
41
- @src = @engine.src.dup
42
+ @src = @engine.src
42
43
 
43
44
  @engine
44
45
  end
data/lib/tilt/erubis.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'tilt/erb'
1
+ # frozen_string_literal: true
2
+ require_relative 'erb'
2
3
  require 'erubis'
3
4
 
4
5
  module Tilt
@@ -17,11 +18,13 @@ module Tilt
17
18
  class ErubisTemplate < ERBTemplate
18
19
  def prepare
19
20
  @freeze_string_literals = !!@options.delete(:freeze)
20
- @outvar = options.delete(:outvar) || self.class.default_output_variable
21
- @options.merge!(:preamble => false, :postamble => false, :bufvar => @outvar)
22
- engine_class = options.delete(:engine_class)
23
- engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
24
- @engine = (engine_class || ::Erubis::Eruby).new(data, options)
21
+ @outvar = @options.delete(:outvar) || '_erbout'
22
+ @options[:preamble] = false
23
+ @options[:postamble] = false
24
+ @options[:bufvar] = @outvar
25
+ engine_class = @options.delete(:engine_class)
26
+ engine_class = ::Erubis::EscapedEruby if @options.delete(:escape_html)
27
+ @engine = (engine_class || ::Erubis::Eruby).new(@data, @options)
25
28
  end
26
29
 
27
30
  def precompiled_preamble(locals)
data/lib/tilt/etanni.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'tilt/template'
1
+ # frozen_string_literal: true
2
+ require_relative 'template'
2
3
 
3
4
  module Tilt
4
5
  class EtanniTemplate < Template
@@ -9,7 +10,7 @@ module Tilt
9
10
  stop = "\n#{separator}\n"
10
11
  replacement = "#{stop}\\1#{start}"
11
12
 
12
- temp = data.strip
13
+ temp = @data.strip
13
14
  temp.gsub!(/<\?r\s+(.*?)\s+\?>/m, replacement)
14
15
 
15
16
  @code = "_out_ = [<<#{separator}.chomp!]\n#{temp}#{stop}_out_.join"
data/lib/tilt/haml.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'tilt/template'
1
+ # frozen_string_literal: true
2
+ require_relative 'template'
2
3
  require 'haml'
3
4
 
4
5
  module Tilt
@@ -14,13 +15,14 @@ module Tilt
14
15
  # `Gem::Version.correct?` may return false because of Haml::VERSION #=> "3.1.8 (Separated Sally)". After Haml 4, it's always correct.
15
16
  if Gem::Version.correct?(Haml::VERSION) && Gem::Version.new(Haml::VERSION) >= Gem::Version.new('5.0.0.beta.2')
16
17
  def prepare
17
- options = {}.update(@options).update(filename: eval_file, line: line)
18
- if options.include?(:outvar)
19
- options[:buffer] = options.delete(:outvar)
20
- options[:save_buffer] = true
18
+ @options[:filename] = eval_file
19
+ @options[:line] = @line
20
+ if @options.include?(:outvar)
21
+ @options[:buffer] = @options.delete(:outvar)
22
+ @options[:save_buffer] = true
21
23
  end
22
- @engine = ::Haml::TempleEngine.new(options)
23
- @engine.compile(data)
24
+ @engine = ::Haml::TempleEngine.new(@options)
25
+ @engine.compile(@data)
24
26
  end
25
27
 
26
28
  def evaluate(scope, locals, &block)
@@ -39,8 +41,9 @@ module Tilt
39
41
  end
40
42
  else # Following definitions are for Haml <= 4 and deprecated.
41
43
  def prepare
42
- options = @options.merge(:filename => eval_file, :line => line)
43
- @engine = ::Haml::Engine.new(data, options)
44
+ @options[:filename] = eval_file
45
+ @options[:line] = @line
46
+ @engine = ::Haml::Engine.new(@data, @options)
44
47
  end
45
48
 
46
49
  def evaluate(scope, locals, &block)
data/lib/tilt/kramdown.rb CHANGED
@@ -1,25 +1,13 @@
1
- require 'tilt/template'
1
+ # frozen_string_literal: true
2
+ require_relative 'template'
2
3
  require 'kramdown'
3
4
 
4
- module Tilt
5
- # Kramdown Markdown implementation. See:
6
- # http://kramdown.rubyforge.org/
7
- class KramdownTemplate < Template
8
- DUMB_QUOTES = [39, 39, 34, 34]
5
+ dumb_quotes = [39, 39, 34, 34].freeze
9
6
 
10
- def prepare
11
- options[:smart_quotes] = DUMB_QUOTES unless options[:smartypants]
12
- @engine = Kramdown::Document.new(data, options)
13
- @output = nil
14
- end
7
+ # Kramdown Markdown implementation. See: https://kramdown.gettalong.org/
8
+ Tilt::KramdownTemplate = Tilt::StaticTemplate.subclass do
9
+ # dup as Krawmdown modifies the passed option with map!
10
+ @options[:smart_quotes] = dumb_quotes.dup unless @options[:smartypants]
15
11
 
16
- def evaluate(scope, locals, &block)
17
- @output ||= @engine.to_html
18
- end
19
-
20
- def allows_script?
21
- false
22
- end
23
- end
12
+ Kramdown::Document.new(@data, @options).to_html
24
13
  end
25
-
data/lib/tilt/liquid.rb CHANGED
@@ -1,11 +1,12 @@
1
- require 'tilt/template'
1
+ # frozen_string_literal: true
2
+ require_relative 'template'
2
3
  require 'liquid'
3
4
 
4
5
  module Tilt
5
6
  # Liquid template implementation. See:
6
7
  # http://liquidmarkup.org/
7
8
  #
8
- # Liquid is designed to be a *safe* template system and threfore
9
+ # Liquid is designed to be a *safe* template system and therefore
9
10
  # does not provide direct access to execuatable scopes. In order to
10
11
  # support a +scope+, the +scope+ must be able to represent itself
11
12
  # as a hash by responding to #to_h. If the +scope+ does not respond
@@ -17,16 +18,17 @@ module Tilt
17
18
  # time when using this template engine.
18
19
  class LiquidTemplate < Template
19
20
  def prepare
20
- @engine = ::Liquid::Template.parse(data, liquid_options)
21
+ @options[:line_numbers] = true unless @options.has_key?(:line_numbers)
22
+ @engine = ::Liquid::Template.parse(@data, @options)
21
23
  end
22
24
 
23
- def evaluate(scope, locals, &block)
24
- locals = locals.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
25
+ def evaluate(scope, locs)
26
+ locals = {}
25
27
  if scope.respond_to?(:to_h)
26
- scope = scope.to_h.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
27
- locals = scope.merge(locals)
28
+ scope.to_h.each{|k, v| locals[k.to_s] = v}
28
29
  end
29
- locals['yield'] = block.nil? ? '' : yield
30
+ locs.each{|k, v| locals[k.to_s] = v}
31
+ locals['yield'] = block_given? ? yield : ''
30
32
  locals['content'] = locals['yield']
31
33
  @engine.render(locals)
32
34
  end
@@ -34,11 +36,5 @@ module Tilt
34
36
  def allows_script?
35
37
  false
36
38
  end
37
-
38
- private
39
-
40
- def liquid_options
41
- { line_numbers: true }.merge options
42
- end
43
39
  end
44
40
  end
@@ -1,23 +1,11 @@
1
- require 'tilt/template'
1
+ # frozen_string_literal: true
2
+ require_relative 'template'
2
3
  require 'livescript'
3
4
 
4
- module Tilt
5
- # LiveScript template implementation. See:
6
- # http://livescript.net/
7
- #
8
- # LiveScript templates do not support object scopes, locals, or yield.
9
- class LiveScriptTemplate < Template
10
- self.default_mime_type = 'application/javascript'
11
-
12
- def prepare
13
- end
14
-
15
- def evaluate(scope, locals, &block)
16
- @output ||= LiveScript.compile(data, options)
17
- end
18
-
19
- def allows_script?
20
- false
21
- end
22
- end
5
+ # LiveScript template implementation. See:
6
+ # http://livescript.net/
7
+ #
8
+ # LiveScript templates do not support object scopes, locals, or yield.
9
+ Tilt::LiveScriptTemplate = Tilt::StaticTemplate.subclass(mime_type: 'application/javascript') do
10
+ LiveScript.compile(@data, @options)
23
11
  end
data/lib/tilt/mapping.rb CHANGED
@@ -1,7 +1,81 @@
1
- require 'monitor'
1
+ # frozen_string_literal: true
2
2
  require_relative 'pipeline'
3
3
 
4
4
  module Tilt
5
+ # Private internal base class for both Mapping and FinalizedMapping, for the shared methods.
6
+ class BaseMapping
7
+ # Instantiates a new template class based on the file.
8
+ #
9
+ # @raise [RuntimeError] if there is no template class registered for the
10
+ # file name.
11
+ #
12
+ # @example
13
+ # mapping.new('index.mt') # => instance of MyEngine::Template
14
+ #
15
+ # @see Tilt::Template.new
16
+ def new(file, line=nil, options={}, &block)
17
+ if template_class = self[file]
18
+ template_class.new(file, line, options, &block)
19
+ else
20
+ fail "No template engine registered for #{File.basename(file)}"
21
+ end
22
+ end
23
+
24
+ # Looks up a template class based on file name and/or extension.
25
+ #
26
+ # @example
27
+ # mapping['views/hello.erb'] # => Tilt::ERBTemplate
28
+ # mapping['hello.erb'] # => Tilt::ERBTemplate
29
+ # mapping['erb'] # => Tilt::ERBTemplate
30
+ #
31
+ # @return [template class]
32
+ def [](file)
33
+ _, ext = split(file)
34
+ ext && lookup(ext)
35
+ end
36
+
37
+ alias template_for []
38
+
39
+ # Looks up a list of template classes based on file name. If the file name
40
+ # has multiple extensions, it will return all template classes matching the
41
+ # extensions from the end.
42
+ #
43
+ # @example
44
+ # mapping.templates_for('views/index.haml.erb')
45
+ # # => [Tilt::ERBTemplate, Tilt::HamlTemplate]
46
+ #
47
+ # @return [Array<template class>]
48
+ def templates_for(file)
49
+ templates = []
50
+
51
+ while true
52
+ prefix, ext = split(file)
53
+ break unless ext
54
+ templates << lookup(ext)
55
+ file = prefix
56
+ end
57
+
58
+ templates
59
+ end
60
+
61
+ private
62
+
63
+ def split(file)
64
+ pattern = file.to_s.downcase
65
+ full_pattern = pattern.dup
66
+
67
+ until registered?(pattern)
68
+ return if pattern.empty?
69
+ pattern = File.basename(pattern)
70
+ pattern.sub!(/\A[^.]*\.?/, '')
71
+ end
72
+
73
+ prefix_size = full_pattern.size - pattern.size
74
+ [full_pattern[0,prefix_size-1], pattern]
75
+ end
76
+ end
77
+ private_constant :BaseMapping
78
+
5
79
  # Tilt::Mapping associates file extensions with template implementations.
6
80
  #
7
81
  # mapping = Tilt::Mapping.new
@@ -39,16 +113,18 @@ module Tilt
39
113
  # the exception of the first, since that was the most preferred one.
40
114
  #
41
115
  # mapping = Tilt::Mapping.new
42
- # mapping.register_lazy('Bluecloth::Template', 'bluecloth/template', 'md')
116
+ # mapping.register_lazy('Maruku::Template', 'maruku/template', 'md')
43
117
  # mapping.register_lazy('RDiscount::Template', 'rdiscount/template', 'md')
44
118
  # mapping['index.md']
45
119
  # # => RDiscount::Template
46
120
  #
47
121
  # In the previous example we say that RDiscount has a *higher priority* than
48
- # BlueCloth. Tilt will first try to `require "rdiscount/template"`, falling
49
- # back to `require "bluecloth/template"`. If none of these are successful,
122
+ # Maruku. Tilt will first try to `require "rdiscount/template"`, falling
123
+ # back to `require "maruku/template"`. If none of these are successful,
50
124
  # the first error will be raised.
51
- class Mapping
125
+ class Mapping < BaseMapping
126
+ LOCK = Mutex.new
127
+
52
128
  # @private
53
129
  attr_reader :lazy_map, :template_map
54
130
 
@@ -59,8 +135,26 @@ module Tilt
59
135
 
60
136
  # @private
61
137
  def initialize_copy(other)
62
- @template_map = other.template_map.dup
63
- @lazy_map = other.lazy_map.dup
138
+ LOCK.synchronize do
139
+ @template_map = other.template_map.dup
140
+ @lazy_map = other.lazy_map.dup
141
+ end
142
+ end
143
+
144
+ # Return a finalized mapping. A finalized mapping will only include
145
+ # support for template libraries already loaded, and will not
146
+ # allow registering new template libraries or lazy loading template
147
+ # libraries not yet loaded. Finalized mappings improve performance
148
+ # by not requiring synchronization and ensure that the mapping will
149
+ # not attempt to load additional files (useful when restricting
150
+ # file system access after template libraries in use are loaded).
151
+ def finalized
152
+ LOCK.synchronize{@lazy_map.dup}.each do |pattern, classes|
153
+ register_defined_classes(LOCK.synchronize{classes.map(&:first)}, pattern)
154
+ end
155
+
156
+ # Check if a template class is already present
157
+ FinalizedMapping.new(LOCK.synchronize{@template_map.dup}.freeze)
64
158
  end
65
159
 
66
160
  # Registers a lazy template implementation by file extension. You
@@ -86,8 +180,9 @@ module Tilt
86
180
  class_name = "Tilt::#{class_name}"
87
181
  end
88
182
 
183
+ v = [class_name, file].freeze
89
184
  extensions.each do |ext|
90
- @lazy_map[ext].unshift([class_name, file])
185
+ LOCK.synchronize{@lazy_map[ext].unshift(v)}
91
186
  end
92
187
  end
93
188
 
@@ -109,7 +204,10 @@ module Tilt
109
204
  end
110
205
 
111
206
  extensions.each do |ext|
112
- @template_map[ext.to_s] = template_class
207
+ ext = ext.to_s
208
+ LOCK.synchronize do
209
+ @template_map[ext] = template_class
210
+ end
113
211
  end
114
212
  end
115
213
 
@@ -137,9 +235,9 @@ module Tilt
137
235
  # mapping.register_pipeline('scss.erb', 'erb'=>{:outvar=>'@foo'})
138
236
  # mapping.register_pipeline('scsserb', :extra_exts => 'scss.erb',
139
237
  # :templates=>['erb', 'scss'])
140
- def register_pipeline(ext, options={})
238
+ def register_pipeline(ext, options=EMPTY_HASH)
141
239
  templates = options[:templates] || ext.split('.').reverse
142
- templates = templates.map{|t| [self[t], options[t] || {}]}
240
+ templates = templates.map{|t| [self[t], options[t] || EMPTY_HASH]}
143
241
 
144
242
  klass = Class.new(Pipeline)
145
243
  klass.send(:const_set, :TEMPLATES, templates)
@@ -162,8 +260,10 @@ module Tilt
162
260
  def unregister(*extensions)
163
261
  extensions.each do |ext|
164
262
  ext = ext.to_s
165
- @template_map.delete(ext)
166
- @lazy_map.delete(ext)
263
+ LOCK.synchronize do
264
+ @template_map.delete(ext)
265
+ @lazy_map.delete(ext)
266
+ end
167
267
  end
168
268
 
169
269
  nil
@@ -178,119 +278,52 @@ module Tilt
178
278
  # mapping.registered?('erb') # => true
179
279
  # mapping.registered?('nope') # => false
180
280
  def registered?(ext)
181
- @template_map.has_key?(ext.downcase) or lazy?(ext)
182
- end
183
-
184
- # Instantiates a new template class based on the file.
185
- #
186
- # @raise [RuntimeError] if there is no template class registered for the
187
- # file name.
188
- #
189
- # @example
190
- # mapping.new('index.mt') # => instance of MyEngine::Template
191
- #
192
- # @see Tilt::Template.new
193
- def new(file, line=nil, options={}, &block)
194
- if template_class = self[file]
195
- template_class.new(file, line, options, &block)
196
- else
197
- fail "No template engine registered for #{File.basename(file)}"
198
- end
199
- end
200
-
201
- # Looks up a template class based on file name and/or extension.
202
- #
203
- # @example
204
- # mapping['views/hello.erb'] # => Tilt::ERBTemplate
205
- # mapping['hello.erb'] # => Tilt::ERBTemplate
206
- # mapping['erb'] # => Tilt::ERBTemplate
207
- #
208
- # @return [template class]
209
- def [](file)
210
- _, ext = split(file)
211
- ext && lookup(ext)
212
- end
213
-
214
- alias template_for []
215
-
216
- # Looks up a list of template classes based on file name. If the file name
217
- # has multiple extensions, it will return all template classes matching the
218
- # extensions from the end.
219
- #
220
- # @example
221
- # mapping.templates_for('views/index.haml.erb')
222
- # # => [Tilt::ERBTemplate, Tilt::HamlTemplate]
223
- #
224
- # @return [Array<template class>]
225
- def templates_for(file)
226
- templates = []
227
-
228
- while true
229
- prefix, ext = split(file)
230
- break unless ext
231
- templates << lookup(ext)
232
- file = prefix
233
- end
234
-
235
- templates
281
+ ext_downcase = ext.downcase
282
+ LOCK.synchronize{@template_map.has_key?(ext_downcase)} or lazy?(ext)
236
283
  end
237
284
 
238
285
  # Finds the extensions the template class has been registered under.
239
286
  # @param [template class] template_class
240
287
  def extensions_for(template_class)
241
288
  res = []
242
- template_map.each do |ext, klass|
289
+ LOCK.synchronize{@template_map.to_a}.each do |ext, klass|
243
290
  res << ext if template_class == klass
244
291
  end
245
- lazy_map.each do |ext, choices|
246
- res << ext if choices.any? { |klass, file| template_class.to_s == klass }
292
+ LOCK.synchronize{@lazy_map.to_a}.each do |ext, choices|
293
+ res << ext if LOCK.synchronize{choices.dup}.any? { |klass, file| template_class.to_s == klass }
247
294
  end
248
- res.uniq
295
+ res.uniq!
296
+ res
249
297
  end
250
298
 
251
299
  private
252
300
 
253
301
  def lazy?(ext)
254
302
  ext = ext.downcase
255
- @lazy_map.has_key?(ext) && !@lazy_map[ext].empty?
256
- end
257
-
258
- def split(file)
259
- pattern = file.to_s.downcase
260
- full_pattern = pattern.dup
261
-
262
- until registered?(pattern)
263
- return if pattern.empty?
264
- pattern = File.basename(pattern)
265
- pattern.sub!(/^[^.]*\.?/, '')
266
- end
267
-
268
- prefix_size = full_pattern.size - pattern.size
269
- [full_pattern[0,prefix_size-1], pattern]
303
+ LOCK.synchronize{@lazy_map.has_key?(ext) && !@lazy_map[ext].empty?}
270
304
  end
271
305
 
272
306
  def lookup(ext)
273
- @template_map[ext] || lazy_load(ext)
307
+ LOCK.synchronize{@template_map[ext]} || lazy_load(ext)
274
308
  end
275
309
 
276
- LOCK = Monitor.new
277
-
278
- def lazy_load(pattern)
279
- return unless @lazy_map.has_key?(pattern)
280
-
281
- LOCK.enter
282
- entered = true
283
-
284
- choices = @lazy_map[pattern]
285
-
286
- # Check if a template class is already present
287
- choices.each do |class_name, file|
310
+ def register_defined_classes(class_names, pattern)
311
+ class_names.each do |class_name|
288
312
  template_class = constant_defined?(class_name)
289
313
  if template_class
290
314
  register(template_class, pattern)
291
- return template_class
315
+ yield template_class if block_given?
292
316
  end
293
317
  end
318
+ end
319
+
320
+ def lazy_load(pattern)
321
+ choices = LOCK.synchronize{@lazy_map[pattern].dup}
322
+
323
+ # Check if a template class is already present
324
+ register_defined_classes(choices.map(&:first), pattern) do |template_class|
325
+ return template_class
326
+ end
294
327
 
295
328
  first_failure = nil
296
329
 
@@ -309,9 +342,7 @@ module Tilt
309
342
  end
310
343
  end
311
344
 
312
- raise first_failure if first_failure
313
- ensure
314
- LOCK.exit if entered
345
+ raise first_failure
315
346
  end
316
347
 
317
348
  # The proper behavior (in MRI) for autoload? is to
@@ -333,4 +364,49 @@ module Tilt
333
364
  end
334
365
  end
335
366
  end
367
+
368
+ # Private internal class for finalized mappings, which are frozen and
369
+ # cannot be modified.
370
+ class FinalizedMapping < BaseMapping
371
+ # Set the template map to use. The template map should already
372
+ # be frozen, but this is an internal class, so it does not
373
+ # explicitly check for that.
374
+ def initialize(template_map)
375
+ @template_map = template_map
376
+ freeze
377
+ end
378
+
379
+ # Returns receiver, since instances are always frozen.
380
+ def dup
381
+ self
382
+ end
383
+
384
+ # Returns receiver, since instances are always frozen.
385
+ def clone(freeze: false)
386
+ self
387
+ end
388
+
389
+ # Return whether the given file extension has been registered.
390
+ def registered?(ext)
391
+ @template_map.has_key?(ext.downcase)
392
+ end
393
+
394
+ # Returns an aarry of all extensions the template class will
395
+ # be used for.
396
+ def extensions_for(template_class)
397
+ res = []
398
+ @template_map.each do |ext, klass|
399
+ res << ext if template_class == klass
400
+ end
401
+ res.uniq!
402
+ res
403
+ end
404
+
405
+ private
406
+
407
+ def lookup(ext)
408
+ @template_map[ext]
409
+ end
410
+ end
411
+ private_constant :FinalizedMapping
336
412
  end
data/lib/tilt/markaby.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'tilt/template'
1
+ # frozen_string_literal: true
2
+ require_relative 'template'
2
3
  require 'markaby'
3
4
 
4
5
  module Tilt
@@ -15,19 +16,16 @@ module Tilt
15
16
  end
16
17
  end
17
18
 
18
- def prepare
19
- end
20
-
21
19
  def evaluate(scope, locals, &block)
22
20
  builder = self.class.builder_class.new({}, scope)
23
21
  builder.locals = locals
24
22
 
25
- if data.kind_of? Proc
26
- (class << builder; self end).send(:define_method, :__run_markaby_tilt__, &data)
23
+ if @data.kind_of? Proc
24
+ (class << builder; self end).send(:define_method, :__run_markaby_tilt__, &@data)
27
25
  else
28
26
  builder.instance_eval <<-CODE, __FILE__, __LINE__
29
27
  def __run_markaby_tilt__
30
- #{data}
28
+ #{@data}
31
29
  end
32
30
  CODE
33
31
  end