tilt 0.6 → 0.9
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.
- data/COPYING +1 -1
- data/README.md +51 -3
- data/Rakefile +31 -48
- data/TEMPLATES.md +26 -4
- data/bin/tilt +45 -16
- data/lib/tilt.rb +348 -139
- data/test/tilt_buildertemplate_test.rb +1 -1
- data/test/tilt_compilesite_test.rb +86 -0
- data/test/tilt_erbtemplate_test.rb +104 -2
- data/test/tilt_erubistemplate_test.rb +35 -1
- data/test/tilt_hamltemplate_test.rb +65 -1
- data/test/tilt_liquidtemplate_test.rb +1 -1
- data/test/tilt_mustachetemplate_test.rb +1 -4
- data/test/tilt_rdiscounttemplate_test.rb +3 -3
- data/test/tilt_rdoctemplate_test.rb +1 -1
- data/test/tilt_stringtemplate_test.rb +87 -1
- data/test/tilt_template_test.rb +29 -24
- data/test/tilt_test.rb +9 -1
- data/tilt.gemspec +6 -4
- metadata +38 -7
data/lib/tilt.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
require 'digest/md5'
|
|
2
|
+
|
|
1
3
|
module Tilt
|
|
2
|
-
VERSION = '0.
|
|
4
|
+
VERSION = '0.9'
|
|
3
5
|
|
|
4
6
|
@template_mappings = {}
|
|
5
7
|
|
|
6
|
-
# Hash of template path pattern => template implementation
|
|
7
|
-
# class mappings.
|
|
8
|
+
# Hash of template path pattern => template implementation class mappings.
|
|
8
9
|
def self.mappings
|
|
9
10
|
@template_mappings
|
|
10
11
|
end
|
|
@@ -15,6 +16,11 @@ module Tilt
|
|
|
15
16
|
mappings[ext.downcase] = template_class
|
|
16
17
|
end
|
|
17
18
|
|
|
19
|
+
# Returns true when a template exists on an exact match of the provided file extension
|
|
20
|
+
def self.registered?(ext)
|
|
21
|
+
mappings.key?(ext.downcase)
|
|
22
|
+
end
|
|
23
|
+
|
|
18
24
|
# Create a new template for the given file using the file's extension
|
|
19
25
|
# to determine the the template mapping.
|
|
20
26
|
def self.new(file, line=nil, options={}, &block)
|
|
@@ -25,28 +31,35 @@ module Tilt
|
|
|
25
31
|
end
|
|
26
32
|
end
|
|
27
33
|
|
|
28
|
-
# Lookup a template class
|
|
34
|
+
# Lookup a template class for the given filename or file
|
|
29
35
|
# extension. Return nil when no implementation is found.
|
|
30
36
|
def self.[](file)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
else
|
|
36
|
-
while !pattern.empty?
|
|
37
|
-
if @template_mappings.key?(pattern)
|
|
38
|
-
return @template_mappings[pattern]
|
|
39
|
-
else
|
|
40
|
-
pattern = pattern.sub(/^[^.]*\.?/, '')
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
nil
|
|
37
|
+
pattern = file.to_s.downcase
|
|
38
|
+
unless registered?(pattern)
|
|
39
|
+
pattern = File.basename(pattern)
|
|
40
|
+
pattern.sub!(/^[^.]*\.?/, '') until (pattern.empty? || registered?(pattern))
|
|
44
41
|
end
|
|
42
|
+
@template_mappings[pattern]
|
|
45
43
|
end
|
|
46
44
|
|
|
45
|
+
# Mixin allowing template compilation on scope objects.
|
|
46
|
+
#
|
|
47
|
+
# Including this module in scope objects passed to Template#render
|
|
48
|
+
# causes template source to be compiled to methods the first time they're
|
|
49
|
+
# used. This can yield significant (5x-10x) performance increases for
|
|
50
|
+
# templates that support it (ERB, Erubis, Builder).
|
|
51
|
+
#
|
|
52
|
+
# It's also possible (though not recommended) to include this module in
|
|
53
|
+
# Object to enable template compilation globally. The downside is that
|
|
54
|
+
# the template methods will polute the global namespace and could lead to
|
|
55
|
+
# unexpected behavior.
|
|
56
|
+
module CompileSite
|
|
57
|
+
def __tilt__
|
|
58
|
+
end
|
|
59
|
+
end
|
|
47
60
|
|
|
48
61
|
# Base class for template implementations. Subclasses must implement
|
|
49
|
-
# the #
|
|
62
|
+
# the #prepare method and one of the #evaluate or #template_source
|
|
50
63
|
# methods.
|
|
51
64
|
class Template
|
|
52
65
|
# Template source; loaded from a file or given directly.
|
|
@@ -63,52 +76,55 @@ module Tilt
|
|
|
63
76
|
# interface.
|
|
64
77
|
attr_reader :options
|
|
65
78
|
|
|
79
|
+
# Used to determine if this class's initialize_engine method has
|
|
80
|
+
# been called yet.
|
|
81
|
+
@engine_initialized = false
|
|
82
|
+
class << self
|
|
83
|
+
attr_accessor :engine_initialized
|
|
84
|
+
alias engine_initialized? engine_initialized
|
|
85
|
+
end
|
|
86
|
+
|
|
66
87
|
# Create a new template with the file, line, and options specified. By
|
|
67
|
-
# default, template data is read from the file
|
|
68
|
-
#
|
|
69
|
-
#
|
|
88
|
+
# default, template data is read from the file. When a block is given,
|
|
89
|
+
# it should read template data and return as a String. When file is nil,
|
|
90
|
+
# a block is required.
|
|
70
91
|
#
|
|
71
|
-
#
|
|
72
|
-
# time this template subclass has been initialized.
|
|
92
|
+
# All arguments are optional.
|
|
73
93
|
def initialize(file=nil, line=1, options={}, &block)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
94
|
+
@file, @line, @options = nil, 1, {}
|
|
95
|
+
|
|
96
|
+
[options, line, file].compact.each do |arg|
|
|
97
|
+
case
|
|
98
|
+
when arg.respond_to?(:to_str) ; @file = arg.to_str
|
|
99
|
+
when arg.respond_to?(:to_int) ; @line = arg.to_int
|
|
100
|
+
when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
|
|
101
|
+
else raise TypeError
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
raise ArgumentError, "file or block required" if (@file || block).nil?
|
|
106
|
+
|
|
107
|
+
# call the initialize_engine method if this is the very first time
|
|
108
|
+
# an instance of this class has been created.
|
|
109
|
+
if !self.class.engine_initialized?
|
|
83
110
|
initialize_engine
|
|
84
111
|
self.class.engine_initialized = true
|
|
85
112
|
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Called once and only once for each template subclass the first time
|
|
89
|
-
# the template class is initialized. This should be used to require the
|
|
90
|
-
# underlying template library and perform any initial setup.
|
|
91
|
-
def initialize_engine
|
|
92
|
-
end
|
|
93
|
-
@engine_initialized = false
|
|
94
|
-
class << self ; attr_accessor :engine_initialized ; end
|
|
95
113
|
|
|
114
|
+
# used to generate unique method names for template compilation
|
|
115
|
+
@stamp = (Time.now.to_f * 10000).to_i
|
|
116
|
+
@compiled_method_names = {}
|
|
96
117
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if @data.nil?
|
|
102
|
-
@data = @reader.call(self)
|
|
103
|
-
compile!
|
|
104
|
-
end
|
|
118
|
+
# load template data and prepare
|
|
119
|
+
@reader = block || lambda { |t| File.read(@file) }
|
|
120
|
+
@data = @reader.call(self)
|
|
121
|
+
prepare
|
|
105
122
|
end
|
|
106
123
|
|
|
107
124
|
# Render the template in the given scope with the locals specified. If a
|
|
108
125
|
# block is given, it is typically available within the template via
|
|
109
126
|
# +yield+.
|
|
110
127
|
def render(scope=Object.new, locals={}, &block)
|
|
111
|
-
compile
|
|
112
128
|
evaluate scope, locals || {}, &block
|
|
113
129
|
end
|
|
114
130
|
|
|
@@ -128,43 +144,167 @@ module Tilt
|
|
|
128
144
|
end
|
|
129
145
|
|
|
130
146
|
protected
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
147
|
+
# Called once and only once for each template subclass the first time
|
|
148
|
+
# the template class is initialized. This should be used to require the
|
|
149
|
+
# underlying template library and perform any initial setup.
|
|
150
|
+
def initialize_engine
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Like Kernel::require but issues a warning urging a manual require when
|
|
154
|
+
# running under a threaded environment.
|
|
155
|
+
def require_template_library(name)
|
|
156
|
+
if Thread.list.size > 1
|
|
157
|
+
warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
|
|
158
|
+
"explicit require '#{name}' suggested."
|
|
159
|
+
end
|
|
160
|
+
require name
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Do whatever preparation is necessary to setup the underlying template
|
|
164
|
+
# engine. Called immediately after template data is loaded. Instance
|
|
165
|
+
# variables set in this method are available when #evaluate is called.
|
|
134
166
|
#
|
|
135
167
|
# Subclasses must provide an implementation of this method.
|
|
136
|
-
def
|
|
137
|
-
|
|
168
|
+
def prepare
|
|
169
|
+
if respond_to?(:compile!)
|
|
170
|
+
# backward compat with tilt < 0.6; just in case
|
|
171
|
+
warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.'
|
|
172
|
+
compile!
|
|
173
|
+
else
|
|
174
|
+
raise NotImplementedError
|
|
175
|
+
end
|
|
138
176
|
end
|
|
139
177
|
|
|
140
|
-
# Process the template and return the result.
|
|
141
|
-
#
|
|
178
|
+
# Process the template and return the result. When the scope mixes in
|
|
179
|
+
# the Tilt::CompileSite module, the template is compiled to a method and
|
|
180
|
+
# reused given identical locals keys. When the scope object
|
|
181
|
+
# does not mix in the CompileSite module, the template source is
|
|
182
|
+
# evaluated with instance_eval. In any case, template executation
|
|
183
|
+
# is guaranteed to be performed in the scope object with the locals
|
|
184
|
+
# specified and with support for yielding to the block.
|
|
142
185
|
def evaluate(scope, locals, &block)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
186
|
+
if scope.respond_to?(:__tilt__)
|
|
187
|
+
method_name = compiled_method_name(locals.keys)
|
|
188
|
+
if scope.respond_to?(method_name)
|
|
189
|
+
scope.send(method_name, locals, &block)
|
|
190
|
+
else
|
|
191
|
+
compile_template_method(method_name, locals)
|
|
192
|
+
scope.send(method_name, locals, &block)
|
|
193
|
+
end
|
|
194
|
+
else
|
|
195
|
+
evaluate_source(scope, locals, &block)
|
|
196
|
+
end
|
|
146
197
|
end
|
|
147
198
|
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
|
|
199
|
+
# Generates all template source by combining the preamble, template, and
|
|
200
|
+
# postamble and returns a two-tuple of the form: [source, offset], where
|
|
201
|
+
# source is the string containing (Ruby) source code for the template and
|
|
202
|
+
# offset is the integer line offset where line reporting should begin.
|
|
203
|
+
#
|
|
204
|
+
# Template subclasses may override this method when they need complete
|
|
205
|
+
# control over source generation or want to adjust the default line
|
|
206
|
+
# offset. In most cases, overriding the #precompiled_template method is
|
|
207
|
+
# easier and more appropriate.
|
|
208
|
+
def precompiled(locals)
|
|
209
|
+
preamble = precompiled_preamble(locals)
|
|
210
|
+
parts = [
|
|
211
|
+
preamble,
|
|
212
|
+
precompiled_template(locals),
|
|
213
|
+
precompiled_postamble(locals)
|
|
214
|
+
]
|
|
215
|
+
[parts.join("\n"), preamble.count("\n") + 1]
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# A string containing the (Ruby) source code for the template. The
|
|
219
|
+
# default Template#evaluate implementation requires either this method
|
|
220
|
+
# or the #precompiled method be overridden. When defined, the base
|
|
221
|
+
# Template guarantees correct file/line handling, locals support, custom
|
|
222
|
+
# scopes, and support for template compilation when the scope object
|
|
223
|
+
# allows it.
|
|
224
|
+
def precompiled_template(locals)
|
|
152
225
|
raise NotImplementedError
|
|
153
226
|
end
|
|
154
227
|
|
|
228
|
+
# Generates preamble code for initializing template state, and performing
|
|
229
|
+
# locals assignment. The default implementation performs locals
|
|
230
|
+
# assignment only. Lines included in the preamble are subtracted from the
|
|
231
|
+
# source line offset, so adding code to the preamble does not effect line
|
|
232
|
+
# reporting in Kernel::caller and backtraces.
|
|
233
|
+
def precompiled_preamble(locals)
|
|
234
|
+
locals.map { |k,v| "#{k} = locals[:#{k}]" }.join("\n")
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Generates postamble code for the precompiled template source. The
|
|
238
|
+
# string returned from this method is appended to the precompiled
|
|
239
|
+
# template source.
|
|
240
|
+
def precompiled_postamble(locals)
|
|
241
|
+
''
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# The unique compiled method name for the locals keys provided.
|
|
245
|
+
def compiled_method_name(locals_keys)
|
|
246
|
+
@compiled_method_names[locals_keys] ||=
|
|
247
|
+
generate_compiled_method_name(locals_keys)
|
|
248
|
+
end
|
|
249
|
+
|
|
155
250
|
private
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
source
|
|
159
|
-
|
|
251
|
+
# Evaluate the template source in the context of the scope object.
|
|
252
|
+
def evaluate_source(scope, locals, &block)
|
|
253
|
+
source, offset = precompiled(locals)
|
|
254
|
+
scope.instance_eval(source, eval_file, line - offset)
|
|
160
255
|
end
|
|
161
256
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
257
|
+
# JRuby doesn't allow Object#instance_eval to yield to the block it's
|
|
258
|
+
# closed over. This is by design and (ostensibly) something that will
|
|
259
|
+
# change in MRI, though no current MRI version tested (1.8.6 - 1.9.2)
|
|
260
|
+
# exhibits the behavior. More info here:
|
|
261
|
+
#
|
|
262
|
+
# http://jira.codehaus.org/browse/JRUBY-2599
|
|
263
|
+
#
|
|
264
|
+
# Additionally, JRuby's eval line reporting is off by one compared to
|
|
265
|
+
# all MRI versions tested.
|
|
266
|
+
#
|
|
267
|
+
# We redefine evaluate_source to work around both issues.
|
|
268
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
|
269
|
+
undef evaluate_source
|
|
270
|
+
def evaluate_source(scope, locals, &block)
|
|
271
|
+
source, offset = precompiled(locals)
|
|
272
|
+
file, lineno = eval_file, (line - offset) - 1
|
|
273
|
+
scope.instance_eval { Kernel::eval(source, binding, file, lineno) }
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def generate_compiled_method_name(locals_keys)
|
|
278
|
+
parts = [object_id, @stamp] + locals_keys.map { |k| k.to_s }.sort
|
|
279
|
+
digest = Digest::MD5.hexdigest(parts.join(':'))
|
|
280
|
+
"__tilt_#{digest}"
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def compile_template_method(method_name, locals)
|
|
284
|
+
source, offset = precompiled(locals)
|
|
285
|
+
offset += 1
|
|
286
|
+
CompileSite.module_eval <<-RUBY, eval_file, line - offset
|
|
287
|
+
def #{method_name}(locals)
|
|
288
|
+
#{source}
|
|
289
|
+
end
|
|
290
|
+
RUBY
|
|
291
|
+
|
|
292
|
+
ObjectSpace.define_finalizer self,
|
|
293
|
+
Template.compiled_template_method_remover(CompileSite, method_name)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def self.compiled_template_method_remover(site, method_name)
|
|
297
|
+
proc { |oid| garbage_collect_compiled_template_method(site, method_name) }
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def self.garbage_collect_compiled_template_method(site, method_name)
|
|
301
|
+
site.module_eval do
|
|
302
|
+
begin
|
|
303
|
+
remove_method(method_name)
|
|
304
|
+
rescue NameError
|
|
305
|
+
# method was already removed (ruby >= 1.9)
|
|
306
|
+
end
|
|
166
307
|
end
|
|
167
|
-
require name
|
|
168
308
|
end
|
|
169
309
|
end
|
|
170
310
|
|
|
@@ -174,7 +314,7 @@ module Tilt
|
|
|
174
314
|
# cache = Tilt::Cache.new
|
|
175
315
|
# cache.fetch(path, line, options) { Tilt.new(path, line, options) }
|
|
176
316
|
#
|
|
177
|
-
# Subsequent invocations return the already
|
|
317
|
+
# Subsequent invocations return the already loaded template object.
|
|
178
318
|
class Cache
|
|
179
319
|
def initialize
|
|
180
320
|
@cache = {}
|
|
@@ -196,11 +336,11 @@ module Tilt
|
|
|
196
336
|
# The template source is evaluated as a Ruby string. The #{} interpolation
|
|
197
337
|
# syntax can be used to generated dynamic output.
|
|
198
338
|
class StringTemplate < Template
|
|
199
|
-
def
|
|
339
|
+
def prepare
|
|
200
340
|
@code = "%Q{#{data}}"
|
|
201
341
|
end
|
|
202
342
|
|
|
203
|
-
def
|
|
343
|
+
def precompiled_template(locals)
|
|
204
344
|
@code
|
|
205
345
|
end
|
|
206
346
|
end
|
|
@@ -211,65 +351,87 @@ module Tilt
|
|
|
211
351
|
# http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
|
|
212
352
|
class ERBTemplate < Template
|
|
213
353
|
def initialize_engine
|
|
214
|
-
|
|
354
|
+
return if defined? ::ERB
|
|
355
|
+
require_template_library 'erb'
|
|
215
356
|
end
|
|
216
357
|
|
|
217
|
-
def
|
|
218
|
-
@
|
|
358
|
+
def prepare
|
|
359
|
+
@outvar = (options[:outvar] || '_erbout').to_s
|
|
360
|
+
@engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
|
|
219
361
|
end
|
|
220
362
|
|
|
221
|
-
def
|
|
363
|
+
def precompiled_template(locals)
|
|
222
364
|
@engine.src
|
|
223
365
|
end
|
|
224
366
|
|
|
225
|
-
def
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
scope.instance_variable_get(:@_out_buf)
|
|
232
|
-
|
|
233
|
-
scope.instance_eval source, eval_file, line - offset
|
|
234
|
-
|
|
235
|
-
output = scope.instance_variable_get(:@_out_buf)
|
|
236
|
-
scope.instance_variable_set(:@_out_buf, original_out_buf)
|
|
237
|
-
|
|
238
|
-
output
|
|
367
|
+
def precompiled_preamble(locals)
|
|
368
|
+
<<-RUBY
|
|
369
|
+
begin
|
|
370
|
+
__original_outvar = #{@outvar} if defined?(#{@outvar})
|
|
371
|
+
#{super}
|
|
372
|
+
RUBY
|
|
239
373
|
end
|
|
240
374
|
|
|
241
|
-
|
|
375
|
+
def precompiled_postamble(locals)
|
|
376
|
+
<<-RUBY
|
|
377
|
+
#{super}
|
|
378
|
+
ensure
|
|
379
|
+
#{@outvar} = __original_outvar
|
|
380
|
+
end
|
|
381
|
+
RUBY
|
|
382
|
+
end
|
|
242
383
|
|
|
243
384
|
# ERB generates a line to specify the character coding of the generated
|
|
244
385
|
# source in 1.9. Account for this in the line offset.
|
|
245
386
|
if RUBY_VERSION >= '1.9.0'
|
|
246
|
-
def
|
|
387
|
+
def precompiled(locals)
|
|
247
388
|
source, offset = super
|
|
248
389
|
[source, offset + 1]
|
|
249
390
|
end
|
|
250
391
|
end
|
|
251
392
|
end
|
|
393
|
+
|
|
252
394
|
%w[erb rhtml].each { |ext| register ext, ERBTemplate }
|
|
253
395
|
|
|
254
396
|
|
|
255
397
|
# Erubis template implementation. See:
|
|
256
398
|
# http://www.kuwata-lab.com/erubis/
|
|
399
|
+
#
|
|
400
|
+
# ErubisTemplate supports the following additional options, which are not
|
|
401
|
+
# passed down to the Erubis engine:
|
|
402
|
+
#
|
|
403
|
+
# :engine_class allows you to specify a custom engine class to use
|
|
404
|
+
# instead of the default (which is ::Erubis::Eruby).
|
|
405
|
+
#
|
|
406
|
+
# :escape_html when true, ::Erubis::EscapedEruby will be used as
|
|
407
|
+
# the engine class instead of the default. All content
|
|
408
|
+
# within <%= %> blocks will be automatically html escaped.
|
|
257
409
|
class ErubisTemplate < ERBTemplate
|
|
258
410
|
def initialize_engine
|
|
259
|
-
|
|
411
|
+
return if defined? ::Erubis
|
|
412
|
+
require_template_library 'erubis'
|
|
260
413
|
end
|
|
261
414
|
|
|
262
|
-
def
|
|
263
|
-
|
|
264
|
-
@
|
|
415
|
+
def prepare
|
|
416
|
+
@options.merge!(:preamble => false, :postamble => false)
|
|
417
|
+
@outvar = (options.delete(:outvar) || '_erbout').to_s
|
|
418
|
+
engine_class = options.delete(:engine_class)
|
|
419
|
+
engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
|
|
420
|
+
@engine = (engine_class || ::Erubis::Eruby).new(data, options)
|
|
265
421
|
end
|
|
266
422
|
|
|
267
|
-
|
|
423
|
+
def precompiled_preamble(locals)
|
|
424
|
+
[super, "#{@outvar} = _buf = ''"].join("\n")
|
|
425
|
+
end
|
|
268
426
|
|
|
269
|
-
|
|
270
|
-
|
|
427
|
+
def precompiled_postamble(locals)
|
|
428
|
+
["_buf", super].join("\n")
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
|
|
432
|
+
# Override and adjust back.
|
|
271
433
|
if RUBY_VERSION >= '1.9.0'
|
|
272
|
-
def
|
|
434
|
+
def precompiled(locals)
|
|
273
435
|
source, offset = super
|
|
274
436
|
[source, offset - 1]
|
|
275
437
|
end
|
|
@@ -282,20 +444,54 @@ module Tilt
|
|
|
282
444
|
# http://haml.hamptoncatlin.com/
|
|
283
445
|
class HamlTemplate < Template
|
|
284
446
|
def initialize_engine
|
|
285
|
-
|
|
447
|
+
return if defined? ::Haml::Engine
|
|
448
|
+
require_template_library 'haml'
|
|
286
449
|
end
|
|
287
450
|
|
|
288
|
-
def
|
|
289
|
-
|
|
451
|
+
def prepare
|
|
452
|
+
options = @options.merge(:filename => eval_file, :line => line)
|
|
453
|
+
@engine = ::Haml::Engine.new(data, options)
|
|
290
454
|
end
|
|
291
455
|
|
|
292
456
|
def evaluate(scope, locals, &block)
|
|
293
|
-
@engine.
|
|
457
|
+
if @engine.respond_to?(:precompiled_method_return_value, true)
|
|
458
|
+
super
|
|
459
|
+
else
|
|
460
|
+
@engine.render(scope, locals, &block)
|
|
461
|
+
end
|
|
294
462
|
end
|
|
295
463
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
464
|
+
# Precompiled Haml source. Taken from the precompiled_with_ambles
|
|
465
|
+
# method in Haml::Precompiler:
|
|
466
|
+
# http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126
|
|
467
|
+
def precompiled_template(locals)
|
|
468
|
+
@engine.precompiled
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def precompiled_preamble(locals)
|
|
472
|
+
local_assigns = super
|
|
473
|
+
@engine.instance_eval do
|
|
474
|
+
<<-RUBY
|
|
475
|
+
begin
|
|
476
|
+
extend Haml::Helpers
|
|
477
|
+
_hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})
|
|
478
|
+
_erbout = _hamlout.buffer
|
|
479
|
+
__in_erb_template = true
|
|
480
|
+
_haml_locals = locals
|
|
481
|
+
#{local_assigns}
|
|
482
|
+
RUBY
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def precompiled_postamble(locals)
|
|
487
|
+
@engine.instance_eval do
|
|
488
|
+
<<-RUBY
|
|
489
|
+
#{precompiled_method_return_value}
|
|
490
|
+
ensure
|
|
491
|
+
@haml_buffer = @haml_buffer.upper
|
|
492
|
+
end
|
|
493
|
+
RUBY
|
|
494
|
+
end
|
|
299
495
|
end
|
|
300
496
|
end
|
|
301
497
|
register 'haml', HamlTemplate
|
|
@@ -307,15 +503,16 @@ module Tilt
|
|
|
307
503
|
# Sass templates do not support object scopes, locals, or yield.
|
|
308
504
|
class SassTemplate < Template
|
|
309
505
|
def initialize_engine
|
|
310
|
-
|
|
506
|
+
return if defined? ::Sass::Engine
|
|
507
|
+
require_template_library 'sass'
|
|
311
508
|
end
|
|
312
509
|
|
|
313
|
-
def
|
|
510
|
+
def prepare
|
|
314
511
|
@engine = ::Sass::Engine.new(data, sass_options)
|
|
315
512
|
end
|
|
316
513
|
|
|
317
514
|
def evaluate(scope, locals, &block)
|
|
318
|
-
@engine.render
|
|
515
|
+
@output ||= @engine.render
|
|
319
516
|
end
|
|
320
517
|
|
|
321
518
|
private
|
|
@@ -332,10 +529,11 @@ module Tilt
|
|
|
332
529
|
# Less templates do not support object scopes, locals, or yield.
|
|
333
530
|
class LessTemplate < Template
|
|
334
531
|
def initialize_engine
|
|
335
|
-
|
|
532
|
+
return if defined? ::Less::Engine
|
|
533
|
+
require_template_library 'less'
|
|
336
534
|
end
|
|
337
535
|
|
|
338
|
-
def
|
|
536
|
+
def prepare
|
|
339
537
|
@engine = ::Less::Engine.new(data)
|
|
340
538
|
end
|
|
341
539
|
|
|
@@ -345,14 +543,16 @@ module Tilt
|
|
|
345
543
|
end
|
|
346
544
|
register 'less', LessTemplate
|
|
347
545
|
|
|
546
|
+
|
|
348
547
|
# Builder template implementation. See:
|
|
349
548
|
# http://builder.rubyforge.org/
|
|
350
549
|
class BuilderTemplate < Template
|
|
351
550
|
def initialize_engine
|
|
352
|
-
|
|
551
|
+
return if defined?(::Builder)
|
|
552
|
+
require_template_library 'builder'
|
|
353
553
|
end
|
|
354
554
|
|
|
355
|
-
def
|
|
555
|
+
def prepare
|
|
356
556
|
end
|
|
357
557
|
|
|
358
558
|
def evaluate(scope, locals, &block)
|
|
@@ -366,7 +566,7 @@ module Tilt
|
|
|
366
566
|
xml.target!
|
|
367
567
|
end
|
|
368
568
|
|
|
369
|
-
def
|
|
569
|
+
def precompiled_template(locals)
|
|
370
570
|
data.to_str
|
|
371
571
|
end
|
|
372
572
|
end
|
|
@@ -388,10 +588,11 @@ module Tilt
|
|
|
388
588
|
# time when using this template engine.
|
|
389
589
|
class LiquidTemplate < Template
|
|
390
590
|
def initialize_engine
|
|
391
|
-
|
|
591
|
+
return if defined? ::Liquid::Template
|
|
592
|
+
require_template_library 'liquid'
|
|
392
593
|
end
|
|
393
594
|
|
|
394
|
-
def
|
|
595
|
+
def prepare
|
|
395
596
|
@engine = ::Liquid::Template.parse(data)
|
|
396
597
|
end
|
|
397
598
|
|
|
@@ -421,15 +622,17 @@ module Tilt
|
|
|
421
622
|
end
|
|
422
623
|
|
|
423
624
|
def initialize_engine
|
|
424
|
-
|
|
625
|
+
return if defined? ::RDiscount
|
|
626
|
+
require_template_library 'rdiscount'
|
|
425
627
|
end
|
|
426
628
|
|
|
427
|
-
def
|
|
629
|
+
def prepare
|
|
428
630
|
@engine = RDiscount.new(data, *flags)
|
|
631
|
+
@output = nil
|
|
429
632
|
end
|
|
430
633
|
|
|
431
634
|
def evaluate(scope, locals, &block)
|
|
432
|
-
@engine.to_html
|
|
635
|
+
@output ||= @engine.to_html
|
|
433
636
|
end
|
|
434
637
|
end
|
|
435
638
|
register 'markdown', RDiscountTemplate
|
|
@@ -441,15 +644,17 @@ module Tilt
|
|
|
441
644
|
# http://redcloth.org/
|
|
442
645
|
class RedClothTemplate < Template
|
|
443
646
|
def initialize_engine
|
|
444
|
-
|
|
647
|
+
return if defined? ::RedCloth
|
|
648
|
+
require_template_library 'redcloth'
|
|
445
649
|
end
|
|
446
650
|
|
|
447
|
-
def
|
|
651
|
+
def prepare
|
|
448
652
|
@engine = RedCloth.new(data)
|
|
653
|
+
@output = nil
|
|
449
654
|
end
|
|
450
655
|
|
|
451
656
|
def evaluate(scope, locals, &block)
|
|
452
|
-
@engine.to_html
|
|
657
|
+
@output ||= @engine.to_html
|
|
453
658
|
end
|
|
454
659
|
end
|
|
455
660
|
register 'textile', RedClothTemplate
|
|
@@ -465,10 +670,11 @@ module Tilt
|
|
|
465
670
|
attr_reader :engine
|
|
466
671
|
|
|
467
672
|
def initialize_engine
|
|
468
|
-
|
|
673
|
+
return if defined? ::Mustache
|
|
674
|
+
require_template_library 'mustache'
|
|
469
675
|
end
|
|
470
676
|
|
|
471
|
-
def
|
|
677
|
+
def prepare
|
|
472
678
|
Mustache.view_namespace = options[:namespace]
|
|
473
679
|
Mustache.view_path = options[:view_path] || options[:mustaches]
|
|
474
680
|
@engine = options[:view] || Mustache.view_class(name)
|
|
@@ -502,6 +708,7 @@ module Tilt
|
|
|
502
708
|
end
|
|
503
709
|
register 'mustache', MustacheTemplate
|
|
504
710
|
|
|
711
|
+
|
|
505
712
|
# RDoc template. See:
|
|
506
713
|
# http://rdoc.rubyforge.org/
|
|
507
714
|
#
|
|
@@ -510,36 +717,38 @@ module Tilt
|
|
|
510
717
|
# engine.
|
|
511
718
|
class RDocTemplate < Template
|
|
512
719
|
def initialize_engine
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
end
|
|
720
|
+
return if defined?(::RDoc::Markup)
|
|
721
|
+
require_template_library 'rdoc/markup'
|
|
722
|
+
require_template_library 'rdoc/markup/to_html'
|
|
517
723
|
end
|
|
518
724
|
|
|
519
|
-
def
|
|
725
|
+
def prepare
|
|
520
726
|
markup = RDoc::Markup::ToHtml.new
|
|
521
727
|
@engine = markup.convert(data)
|
|
728
|
+
@output = nil
|
|
522
729
|
end
|
|
523
730
|
|
|
524
731
|
def evaluate(scope, locals, &block)
|
|
525
|
-
@engine.to_s
|
|
732
|
+
@output ||= @engine.to_s
|
|
526
733
|
end
|
|
527
734
|
end
|
|
528
735
|
register 'rdoc', RDocTemplate
|
|
529
736
|
|
|
737
|
+
|
|
530
738
|
# CoffeeScript info:
|
|
531
739
|
# http://jashkenas.github.com/coffee-script/
|
|
532
740
|
class CoffeeTemplate < Template
|
|
533
741
|
def initialize_engine
|
|
534
|
-
|
|
742
|
+
return if defined? ::CoffeeScript
|
|
743
|
+
require_template_library 'coffee-script'
|
|
535
744
|
end
|
|
536
745
|
|
|
537
|
-
def
|
|
538
|
-
@
|
|
746
|
+
def prepare
|
|
747
|
+
@output = nil
|
|
539
748
|
end
|
|
540
749
|
|
|
541
750
|
def evaluate(scope, locals, &block)
|
|
542
|
-
@
|
|
751
|
+
@output ||= ::CoffeeScript::compile(data, options)
|
|
543
752
|
end
|
|
544
753
|
end
|
|
545
754
|
register 'coffee', CoffeeTemplate
|