tilt 2.1.0 → 2.3.0

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 (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