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