tilt 1.3.3 → 1.3.4

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