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 'markaby'
2
3
 
3
4
  module Tilt
4
5
  # Markaby
@@ -14,14 +15,6 @@ module Tilt
14
15
  end
15
16
  end
16
17
 
17
- def self.engine_initialized?
18
- defined? ::Markaby
19
- end
20
-
21
- def initialize_engine
22
- require_template_library 'markaby'
23
- end
24
-
25
18
  def prepare
26
19
  end
27
20
 
@@ -0,0 +1,22 @@
1
+ require 'tilt/template'
2
+ require 'maruku'
3
+
4
+ module Tilt
5
+ # Maruku markdown implementation. See:
6
+ # http://maruku.rubyforge.org/
7
+ class MarukuTemplate < Template
8
+ def prepare
9
+ @engine = Maruku.new(data, options)
10
+ @output = nil
11
+ end
12
+
13
+ def evaluate(scope, locals, &block)
14
+ @output ||= @engine.to_html
15
+ end
16
+
17
+ def allows_script?
18
+ false
19
+ end
20
+ end
21
+ end
22
+
@@ -1,4 +1,5 @@
1
1
  require 'tilt/template'
2
+ require 'nokogiri'
2
3
 
3
4
  module Tilt
4
5
  # Nokogiri template implementation. See:
@@ -7,14 +8,6 @@ module Tilt
7
8
  DOCUMENT_HEADER = /^<\?xml version=\"1\.0\"\?>\n?/
8
9
  self.default_mime_type = 'text/xml'
9
10
 
10
- def self.engine_initialized?
11
- defined? ::Nokogiri
12
- end
13
-
14
- def initialize_engine
15
- require_template_library 'nokogiri'
16
- end
17
-
18
11
  def prepare; end
19
12
 
20
13
  def evaluate(scope, locals)
@@ -1,13 +1,10 @@
1
1
  require 'tilt/template'
2
+ require 'radius'
2
3
 
3
4
  module Tilt
4
5
  # Radius Template
5
6
  # http://github.com/jlong/radius/
6
7
  class RadiusTemplate < Template
7
- def self.engine_initialized?
8
- defined? ::Radius
9
- end
10
-
11
8
  def self.context_class
12
9
  @context_class ||= Class.new(Radius::Context) do
13
10
  attr_accessor :tilt_scope
@@ -24,10 +21,6 @@ module Tilt
24
21
  end
25
22
  end
26
23
 
27
- def initialize_engine
28
- require_template_library 'radius'
29
- end
30
-
31
24
  def prepare
32
25
  end
33
26
 
@@ -0,0 +1,39 @@
1
+ require 'tilt/template'
2
+ require 'rdiscount'
3
+
4
+ module Tilt
5
+ # Discount Markdown implementation. See:
6
+ # http://github.com/rtomayko/rdiscount
7
+ #
8
+ # RDiscount is a simple text filter. It does not support +scope+ or
9
+ # +locals+. The +:smart+ and +:filter_html+ options may be set true
10
+ # to enable those flags on the underlying RDiscount object.
11
+ class RDiscountTemplate < Template
12
+ self.default_mime_type = 'text/html'
13
+
14
+ ALIAS = {
15
+ :escape_html => :filter_html,
16
+ :smartypants => :smart
17
+ }
18
+
19
+ FLAGS = [:smart, :filter_html, :smartypants, :escape_html]
20
+
21
+ def flags
22
+ FLAGS.select { |flag| options[flag] }.map { |flag| ALIAS[flag] || flag }
23
+ end
24
+
25
+ def prepare
26
+ @engine = RDiscount.new(data, *flags)
27
+ @output = nil
28
+ end
29
+
30
+ def evaluate(scope, locals, &block)
31
+ @output ||= @engine.to_html
32
+ end
33
+
34
+ def allows_script?
35
+ false
36
+ end
37
+ end
38
+ end
39
+
@@ -1,4 +1,7 @@
1
1
  require 'tilt/template'
2
+ require 'rdoc'
3
+ require 'rdoc/markup'
4
+ require 'rdoc/markup/to_html'
2
5
 
3
6
  module Tilt
4
7
  # RDoc template. See:
@@ -10,16 +13,6 @@ module Tilt
10
13
  class RDocTemplate < Template
11
14
  self.default_mime_type = 'text/html'
12
15
 
13
- def self.engine_initialized?
14
- defined? ::RDoc::Markup::ToHtml
15
- end
16
-
17
- def initialize_engine
18
- require_template_library 'rdoc'
19
- require_template_library 'rdoc/markup'
20
- require_template_library 'rdoc/markup/to_html'
21
- end
22
-
23
16
  def markup
24
17
  begin
25
18
  # RDoc 4.0
@@ -0,0 +1,104 @@
1
+ require 'tilt/template'
2
+ require 'redcarpet'
3
+
4
+ module Tilt
5
+ # Compatibility mode for Redcarpet 1.x
6
+ class Redcarpet1Template < Template
7
+ self.default_mime_type = 'text/html'
8
+
9
+ ALIAS = {
10
+ :escape_html => :filter_html,
11
+ :smartypants => :smart
12
+ }
13
+
14
+ FLAGS = [:smart, :filter_html, :smartypants, :escape_html]
15
+
16
+ def flags
17
+ FLAGS.select { |flag| options[flag] }.map { |flag| ALIAS[flag] || flag }
18
+ end
19
+
20
+ def prepare
21
+ @engine = RedcarpetCompat.new(data, *flags)
22
+ @output = nil
23
+ end
24
+
25
+ def evaluate(scope, locals, &block)
26
+ @output ||= @engine.to_html
27
+ end
28
+
29
+ def allows_script?
30
+ false
31
+ end
32
+ end
33
+
34
+ # Future proof mode for Redcarpet 2.x (not yet released)
35
+ class Redcarpet2Template < Template
36
+ self.default_mime_type = 'text/html'
37
+
38
+ def self.engine_initialized?
39
+ defined? ::Redcarpet::Render and defined? ::Redcarpet::Markdown
40
+ end
41
+
42
+ def generate_renderer
43
+ renderer = options.delete(:renderer) || ::Redcarpet::Render::HTML
44
+ return renderer unless options.delete(:smartypants)
45
+ return renderer if renderer.is_a?(Class) && renderer <= ::Redcarpet::Render::SmartyPants
46
+
47
+ if renderer == ::Redcarpet::Render::XHTML
48
+ ::Redcarpet::Render::SmartyHTML.new(:xhtml => true)
49
+ elsif renderer == ::Redcarpet::Render::HTML
50
+ ::Redcarpet::Render::SmartyHTML
51
+ elsif renderer.is_a? Class
52
+ Class.new(renderer) { include ::Redcarpet::Render::SmartyPants }
53
+ else
54
+ renderer.extend ::Redcarpet::Render::SmartyPants
55
+ end
56
+ end
57
+
58
+ def prepare
59
+ # try to support the same aliases
60
+ Redcarpet1Template::ALIAS.each do |opt, aka|
61
+ next if options.key? opt or not options.key? aka
62
+ options[opt] = options.delete(aka)
63
+ end
64
+
65
+ # only raise an exception if someone is trying to enable :escape_html
66
+ options.delete(:escape_html) unless options[:escape_html]
67
+
68
+ @engine = ::Redcarpet::Markdown.new(generate_renderer, options)
69
+ @output = nil
70
+ end
71
+
72
+ def evaluate(scope, locals, &block)
73
+ @output ||= @engine.render(data)
74
+ end
75
+
76
+ def allows_script?
77
+ false
78
+ end
79
+ end
80
+
81
+ # Upskirt Markdown implementation. See:
82
+ # https://github.com/tanoku/redcarpet
83
+ #
84
+ # Supports both Redcarpet 1.x and 2.x
85
+ class RedcarpetTemplate < Template
86
+ Redcarpet1 = Redcarpet1Template
87
+ Redcarpet2 = Redcarpet2Template
88
+
89
+ def prepare
90
+ klass = Redcarpet2.engine_initialized? ? Redcarpet2 : Redcarpet1
91
+ @engine = klass.new(file, line, options) { data }
92
+ end
93
+
94
+ def evaluate(scope, locals, &block)
95
+ @engine.evaluate(scope, locals, &block)
96
+ end
97
+
98
+ def allows_script?
99
+ false
100
+ end
101
+ end
102
+
103
+ end
104
+
@@ -1,17 +1,10 @@
1
1
  require 'tilt/template'
2
+ require 'redcloth'
2
3
 
3
4
  module Tilt
4
5
  # RedCloth implementation. See:
5
6
  # http://redcloth.org/
6
7
  class RedClothTemplate < Template
7
- def self.engine_initialized?
8
- defined? ::RedCloth
9
- end
10
-
11
- def initialize_engine
12
- require_template_library 'redcloth'
13
- end
14
-
15
8
  def prepare
16
9
  @engine = RedCloth.new(data)
17
10
  options.each {|k, v| @engine.send("#{k}=", v) if @engine.respond_to? "#{k}="}
@@ -0,0 +1,41 @@
1
+ require 'tilt/template'
2
+ require 'sass'
3
+
4
+ module Tilt
5
+ # Sass template implementation. See:
6
+ # http://haml.hamptoncatlin.com/
7
+ #
8
+ # Sass templates do not support object scopes, locals, or yield.
9
+ class SassTemplate < Template
10
+ self.default_mime_type = 'text/css'
11
+
12
+ def prepare
13
+ @engine = ::Sass::Engine.new(data, sass_options)
14
+ end
15
+
16
+ def evaluate(scope, locals, &block)
17
+ @output ||= @engine.render
18
+ end
19
+
20
+ def allows_script?
21
+ false
22
+ end
23
+
24
+ private
25
+ def sass_options
26
+ options.merge(:filename => eval_file, :line => line, :syntax => :sass)
27
+ end
28
+ end
29
+
30
+ # Sass's new .scss type template implementation.
31
+ class ScssTemplate < SassTemplate
32
+ self.default_mime_type = 'text/css'
33
+
34
+ private
35
+ def sass_options
36
+ options.merge(:filename => eval_file, :line => line, :syntax => :scss)
37
+ end
38
+ end
39
+
40
+ end
41
+
@@ -1,5 +1,11 @@
1
+ require 'tilt'
2
+ require 'thread'
3
+
1
4
  module Tilt
5
+ # @private
2
6
  TOPOBJECT = Object.superclass || Object
7
+ # @private
8
+ LOCK = Mutex.new
3
9
 
4
10
  # Base class for template implementations. Subclasses must implement
5
11
  # the #prepare method and one of the #evaluate or #precompiled_template
@@ -19,14 +25,22 @@ module Tilt
19
25
  # interface.
20
26
  attr_reader :options
21
27
 
22
- # Used to determine if this class's initialize_engine method has
23
- # been called yet.
24
- @engine_initialized = false
25
28
  class << self
26
- attr_accessor :engine_initialized
27
- alias engine_initialized? engine_initialized
29
+ # An empty Hash that the template engine can populate with various
30
+ # metadata.
31
+ def metadata
32
+ @metadata ||= {}
33
+ end
28
34
 
29
- attr_accessor :default_mime_type
35
+ # @deprecated Use `.metadata[:mime_type]` instead.
36
+ def default_mime_type
37
+ metadata[:mime_type]
38
+ end
39
+
40
+ # @deprecated Use `.metadata[:mime_type] = val` instead.
41
+ def default_mime_type=(value)
42
+ metadata[:mime_type] = value
43
+ end
30
44
  end
31
45
 
32
46
  # Create a new template with the file, line, and options specified. By
@@ -50,13 +64,6 @@ module Tilt
50
64
 
51
65
  raise ArgumentError, "file or block required" if (@file || block).nil?
52
66
 
53
- # call the initialize_engine method if this is the very first time
54
- # an instance of this class has been created.
55
- if !self.class.engine_initialized?
56
- initialize_engine
57
- self.class.engine_initialized = true
58
- end
59
-
60
67
  # used to hold compiled template methods
61
68
  @compiled_method = {}
62
69
 
@@ -79,28 +86,15 @@ module Tilt
79
86
  prepare
80
87
  end
81
88
 
82
- # The encoding of the source data. Defaults to the
83
- # default_encoding-option if present. You may override this method
84
- # in your template class if you have a better hint of the data's
85
- # encoding.
86
- def default_encoding
87
- @default_encoding
88
- end
89
-
90
- def read_template_file
91
- data = File.open(file, 'rb') { |io| io.read }
92
- if data.respond_to?(:force_encoding)
93
- # Set it to the default external (without verifying)
94
- data.force_encoding(Encoding.default_external) if Encoding.default_external
95
- end
96
- data
97
- end
98
-
99
89
  # Render the template in the given scope with the locals specified. If a
100
90
  # block is given, it is typically available within the template via
101
91
  # +yield+.
102
92
  def render(scope=Object.new, locals={}, &block)
103
- evaluate scope, locals || {}, &block
93
+ current_template = Thread.current[:tilt_current_template]
94
+ Thread.current[:tilt_current_template] = self
95
+ evaluate(scope, locals || {}, &block)
96
+ ensure
97
+ Thread.current[:tilt_current_template] = current_template
104
98
  end
105
99
 
106
100
  # The basename of the template file.
@@ -118,30 +112,26 @@ module Tilt
118
112
  file || '(__TEMPLATE__)'
119
113
  end
120
114
 
121
- # Whether or not this template engine allows executing Ruby script
122
- # within the template. If this is false, +scope+ and +locals+ will
123
- # generally not be used, nor will the provided block be avaiable
124
- # via +yield+.
125
- # This should be overridden by template subclasses.
126
- def allows_script?
127
- true
115
+ # An empty Hash that the template engine can populate with various
116
+ # metadata.
117
+ def metadata
118
+ if respond_to?(:allows_script?)
119
+ self.class.metadata.merge(:allows_script => allows_script?)
120
+ else
121
+ self.class.metadata
122
+ end
128
123
  end
129
124
 
130
- protected
131
- # Called once and only once for each template subclass the first time
132
- # the template class is initialized. This should be used to require the
133
- # underlying template library and perform any initial setup.
134
- def initialize_engine
135
- end
125
+ protected
136
126
 
137
- # Like Kernel#require but issues a warning urging a manual require when
138
- # running under a threaded environment.
139
- def require_template_library(name)
140
- if Thread.list.size > 1
141
- warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
142
- "explicit require '#{name}' suggested."
143
- end
144
- require name
127
+ # @!group For template implementations
128
+
129
+ # The encoding of the source data. Defaults to the
130
+ # default_encoding-option if present. You may override this method
131
+ # in your template class if you have a better hint of the data's
132
+ # encoding.
133
+ def default_encoding
134
+ @default_encoding
145
135
  end
146
136
 
147
137
  # Do whatever preparation is necessary to setup the underlying template
@@ -150,13 +140,7 @@ module Tilt
150
140
  #
151
141
  # Subclasses must provide an implementation of this method.
152
142
  def prepare
153
- if respond_to?(:compile!)
154
- # backward compat with tilt < 0.6; just in case
155
- warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.'
156
- compile!
157
- else
158
- raise NotImplementedError
159
- end
143
+ raise NotImplementedError
160
144
  end
161
145
 
162
146
  # Execute the compiled template and return the result string. Template
@@ -179,10 +163,10 @@ module Tilt
179
163
  # control over source generation or want to adjust the default line
180
164
  # offset. In most cases, overriding the #precompiled_template method is
181
165
  # easier and more appropriate.
182
- def precompiled(locals)
183
- preamble = precompiled_preamble(locals)
184
- template = precompiled_template(locals)
185
- postamble = precompiled_postamble(locals)
166
+ def precompiled(local_keys)
167
+ preamble = precompiled_preamble(local_keys)
168
+ template = precompiled_template(local_keys)
169
+ postamble = precompiled_postamble(local_keys)
186
170
  source = ''
187
171
 
188
172
  # Ensure that our generated source code has the same encoding as the
@@ -194,10 +178,7 @@ module Tilt
194
178
  template.force_encoding(template_encoding)
195
179
  end
196
180
 
197
- # https://github.com/rtomayko/tilt/issues/193
198
- warn "precompiled_preamble should return String (not Array)" if preamble.is_a?(Array)
199
- warn "precompiled_postamble should return String (not Array)" if postamble.is_a?(Array)
200
- source << [preamble, template, postamble].join("\n")
181
+ source << preamble << "\n" << template << "\n" << postamble
201
182
 
202
183
  [source, preamble.count("\n")+1]
203
184
  end
@@ -208,41 +189,52 @@ module Tilt
208
189
  # the base Template guarantees correct file/line handling, locals
209
190
  # support, custom scopes, proper encoding, and support for template
210
191
  # compilation.
211
- def precompiled_template(locals)
192
+ def precompiled_template(local_keys)
212
193
  raise NotImplementedError
213
194
  end
214
195
 
215
- # Generates preamble code for initializing template state, and performing
216
- # locals assignment. The default implementation performs locals
217
- # assignment only. Lines included in the preamble are subtracted from the
218
- # source line offset, so adding code to the preamble does not effect line
219
- # reporting in Kernel::caller and backtraces.
220
- def precompiled_preamble(locals)
221
- locals.map do |k,v|
222
- if k.to_s =~ /\A[a-z_][a-zA-Z_0-9]*\z/
223
- "#{k} = locals[#{k.inspect}]"
224
- else
225
- raise "invalid locals key: #{k.inspect} (keys must be variable names)"
226
- end
227
- end.join("\n")
196
+ def precompiled_preamble(local_keys)
197
+ ''
228
198
  end
229
199
 
230
- # Generates postamble code for the precompiled template source. The
231
- # string returned from this method is appended to the precompiled
232
- # template source.
233
- def precompiled_postamble(locals)
200
+ def precompiled_postamble(local_keys)
234
201
  ''
235
202
  end
236
203
 
204
+ # !@endgroup
205
+
206
+ private
207
+
208
+ def read_template_file
209
+ data = File.open(file, 'rb') { |io| io.read }
210
+ if data.respond_to?(:force_encoding)
211
+ # Set it to the default external (without verifying)
212
+ data.force_encoding(Encoding.default_external) if Encoding.default_external
213
+ end
214
+ data
215
+ end
216
+
237
217
  # The compiled method for the locals keys provided.
238
218
  def compiled_method(locals_keys)
239
- @compiled_method[locals_keys] ||=
240
- compile_template_method(locals_keys)
219
+ LOCK.synchronize do
220
+ @compiled_method[locals_keys] ||= compile_template_method(locals_keys)
221
+ end
222
+ end
223
+
224
+ def local_extraction(local_keys)
225
+ local_keys.map do |k|
226
+ if k.to_s =~ /\A[a-z_][a-zA-Z_0-9]*\z/
227
+ "#{k} = locals[#{k.inspect}]"
228
+ else
229
+ raise "invalid locals key: #{k.inspect} (keys must be variable names)"
230
+ end
231
+ end.join("\n")
241
232
  end
242
233
 
243
- private
244
- def compile_template_method(locals)
245
- source, offset = precompiled(locals)
234
+ def compile_template_method(local_keys)
235
+ source, offset = precompiled(local_keys)
236
+ local_code = local_extraction(local_keys)
237
+
246
238
  method_name = "__tilt_#{Thread.current.object_id.abs}"
247
239
  method_source = ""
248
240
 
@@ -257,11 +249,12 @@ module Tilt
257
249
  class << self
258
250
  this, locals = Thread.current[:tilt_vars]
259
251
  this.instance_eval do
252
+ #{local_code}
260
253
  RUBY
261
254
  offset += method_source.count("\n")
262
255
  method_source << source
263
256
  method_source << "\nend;end;end;end"
264
- Object.class_eval method_source, eval_file, line - offset
257
+ Object.class_eval(method_source, eval_file, line - offset)
265
258
  unbind_compiled_method(method_name)
266
259
  end
267
260
 
@@ -276,7 +269,7 @@ module Tilt
276
269
  end
277
270
 
278
271
  def extract_magic_comment(script)
279
- binary script do
272
+ binary(script) do
280
273
  script[/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/n, 1]
281
274
  end
282
275
  end