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