tilt 1.3.3 → 1.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/COPYING +1 -1
  2. data/Gemfile +31 -1
  3. data/HACKING +16 -0
  4. data/README.md +5 -2
  5. data/Rakefile +25 -1
  6. data/TEMPLATES.md +13 -13
  7. data/bin/tilt +8 -6
  8. data/lib/tilt.rb +15 -1
  9. data/lib/tilt/asciidoc.rb +34 -0
  10. data/lib/tilt/coffee.rb +4 -0
  11. data/lib/tilt/css.rb +10 -2
  12. data/lib/tilt/csv.rb +71 -0
  13. data/lib/tilt/erb.rb +1 -1
  14. data/lib/tilt/etanni.rb +27 -0
  15. data/lib/tilt/liquid.rb +4 -0
  16. data/lib/tilt/markdown.rb +35 -11
  17. data/lib/tilt/nokogiri.rb +4 -4
  18. data/lib/tilt/plain.rb +20 -0
  19. data/lib/tilt/radius.rb +4 -0
  20. data/lib/tilt/rdoc.rb +20 -5
  21. data/lib/tilt/template.rb +35 -74
  22. data/lib/tilt/textile.rb +5 -0
  23. data/lib/tilt/wiki.rb +8 -0
  24. data/test/tilt_asciidoctor_test.rb +44 -0
  25. data/test/tilt_blueclothtemplate_test.rb +1 -1
  26. data/test/tilt_coffeescripttemplate_test.rb +64 -11
  27. data/test/tilt_creoletemplate_test.rb +1 -1
  28. data/test/tilt_csv_test.rb +73 -0
  29. data/test/tilt_erbtemplate_test.rb +5 -0
  30. data/test/tilt_erubistemplate_test.rb +1 -1
  31. data/test/tilt_etannitemplate_test.rb +173 -0
  32. data/test/tilt_fallback_test.rb +6 -6
  33. data/test/tilt_hamltemplate_test.rb +1 -1
  34. data/test/tilt_kramdown_test.rb +1 -1
  35. data/test/tilt_lesstemplate_test.less +1 -0
  36. data/test/tilt_lesstemplate_test.rb +19 -3
  37. data/test/tilt_liquidtemplate_test.rb +1 -1
  38. data/test/tilt_markaby_test.rb +1 -1
  39. data/test/tilt_markdown_test.rb +15 -4
  40. data/test/tilt_marukutemplate_test.rb +1 -1
  41. data/test/tilt_radiustemplate_test.rb +1 -1
  42. data/test/tilt_rdiscounttemplate_test.rb +1 -1
  43. data/test/tilt_rdoctemplate_test.rb +10 -3
  44. data/test/tilt_redcarpettemplate_test.rb +15 -3
  45. data/test/tilt_redclothtemplate_test.rb +13 -1
  46. data/test/tilt_sasstemplate_test.rb +1 -1
  47. data/test/tilt_stringtemplate_test.rb +1 -1
  48. data/test/tilt_template_test.rb +13 -0
  49. data/test/tilt_wikiclothtemplate_test.rb +1 -1
  50. data/test/tilt_yajltemplate_test.rb +13 -4
  51. data/tilt.gemspec +27 -17
  52. metadata +203 -75
@@ -24,7 +24,7 @@ module Tilt
24
24
 
25
25
  def prepare
26
26
  @outvar = options[:outvar] || self.class.default_output_variable
27
- options[:trim] = '<>' if options[:trim].nil? || options[:trim] == true
27
+ options[:trim] = '<>' if !(options[:trim] == false) && (options[:trim].nil? || options[:trim] == true)
28
28
  @engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
29
29
  end
30
30
 
@@ -0,0 +1,27 @@
1
+ require 'tilt/template'
2
+
3
+ module Tilt
4
+ class EtanniTemplate < Template
5
+ def prepare
6
+ separator = data.hash.abs
7
+ chomp = "<<#{separator}.chomp!"
8
+ start = "\n_out_ << #{chomp}\n"
9
+ stop = "\n#{separator}\n"
10
+ replacement = "#{stop}\\1#{start}"
11
+
12
+ temp = data.strip
13
+ temp.gsub!(/<\?r\s+(.*?)\s+\?>/m, replacement)
14
+
15
+ @code = "_out_ = [<<#{separator}.chomp!]\n#{temp}#{stop}_out_.join"
16
+ end
17
+
18
+ def precompiled_template(locals)
19
+ @code
20
+ end
21
+
22
+ def precompiled(locals)
23
+ source, offset = super
24
+ [source, offset + 1]
25
+ end
26
+ end
27
+ end
@@ -37,5 +37,9 @@ module Tilt
37
37
  locals['content'] = locals['yield']
38
38
  @engine.render(locals)
39
39
  end
40
+
41
+ def allows_script?
42
+ false
43
+ end
40
44
  end
41
45
  end
@@ -37,6 +37,10 @@ module Tilt
37
37
  def evaluate(scope, locals, &block)
38
38
  @output ||= @engine.to_html
39
39
  end
40
+
41
+ def allows_script?
42
+ false
43
+ end
40
44
  end
41
45
 
42
46
  # Upskirt Markdown implementation. See:
@@ -53,7 +57,7 @@ module Tilt
53
57
  end
54
58
 
55
59
  def prepare
56
- klass = [Redcarpet1, Redcarpet2].detect { |e| e.engine_initialized? }
60
+ klass = [Redcarpet2, Redcarpet1].detect { |e| e.engine_initialized? }
57
61
  @engine = klass.new(file, line, options) { data }
58
62
  end
59
63
 
@@ -61,6 +65,10 @@ module Tilt
61
65
  @engine.evaluate(scope, locals, &block)
62
66
  end
63
67
 
68
+ def allows_script?
69
+ false
70
+ end
71
+
64
72
  # Compatibility mode for Redcarpet 1.x
65
73
  class Redcarpet1 < RDiscountTemplate
66
74
  self.default_mime_type = 'text/html'
@@ -80,22 +88,22 @@ module Tilt
80
88
  self.default_mime_type = 'text/html'
81
89
 
82
90
  def self.engine_initialized?
83
- defined? ::Redcarpet::Render
91
+ defined? ::Redcarpet::Render and defined? ::Redcarpet::Markdown
84
92
  end
85
93
 
86
94
  def generate_renderer
87
- renderer = options.delete(:renderer) || Redcarpet::Render::HTML
95
+ renderer = options.delete(:renderer) || ::Redcarpet::Render::HTML
88
96
  return renderer unless options.delete(:smartypants)
89
- return renderer if renderer <= Redcarpet::Render::SmartyPants
97
+ return renderer if renderer.is_a?(Class) && renderer <= ::Redcarpet::Render::SmartyPants
90
98
 
91
- if renderer == Redcarpet::Render::XHTML
92
- Redcarpet::Render::SmartyHTML.new(:xhtml => true)
93
- elsif renderer == Redcarpet::Render::HTML
94
- Redcarpet::Render::SmartyHTML
99
+ if renderer == ::Redcarpet::Render::XHTML
100
+ ::Redcarpet::Render::SmartyHTML.new(:xhtml => true)
101
+ elsif renderer == ::Redcarpet::Render::HTML
102
+ ::Redcarpet::Render::SmartyHTML
95
103
  elsif renderer.is_a? Class
96
- Class.new(renderer) { include Redcarpet::Render::SmartyPants }
104
+ Class.new(renderer) { include ::Redcarpet::Render::SmartyPants }
97
105
  else
98
- renderer.extend Redcarpet::Render::SmartyPants
106
+ renderer.extend ::Redcarpet::Render::SmartyPants
99
107
  end
100
108
  end
101
109
 
@@ -109,13 +117,17 @@ module Tilt
109
117
  # only raise an exception if someone is trying to enable :escape_html
110
118
  options.delete(:escape_html) unless options[:escape_html]
111
119
 
112
- @engine = Redcarpet::Markdown.new(generate_renderer, options)
120
+ @engine = ::Redcarpet::Markdown.new(generate_renderer, options)
113
121
  @output = nil
114
122
  end
115
123
 
116
124
  def evaluate(scope, locals, &block)
117
125
  @output ||= @engine.render(data)
118
126
  end
127
+
128
+ def allows_script?
129
+ false
130
+ end
119
131
  end
120
132
  end
121
133
 
@@ -140,6 +152,10 @@ module Tilt
140
152
  def evaluate(scope, locals, &block)
141
153
  @output ||= @engine.to_html
142
154
  end
155
+
156
+ def allows_script?
157
+ false
158
+ end
143
159
  end
144
160
 
145
161
  # Maruku markdown implementation. See:
@@ -161,6 +177,10 @@ module Tilt
161
177
  def evaluate(scope, locals, &block)
162
178
  @output ||= @engine.to_html
163
179
  end
180
+
181
+ def allows_script?
182
+ false
183
+ end
164
184
  end
165
185
 
166
186
  # Kramdown Markdown implementation. See:
@@ -185,6 +205,10 @@ module Tilt
185
205
  def evaluate(scope, locals, &block)
186
206
  @output ||= @engine.to_html
187
207
  end
208
+
209
+ def allows_script?
210
+ false
211
+ end
188
212
  end
189
213
  end
190
214
 
@@ -4,6 +4,7 @@ module Tilt
4
4
  # Nokogiri template implementation. See:
5
5
  # http://nokogiri.org/
6
6
  class NokogiriTemplate < Template
7
+ DOCUMENT_HEADER = /^<\?xml version=\"1\.0\"\?>\n?/
7
8
  self.default_mime_type = 'text/xml'
8
9
 
9
10
  def self.engine_initialized?
@@ -16,11 +17,10 @@ module Tilt
16
17
 
17
18
  def prepare; end
18
19
 
19
- def evaluate(scope, locals, &block)
20
- block &&= proc { yield.gsub(/^<\?xml version=\"1\.0\"\?>\n?/, "") }
21
-
20
+ def evaluate(scope, locals)
22
21
  if data.respond_to?(:to_str)
23
- super(scope, locals, &block)
22
+ wrapper = proc { yield.sub(DOCUMENT_HEADER, "") } if block_given?
23
+ super(scope, locals, &wrapper)
24
24
  else
25
25
  ::Nokogiri::XML::Builder.new.tap(&data).to_xml
26
26
  end
@@ -0,0 +1,20 @@
1
+ require 'tilt/template'
2
+
3
+
4
+ module Tilt
5
+ # Raw text (no template functionality).
6
+ class PlainTemplate < Template
7
+ self.default_mime_type = 'text/html'
8
+
9
+ def self.engine_initialized?
10
+ true
11
+ end
12
+
13
+ def prepare
14
+ end
15
+
16
+ def evaluate(scope, locals, &block)
17
+ @output ||= data
18
+ end
19
+ end
20
+ end
@@ -47,5 +47,9 @@ module Tilt
47
47
  parser = Radius::Parser.new(context, options)
48
48
  parser.parse(data)
49
49
  end
50
+
51
+ def allows_script?
52
+ false
53
+ end
50
54
  end
51
55
  end
@@ -4,23 +4,34 @@ module Tilt
4
4
  # RDoc template. See:
5
5
  # http://rdoc.rubyforge.org/
6
6
  #
7
- # It's suggested that your program require 'rdoc/markup' and
8
- # 'rdoc/markup/to_html' at load time when using this template
9
- # engine.
7
+ # It's suggested that your program `require 'rdoc/markup'` and
8
+ # `require 'rdoc/markup/to_html'` at load time when using this template
9
+ # engine in a threaded environment.
10
10
  class RDocTemplate < Template
11
11
  self.default_mime_type = 'text/html'
12
12
 
13
13
  def self.engine_initialized?
14
- defined? ::RDoc::Markup
14
+ defined? ::RDoc::Markup::ToHtml
15
15
  end
16
16
 
17
17
  def initialize_engine
18
+ require_template_library 'rdoc'
18
19
  require_template_library 'rdoc/markup'
19
20
  require_template_library 'rdoc/markup/to_html'
20
21
  end
21
22
 
23
+ def markup
24
+ begin
25
+ # RDoc 4.0
26
+ require 'rdoc/options'
27
+ RDoc::Markup::ToHtml.new(RDoc::Options.new, nil)
28
+ rescue ArgumentError
29
+ # RDoc < 4.0
30
+ RDoc::Markup::ToHtml.new
31
+ end
32
+ end
33
+
22
34
  def prepare
23
- markup = RDoc::Markup::ToHtml.new
24
35
  @engine = markup.convert(data)
25
36
  @output = nil
26
37
  end
@@ -28,5 +39,9 @@ module Tilt
28
39
  def evaluate(scope, locals, &block)
29
40
  @output ||= @engine.to_s
30
41
  end
42
+
43
+ def allows_script?
44
+ false
45
+ end
31
46
  end
32
47
  end
@@ -41,6 +41,7 @@ module Tilt
41
41
  [options, line, file].compact.each do |arg|
42
42
  case
43
43
  when arg.respond_to?(:to_str) ; @file = arg.to_str
44
+ when arg.respond_to?(:path) ; @file = arg.path
44
45
  when arg.respond_to?(:to_int) ; @line = arg.to_int
45
46
  when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
46
47
  else raise TypeError
@@ -91,6 +92,15 @@ module Tilt
91
92
  file || '(__TEMPLATE__)'
92
93
  end
93
94
 
95
+ # Whether or not this template engine allows executing Ruby script
96
+ # within the template. If this is false, +scope+ and +locals+ will
97
+ # generally not be used, nor will the provided block be avaiable
98
+ # via +yield+.
99
+ # This should be overridden by template subclasses.
100
+ def allows_script?
101
+ true
102
+ end
103
+
94
104
  protected
95
105
  # Called once and only once for each template subclass the first time
96
106
  # the template class is initialized. This should be used to require the
@@ -98,7 +108,7 @@ module Tilt
98
108
  def initialize_engine
99
109
  end
100
110
 
101
- # Like Kernel::require but issues a warning urging a manual require when
111
+ # Like Kernel#require but issues a warning urging a manual require when
102
112
  # running under a threaded environment.
103
113
  def require_template_library(name)
104
114
  if Thread.list.size > 1
@@ -123,25 +133,15 @@ module Tilt
123
133
  end
124
134
  end
125
135
 
136
+ # Execute the compiled template and return the result string. Template
137
+ # evaluation is guaranteed to be performed in the scope object with the
138
+ # locals specified and with support for yielding to the block.
139
+ #
140
+ # This method is only used by source generating templates. Subclasses that
141
+ # override render() may not support all features.
126
142
  def evaluate(scope, locals, &block)
127
- cached_evaluate(scope, locals, &block)
128
- end
129
-
130
- # Process the template and return the result. The first time this
131
- # method is called, the template source is evaluated with instance_eval.
132
- # On the sequential method calls it will compile the template to an
133
- # unbound method which will lead to better performance. In any case,
134
- # template executation is guaranteed to be performed in the scope object
135
- # with the locals specified and with support for yielding to the block.
136
- def cached_evaluate(scope, locals, &block)
137
- # Redefine itself to use method compilation the next time:
138
- def self.cached_evaluate(scope, locals, &block)
139
- method = compiled_method(locals.keys)
140
- method.bind(scope).call(locals, &block)
141
- end
142
-
143
- # Use instance_eval the first time:
144
- evaluate_source(scope, locals, &block)
143
+ method = compiled_method(locals.keys)
144
+ method.bind(scope).call(locals, &block)
145
145
  end
146
146
 
147
147
  # Generates all template source by combining the preamble, template, and
@@ -186,7 +186,13 @@ module Tilt
186
186
  # source line offset, so adding code to the preamble does not effect line
187
187
  # reporting in Kernel::caller and backtraces.
188
188
  def precompiled_preamble(locals)
189
- locals.map { |k,v| "#{k} = locals[#{k.inspect}]" }.join("\n")
189
+ locals.map do |k,v|
190
+ if k.to_s =~ /\A[a-z]\w*\z/
191
+ "#{k} = locals[#{k.inspect}]"
192
+ else
193
+ raise "invalid locals key: #{k.inspect} (keys must be variable names)"
194
+ end
195
+ end.join("\n")
190
196
  end
191
197
 
192
198
  # Generates postamble code for the precompiled template source. The
@@ -203,34 +209,10 @@ module Tilt
203
209
  end
204
210
 
205
211
  private
206
- # Evaluate the template source in the context of the scope object.
207
- def evaluate_source(scope, locals, &block)
208
- source, offset = precompiled(locals)
209
- scope.instance_eval(source, eval_file, line - offset)
210
- end
211
-
212
- # JRuby doesn't allow Object#instance_eval to yield to the block it's
213
- # closed over. This is by design and (ostensibly) something that will
214
- # change in MRI, though no current MRI version tested (1.8.6 - 1.9.2)
215
- # exhibits the behavior. More info here:
216
- #
217
- # http://jira.codehaus.org/browse/JRUBY-2599
218
- #
219
- # We redefine evaluate_source to work around this issues.
220
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
221
- undef evaluate_source
222
- def evaluate_source(scope, locals, &block)
223
- source, offset = precompiled(locals)
224
- file, lineno = eval_file, (line - offset)
225
- scope.instance_eval { Kernel::eval(source, binding, file, lineno) }
226
- end
227
- end
228
-
229
212
  def compile_template_method(locals)
230
213
  source, offset = precompiled(locals)
231
- offset += 5
232
214
  method_name = "__tilt_#{Thread.current.object_id.abs}"
233
- Object.class_eval <<-RUBY, eval_file, line - offset
215
+ method_source = <<-RUBY
234
216
  #{extract_magic_comment source}
235
217
  TOPOBJECT.class_eval do
236
218
  def #{method_name}(locals)
@@ -238,12 +220,11 @@ module Tilt
238
220
  class << self
239
221
  this, locals = Thread.current[:tilt_vars]
240
222
  this.instance_eval do
241
- #{source}
242
- end
243
- end
244
- end
245
- end
246
223
  RUBY
224
+ offset += method_source.count("\n")
225
+ method_source << source
226
+ method_source << "\nend;end;end;end"
227
+ Object.class_eval method_source, eval_file, line - offset
247
228
  unbind_compiled_method(method_name)
248
229
  end
249
230
 
@@ -255,30 +236,10 @@ module Tilt
255
236
 
256
237
  def extract_magic_comment(script)
257
238
  comment = script.slice(/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/)
258
- return comment if comment and not %w[ascii-8bit binary].include?($1.downcase)
259
- "# coding: #{@default_encoding}" if @default_encoding
260
- end
261
-
262
- # Special case Ruby 1.9.1's broken yield.
263
- #
264
- # http://github.com/rtomayko/tilt/commit/20c01a5
265
- # http://redmine.ruby-lang.org/issues/show/3601
266
- #
267
- # Remove when 1.9.2 dominates 1.9.1 installs in the wild.
268
- if RUBY_VERSION =~ /^1.9.1/
269
- undef compile_template_method
270
- def compile_template_method(locals)
271
- source, offset = precompiled(locals)
272
- offset += 1
273
- method_name = "__tilt_#{Thread.current.object_id}"
274
- Object.class_eval <<-RUBY, eval_file, line - offset
275
- TOPOBJECT.class_eval do
276
- def #{method_name}(locals)
277
- #{source}
278
- end
279
- end
280
- RUBY
281
- unbind_compiled_method(method_name)
239
+ if comment && !%w[ascii-8bit binary].include?($1.downcase)
240
+ comment
241
+ elsif @default_encoding
242
+ "# coding: #{@default_encoding}"
282
243
  end
283
244
  end
284
245
  end
@@ -14,12 +14,17 @@ module Tilt
14
14
 
15
15
  def prepare
16
16
  @engine = RedCloth.new(data)
17
+ options.each {|k, v| @engine.send("#{k}=", v) if @engine.respond_to? "#{k}="}
17
18
  @output = nil
18
19
  end
19
20
 
20
21
  def evaluate(scope, locals, &block)
21
22
  @output ||= @engine.to_html
22
23
  end
24
+
25
+ def allows_script?
26
+ false
27
+ end
23
28
  end
24
29
  end
25
30