tilt 0.7 → 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 +45 -3
- data/bin/tilt +45 -16
- data/lib/tilt.rb +181 -94
- data/test/tilt_compilesite_test.rb +28 -4
- data/test/tilt_erbtemplate_test.rb +1 -1
- data/test/tilt_hamltemplate_test.rb +11 -0
- data/test/tilt_stringtemplate_test.rb +1 -1
- data/test/tilt_template_test.rb +1 -1
- data/tilt.gemspec +4 -4
- metadata +25 -7
data/COPYING
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2010 Ryan Tomayko <http://tomayko.com/about>
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
of this software and associated documentation files (the "Software"), to
|
data/README.md
CHANGED
@@ -13,7 +13,8 @@ feature is relevant to the engine):
|
|
13
13
|
* Ability to pass locals to template evaluation
|
14
14
|
* Support for passing a block to template evaluation for "yield"
|
15
15
|
* Backtraces with correct filenames and line numbers
|
16
|
-
* Template
|
16
|
+
* Template file caching and reloading
|
17
|
+
* Fast, method-based template source compilation
|
17
18
|
|
18
19
|
The primary goal is to get all of the things listed above right for all
|
19
20
|
template engines included in the distribution.
|
@@ -145,8 +146,49 @@ Or, use BlueCloth for markdown instead of RDiscount:
|
|
145
146
|
|
146
147
|
Tilt.register 'markdown', Tilt::BlueClothTemplate
|
147
148
|
|
149
|
+
Template Compilation
|
150
|
+
--------------------
|
151
|
+
|
152
|
+
Tilt can compile generated Ruby source code produced by template engines and
|
153
|
+
reuse on subsequent template invocations. Benchmarks show this yields a 5x-10x
|
154
|
+
performance increase over evaluating the Ruby source on each invocation.
|
155
|
+
|
156
|
+
Template compilation is currently supported for these template engines:
|
157
|
+
StringTemplate, ERB, Erubis, Haml, and Builder.
|
158
|
+
|
159
|
+
To enable template compilation, the `Tilt::CompileSite` module must be mixed in
|
160
|
+
to the scope object passed to the template's `#render` method. This can be
|
161
|
+
accomplished by including (with `Module#include`) the module in the class used
|
162
|
+
for scope objects or by extending (with `Object#extend`) scope objects before
|
163
|
+
passing to `Template#render`:
|
164
|
+
|
165
|
+
require 'tilt'
|
166
|
+
|
167
|
+
template = Tilt::ERBTemplate.new('foo.erb')
|
168
|
+
|
169
|
+
# Slow. Uses Object#instance_eval to process template
|
170
|
+
class Scope
|
171
|
+
end
|
172
|
+
scope = Scope.new
|
173
|
+
template.render(scope)
|
174
|
+
|
175
|
+
# Fast. Uses compiled template and Object#send to process template
|
176
|
+
class Scope
|
177
|
+
include Tilt::CompileSite
|
178
|
+
end
|
179
|
+
scope = Scope.new
|
180
|
+
template.render(scope)
|
181
|
+
|
182
|
+
# Also fast, though a bit a slower due to having to extend each time
|
183
|
+
scope = Object.new
|
184
|
+
scope.extend Tilt::CompileSite
|
185
|
+
template.render(scope)
|
186
|
+
|
187
|
+
When the `Tilt::CompileSite` module is not present, template execution falls
|
188
|
+
back to evaluating the template from source on each invocation.
|
189
|
+
|
148
190
|
LICENSE
|
149
191
|
-------
|
150
192
|
|
151
|
-
Tilt is Copyright (c)
|
152
|
-
distributed under the MIT license. See the COPYING file for more info.
|
193
|
+
Tilt is Copyright (c) 2010 [Ryan Tomayko](http://tomayko.com/about) and
|
194
|
+
distributed under the MIT license. See the `COPYING` file for more info.
|
data/bin/tilt
CHANGED
@@ -3,17 +3,21 @@ require 'ostruct'
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'tilt'
|
5
5
|
|
6
|
-
usage = <<
|
7
|
-
Usage: tilt
|
8
|
-
Process template
|
9
|
-
is '-', read template from stdin and use the --type option
|
10
|
-
the template's type.
|
6
|
+
usage = <<USAGE
|
7
|
+
Usage: tilt <options> <file>
|
8
|
+
Process template <file> and write output to stdout. With no <file> or
|
9
|
+
when <file> is '-', read template from stdin and use the --type option
|
10
|
+
to determine the template's type.
|
11
11
|
|
12
12
|
Options
|
13
|
-
-l, --list List
|
14
|
-
-t, --type
|
13
|
+
-l, --list List template engines + file patterns and exit
|
14
|
+
-t, --type=<pattern> Use this template engine; required if no <file>
|
15
|
+
-y, --layout=<file> Use <file> as a layout template
|
15
16
|
|
16
|
-
-
|
17
|
+
-D<name>=<value> Define variable <name> as <value>
|
18
|
+
-o, --vars=<ruby> Evaluate <ruby> to Hash and use for variables
|
19
|
+
|
20
|
+
-h, --help Show this help message
|
17
21
|
|
18
22
|
Convert markdown to HTML:
|
19
23
|
$ tilt foo.markdown > foo.html
|
@@ -21,11 +25,18 @@ Convert markdown to HTML:
|
|
21
25
|
Process ERB template:
|
22
26
|
$ echo "Answer: <%= 2 + 2 %>" | tilt -t erb
|
23
27
|
Answer: 4
|
24
|
-
|
28
|
+
|
29
|
+
Define variables:
|
30
|
+
$ echo "Answer: <%= 2 + n %>" | tilt --locals="{:n=>40, :x=>0}"
|
31
|
+
Answer: 42
|
32
|
+
$ echo "Answer: <%= 2 + n %>" | tilt -Dn=40 -Dx=0
|
33
|
+
Answer: 42
|
34
|
+
USAGE
|
25
35
|
|
26
36
|
script_name = File.basename($0)
|
27
37
|
pattern = nil
|
28
38
|
layout = nil
|
39
|
+
locals = {}
|
29
40
|
|
30
41
|
ARGV.options do |o|
|
31
42
|
o.program_name = script_name
|
@@ -44,18 +55,31 @@ ARGV.options do |o|
|
|
44
55
|
end
|
45
56
|
|
46
57
|
# the template type / pattern
|
47
|
-
o.on("-t", "--type=PATTERN") do |val|
|
48
|
-
|
58
|
+
o.on("-t", "--type=PATTERN", String) do |val|
|
59
|
+
abort "unknown template type: #{val}" if Tilt[val].nil?
|
49
60
|
pattern = val
|
50
61
|
end
|
51
62
|
|
52
63
|
# pass template output into the specified layout template
|
53
|
-
o.on("-y", "--layout=FILE") do |file|
|
64
|
+
o.on("-y", "--layout=FILE", String) do |file|
|
54
65
|
paths = [file, "~/.tilt/#{file}", "/etc/tilt/#{file}"]
|
55
66
|
layout = paths.
|
56
67
|
map { |p| File.expand_path(p) }.
|
57
68
|
find { |p| File.exist?(p) }
|
58
|
-
|
69
|
+
abort "no such layout: #{file}" if layout.nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
# define a local variable
|
73
|
+
o.on("-D", "--define=PAIR", String) do |pair|
|
74
|
+
key, value = pair.split(/[=:]/, 2)
|
75
|
+
locals[key.to_sym] = value
|
76
|
+
end
|
77
|
+
|
78
|
+
# define local variables using a Ruby hash
|
79
|
+
o.on("--vars=RUBY") do |ruby|
|
80
|
+
hash = eval(ruby)
|
81
|
+
abort "vars must be a Hash, not #{hash.inspect}" if !hash.is_a?(Hash)
|
82
|
+
hash.each { |key, value| locals[key.to_sym] = value }
|
59
83
|
end
|
60
84
|
|
61
85
|
o.on_tail("-h", "--help") { puts usage; exit }
|
@@ -65,17 +89,22 @@ end
|
|
65
89
|
|
66
90
|
file = ARGV.first || '-'
|
67
91
|
pattern = file if pattern.nil?
|
92
|
+
abort "template type not given. see: #{$0} --help" if ['-', ''].include?(pattern)
|
93
|
+
|
94
|
+
engine = Tilt[pattern]
|
95
|
+
abort "template engine not found for: #{pattern}" if engine.nil?
|
96
|
+
|
68
97
|
template =
|
69
|
-
|
98
|
+
engine.new(file) {
|
70
99
|
if file == '-'
|
71
100
|
$stdin.read
|
72
101
|
else
|
73
102
|
File.read(file)
|
74
103
|
end
|
75
104
|
}
|
76
|
-
output = template.render
|
105
|
+
output = template.render(self, locals)
|
77
106
|
|
78
107
|
# process layout
|
79
|
-
output = Tilt.new(layout).render { output } if layout
|
108
|
+
output = Tilt.new(layout).render(self, locals) { output } if layout
|
80
109
|
|
81
110
|
$stdout.write(output)
|
data/lib/tilt.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
1
3
|
module Tilt
|
2
|
-
VERSION = '0.
|
4
|
+
VERSION = '0.8'
|
3
5
|
|
4
6
|
@template_mappings = {}
|
5
7
|
|
@@ -113,8 +115,8 @@ module Tilt
|
|
113
115
|
end
|
114
116
|
|
115
117
|
# used to generate unique method names for template compilation
|
116
|
-
stamp = (Time.now.to_f * 10000).to_i
|
117
|
-
@
|
118
|
+
@stamp = (Time.now.to_f * 10000).to_i
|
119
|
+
@compiled_method_names = {}
|
118
120
|
|
119
121
|
# load template data and prepare
|
120
122
|
@reader = block || lambda { |t| File.read(@file) }
|
@@ -151,6 +153,16 @@ module Tilt
|
|
151
153
|
def initialize_engine
|
152
154
|
end
|
153
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
|
+
|
154
166
|
# Do whatever preparation is necessary to setup the underlying template
|
155
167
|
# engine. Called immediately after template data is loaded. Instance
|
156
168
|
# variables set in this method are available when #evaluate is called.
|
@@ -175,54 +187,111 @@ module Tilt
|
|
175
187
|
# specified and with support for yielding to the block.
|
176
188
|
def evaluate(scope, locals, &block)
|
177
189
|
if scope.respond_to?(:__tilt__)
|
178
|
-
method_name = compiled_method_name(locals.keys
|
190
|
+
method_name = compiled_method_name(locals.keys)
|
179
191
|
if scope.respond_to?(method_name)
|
180
|
-
|
181
|
-
scope.send method_name, locals, &block
|
192
|
+
scope.send(method_name, locals, &block)
|
182
193
|
else
|
183
|
-
# compile first and then run
|
184
194
|
compile_template_method(method_name, locals)
|
185
|
-
scope.send
|
195
|
+
scope.send(method_name, locals, &block)
|
186
196
|
end
|
187
197
|
else
|
188
|
-
|
189
|
-
source = [source, template_source].join("\n")
|
190
|
-
scope.instance_eval source, eval_file, line - offset
|
198
|
+
evaluate_source(scope, locals, &block)
|
191
199
|
end
|
192
200
|
end
|
193
201
|
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
|
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)
|
199
228
|
raise NotImplementedError
|
200
229
|
end
|
201
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
|
+
|
202
253
|
private
|
203
|
-
|
204
|
-
|
205
|
-
source
|
206
|
-
|
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)
|
207
258
|
end
|
208
259
|
|
209
|
-
|
210
|
-
|
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}"
|
211
284
|
end
|
212
285
|
|
213
286
|
def compile_template_method(method_name, locals)
|
214
|
-
source, offset =
|
215
|
-
source = [source, template_source].join("\n")
|
287
|
+
source, offset = precompiled(locals)
|
216
288
|
offset += 1
|
217
|
-
|
218
|
-
# add the new method
|
219
289
|
CompileSite.module_eval <<-RUBY, eval_file, line - offset
|
220
290
|
def #{method_name}(locals)
|
221
291
|
#{source}
|
222
292
|
end
|
223
293
|
RUBY
|
224
294
|
|
225
|
-
# setup a finalizer to remove the newly added method
|
226
295
|
ObjectSpace.define_finalizer self,
|
227
296
|
Template.compiled_template_method_remover(CompileSite, method_name)
|
228
297
|
end
|
@@ -240,14 +309,6 @@ module Tilt
|
|
240
309
|
end
|
241
310
|
end
|
242
311
|
end
|
243
|
-
|
244
|
-
def require_template_library(name)
|
245
|
-
if Thread.list.size > 1
|
246
|
-
warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
|
247
|
-
"explicit require '#{name}' suggested."
|
248
|
-
end
|
249
|
-
require name
|
250
|
-
end
|
251
312
|
end
|
252
313
|
|
253
314
|
# Extremely simple template cache implementation. Calling applications
|
@@ -282,7 +343,7 @@ module Tilt
|
|
282
343
|
@code = "%Q{#{data}}"
|
283
344
|
end
|
284
345
|
|
285
|
-
def
|
346
|
+
def precompiled_template(locals)
|
286
347
|
@code
|
287
348
|
end
|
288
349
|
end
|
@@ -293,7 +354,8 @@ module Tilt
|
|
293
354
|
# http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
|
294
355
|
class ERBTemplate < Template
|
295
356
|
def initialize_engine
|
296
|
-
|
357
|
+
return if defined? ::ERB
|
358
|
+
require_template_library 'erb'
|
297
359
|
end
|
298
360
|
|
299
361
|
def prepare
|
@@ -301,38 +363,37 @@ module Tilt
|
|
301
363
|
@engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
|
302
364
|
end
|
303
365
|
|
304
|
-
def
|
366
|
+
def precompiled_template(locals)
|
305
367
|
@engine.src
|
306
368
|
end
|
307
369
|
|
308
|
-
def
|
309
|
-
|
370
|
+
def precompiled_preamble(locals)
|
371
|
+
<<-RUBY
|
372
|
+
begin
|
373
|
+
__original_outvar = #{@outvar} if defined?(#{@outvar})
|
374
|
+
#{super}
|
375
|
+
RUBY
|
310
376
|
end
|
311
377
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
output = yield
|
320
|
-
scope.instance_variable_set(@outvar, previous)
|
321
|
-
output
|
322
|
-
else
|
323
|
-
yield
|
324
|
-
end
|
378
|
+
def precompiled_postamble(locals)
|
379
|
+
<<-RUBY
|
380
|
+
#{super}
|
381
|
+
ensure
|
382
|
+
#{@outvar} = __original_outvar
|
383
|
+
end
|
384
|
+
RUBY
|
325
385
|
end
|
326
386
|
|
327
387
|
# ERB generates a line to specify the character coding of the generated
|
328
388
|
# source in 1.9. Account for this in the line offset.
|
329
389
|
if RUBY_VERSION >= '1.9.0'
|
330
|
-
def
|
390
|
+
def precompiled(locals)
|
331
391
|
source, offset = super
|
332
392
|
[source, offset + 1]
|
333
393
|
end
|
334
394
|
end
|
335
395
|
end
|
396
|
+
|
336
397
|
%w[erb rhtml].each { |ext| register ext, ERBTemplate }
|
337
398
|
|
338
399
|
|
@@ -340,7 +401,8 @@ module Tilt
|
|
340
401
|
# http://www.kuwata-lab.com/erubis/
|
341
402
|
class ErubisTemplate < ERBTemplate
|
342
403
|
def initialize_engine
|
343
|
-
|
404
|
+
return if defined? ::Erubis
|
405
|
+
require_template_library 'erubis'
|
344
406
|
end
|
345
407
|
|
346
408
|
def prepare
|
@@ -349,15 +411,18 @@ module Tilt
|
|
349
411
|
@engine = ::Erubis::Eruby.new(data, options)
|
350
412
|
end
|
351
413
|
|
352
|
-
def
|
353
|
-
["#{@outvar} = _buf = ''"
|
414
|
+
def precompiled_preamble(locals)
|
415
|
+
[super, "#{@outvar} = _buf = ''"].join("\n")
|
354
416
|
end
|
355
417
|
|
356
|
-
|
357
|
-
|
358
|
-
|
418
|
+
def precompiled_postamble(locals)
|
419
|
+
["_buf", super].join("\n")
|
420
|
+
end
|
421
|
+
|
422
|
+
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
|
423
|
+
# Override and adjust back.
|
359
424
|
if RUBY_VERSION >= '1.9.0'
|
360
|
-
def
|
425
|
+
def precompiled(locals)
|
361
426
|
source, offset = super
|
362
427
|
[source, offset - 1]
|
363
428
|
end
|
@@ -370,26 +435,48 @@ module Tilt
|
|
370
435
|
# http://haml.hamptoncatlin.com/
|
371
436
|
class HamlTemplate < Template
|
372
437
|
def initialize_engine
|
373
|
-
|
438
|
+
return if defined? ::Haml::Engine
|
439
|
+
require_template_library 'haml'
|
374
440
|
end
|
375
441
|
|
376
442
|
def prepare
|
377
|
-
|
443
|
+
options = @options.merge(:filename => eval_file, :line => line)
|
444
|
+
@engine = ::Haml::Engine.new(data, options)
|
445
|
+
end
|
446
|
+
|
447
|
+
def evaluate(scope, locals, &block)
|
448
|
+
if @engine.respond_to?(:precompiled_method_return_value, true)
|
449
|
+
super
|
450
|
+
else
|
451
|
+
@engine.render(scope, locals, &block)
|
452
|
+
end
|
378
453
|
end
|
379
454
|
|
380
455
|
# Precompiled Haml source. Taken from the precompiled_with_ambles
|
381
456
|
# method in Haml::Precompiler:
|
382
457
|
# http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126
|
383
|
-
def
|
458
|
+
def precompiled_template(locals)
|
459
|
+
@engine.precompiled
|
460
|
+
end
|
461
|
+
|
462
|
+
def precompiled_preamble(locals)
|
463
|
+
local_assigns = super
|
384
464
|
@engine.instance_eval do
|
385
465
|
<<-RUBY
|
386
|
-
_haml_locals = locals
|
387
466
|
begin
|
388
467
|
extend Haml::Helpers
|
389
468
|
_hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})
|
390
469
|
_erbout = _hamlout.buffer
|
391
470
|
__in_erb_template = true
|
392
|
-
|
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
|
393
480
|
#{precompiled_method_return_value}
|
394
481
|
ensure
|
395
482
|
@haml_buffer = @haml_buffer.upper
|
@@ -397,16 +484,6 @@ module Tilt
|
|
397
484
|
RUBY
|
398
485
|
end
|
399
486
|
end
|
400
|
-
|
401
|
-
private
|
402
|
-
def local_assignment_code(locals)
|
403
|
-
source, offset = super
|
404
|
-
[source, offset + 6]
|
405
|
-
end
|
406
|
-
|
407
|
-
def haml_options
|
408
|
-
options.merge(:filename => eval_file, :line => line)
|
409
|
-
end
|
410
487
|
end
|
411
488
|
register 'haml', HamlTemplate
|
412
489
|
|
@@ -417,7 +494,8 @@ module Tilt
|
|
417
494
|
# Sass templates do not support object scopes, locals, or yield.
|
418
495
|
class SassTemplate < Template
|
419
496
|
def initialize_engine
|
420
|
-
|
497
|
+
return if defined? ::Sass::Engine
|
498
|
+
require_template_library 'sass'
|
421
499
|
end
|
422
500
|
|
423
501
|
def prepare
|
@@ -442,7 +520,8 @@ module Tilt
|
|
442
520
|
# Less templates do not support object scopes, locals, or yield.
|
443
521
|
class LessTemplate < Template
|
444
522
|
def initialize_engine
|
445
|
-
|
523
|
+
return if defined? ::Less::Engine
|
524
|
+
require_template_library 'less'
|
446
525
|
end
|
447
526
|
|
448
527
|
def prepare
|
@@ -460,7 +539,8 @@ module Tilt
|
|
460
539
|
# http://builder.rubyforge.org/
|
461
540
|
class BuilderTemplate < Template
|
462
541
|
def initialize_engine
|
463
|
-
|
542
|
+
return if defined?(::Builder)
|
543
|
+
require_template_library 'builder'
|
464
544
|
end
|
465
545
|
|
466
546
|
def prepare
|
@@ -477,7 +557,7 @@ module Tilt
|
|
477
557
|
xml.target!
|
478
558
|
end
|
479
559
|
|
480
|
-
def
|
560
|
+
def precompiled_template(locals)
|
481
561
|
data.to_str
|
482
562
|
end
|
483
563
|
end
|
@@ -499,7 +579,8 @@ module Tilt
|
|
499
579
|
# time when using this template engine.
|
500
580
|
class LiquidTemplate < Template
|
501
581
|
def initialize_engine
|
502
|
-
|
582
|
+
return if defined? ::Liquid::Template
|
583
|
+
require_template_library 'liquid'
|
503
584
|
end
|
504
585
|
|
505
586
|
def prepare
|
@@ -532,15 +613,17 @@ module Tilt
|
|
532
613
|
end
|
533
614
|
|
534
615
|
def initialize_engine
|
535
|
-
|
616
|
+
return if defined? ::RDiscount
|
617
|
+
require_template_library 'rdiscount'
|
536
618
|
end
|
537
619
|
|
538
620
|
def prepare
|
539
621
|
@engine = RDiscount.new(data, *flags)
|
622
|
+
@output = nil
|
540
623
|
end
|
541
624
|
|
542
625
|
def evaluate(scope, locals, &block)
|
543
|
-
@engine.to_html
|
626
|
+
@output ||= @engine.to_html
|
544
627
|
end
|
545
628
|
end
|
546
629
|
register 'markdown', RDiscountTemplate
|
@@ -552,15 +635,17 @@ module Tilt
|
|
552
635
|
# http://redcloth.org/
|
553
636
|
class RedClothTemplate < Template
|
554
637
|
def initialize_engine
|
555
|
-
|
638
|
+
return if defined? ::RedCloth
|
639
|
+
require_template_library 'redcloth'
|
556
640
|
end
|
557
641
|
|
558
642
|
def prepare
|
559
643
|
@engine = RedCloth.new(data)
|
644
|
+
@output = nil
|
560
645
|
end
|
561
646
|
|
562
647
|
def evaluate(scope, locals, &block)
|
563
|
-
@engine.to_html
|
648
|
+
@output ||= @engine.to_html
|
564
649
|
end
|
565
650
|
end
|
566
651
|
register 'textile', RedClothTemplate
|
@@ -576,7 +661,8 @@ module Tilt
|
|
576
661
|
attr_reader :engine
|
577
662
|
|
578
663
|
def initialize_engine
|
579
|
-
|
664
|
+
return if defined? ::Mustache
|
665
|
+
require_template_library 'mustache'
|
580
666
|
end
|
581
667
|
|
582
668
|
def prepare
|
@@ -622,19 +708,19 @@ module Tilt
|
|
622
708
|
# engine.
|
623
709
|
class RDocTemplate < Template
|
624
710
|
def initialize_engine
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
end
|
711
|
+
return if defined?(::RDoc::Markup)
|
712
|
+
require_template_library 'rdoc/markup'
|
713
|
+
require_template_library 'rdoc/markup/to_html'
|
629
714
|
end
|
630
715
|
|
631
716
|
def prepare
|
632
717
|
markup = RDoc::Markup::ToHtml.new
|
633
718
|
@engine = markup.convert(data)
|
719
|
+
@output = nil
|
634
720
|
end
|
635
721
|
|
636
722
|
def evaluate(scope, locals, &block)
|
637
|
-
@engine.to_s
|
723
|
+
@output ||= @engine.to_s
|
638
724
|
end
|
639
725
|
end
|
640
726
|
register 'rdoc', RDocTemplate
|
@@ -644,15 +730,16 @@ module Tilt
|
|
644
730
|
# http://jashkenas.github.com/coffee-script/
|
645
731
|
class CoffeeTemplate < Template
|
646
732
|
def initialize_engine
|
647
|
-
|
733
|
+
return if defined? ::CoffeeScript
|
734
|
+
require_template_library 'coffee-script'
|
648
735
|
end
|
649
736
|
|
650
737
|
def prepare
|
651
|
-
@
|
738
|
+
@output = nil
|
652
739
|
end
|
653
740
|
|
654
741
|
def evaluate(scope, locals, &block)
|
655
|
-
@
|
742
|
+
@output ||= ::CoffeeScript::compile(data, options)
|
656
743
|
end
|
657
744
|
end
|
658
745
|
register 'coffee', CoffeeTemplate
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'contest'
|
2
2
|
require 'tilt'
|
3
|
+
require 'thread'
|
3
4
|
|
4
5
|
class CompileSiteTest < Test::Unit::TestCase
|
5
6
|
def setup
|
@@ -10,7 +11,7 @@ class CompileSiteTest < Test::Unit::TestCase
|
|
10
11
|
def prepare
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
14
|
+
def precompiled_template(locals)
|
14
15
|
@data.inspect
|
15
16
|
end
|
16
17
|
end
|
@@ -22,7 +23,7 @@ class CompileSiteTest < Test::Unit::TestCase
|
|
22
23
|
test "compiling template source to a method" do
|
23
24
|
template = CompilingTemplate.new { |t| "Hello World!" }
|
24
25
|
template.render(Scope.new)
|
25
|
-
method_name = template.send(:compiled_method_name, []
|
26
|
+
method_name = template.send(:compiled_method_name, [])
|
26
27
|
method_name = method_name.to_sym if Symbol === Kernel.methods.first
|
27
28
|
assert Tilt::CompileSite.instance_methods.include?(method_name),
|
28
29
|
"CompileSite.instance_methods.include?(#{method_name.inspect})"
|
@@ -32,7 +33,7 @@ class CompileSiteTest < Test::Unit::TestCase
|
|
32
33
|
|
33
34
|
test 'garbage collecting compiled methods' do
|
34
35
|
template = CompilingTemplate.new { '' }
|
35
|
-
method_name = template.send(:compiled_method_name, []
|
36
|
+
method_name = template.send(:compiled_method_name, [])
|
36
37
|
template.render(Scope.new)
|
37
38
|
assert Scope.new.respond_to?(method_name)
|
38
39
|
Tilt::Template.send(
|
@@ -46,7 +47,7 @@ class CompileSiteTest < Test::Unit::TestCase
|
|
46
47
|
def self.create_and_destroy_template
|
47
48
|
template = CompilingTemplate.new { 'Hello World' }
|
48
49
|
template.render(Scope.new)
|
49
|
-
method_name = template.send(:compiled_method_name, []
|
50
|
+
method_name = template.send(:compiled_method_name, [])
|
50
51
|
method_name = method_name.to_sym if Symbol === Kernel.methods.first
|
51
52
|
[template.object_id, method_name]
|
52
53
|
end
|
@@ -59,4 +60,27 @@ class CompileSiteTest < Test::Unit::TestCase
|
|
59
60
|
assert !Scope.new.respond_to?(finalized_method_name),
|
60
61
|
"Scope.new.respond_to?(#{finalized_method_name.inspect})"
|
61
62
|
end
|
63
|
+
|
64
|
+
# This test attempts to surface issues with compiling templates from
|
65
|
+
# multiple threads.
|
66
|
+
test "using compiled templates from multiple threads" do
|
67
|
+
template = CompilingTemplate.new { 'template' }
|
68
|
+
main_thread = Thread.current
|
69
|
+
10.times do |i|
|
70
|
+
threads =
|
71
|
+
(1..50).map do |j|
|
72
|
+
Thread.new {
|
73
|
+
begin
|
74
|
+
locals = { "local#{i}" => 'value' }
|
75
|
+
res = template.render(self, locals)
|
76
|
+
thread_id = Thread.current.object_id
|
77
|
+
res = template.render(self, "local#{thread_id.to_s}" => 'value')
|
78
|
+
rescue => boom
|
79
|
+
main_thread.raise(boom)
|
80
|
+
end
|
81
|
+
}
|
82
|
+
end
|
83
|
+
threads.each { |t| t.join }
|
84
|
+
end
|
85
|
+
end
|
62
86
|
end
|
@@ -102,7 +102,7 @@ class CompiledERBTemplateTest < Test::Unit::TestCase
|
|
102
102
|
test "compiling template source to a method" do
|
103
103
|
template = Tilt::ERBTemplate.new { |t| "Hello World!" }
|
104
104
|
template.render(Scope.new)
|
105
|
-
method_name = template.send(:compiled_method_name, []
|
105
|
+
method_name = template.send(:compiled_method_name, [])
|
106
106
|
method_name = method_name.to_sym if Symbol === Kernel.methods.first
|
107
107
|
assert Tilt::CompileSite.instance_methods.include?(method_name),
|
108
108
|
"CompileSite.instance_methods.include?(#{method_name.inspect})"
|
@@ -71,6 +71,17 @@ begin
|
|
71
71
|
include Tilt::CompileSite
|
72
72
|
end
|
73
73
|
|
74
|
+
test "compiling template source to a method" do
|
75
|
+
template = Tilt::HamlTemplate.new { |t| "Hello World!" }
|
76
|
+
template.render(Scope.new)
|
77
|
+
method_name = template.send(:compiled_method_name, [])
|
78
|
+
method_name = method_name.to_sym if Symbol === Kernel.methods.first
|
79
|
+
assert Tilt::CompileSite.instance_methods.include?(method_name),
|
80
|
+
"CompileSite.instance_methods.include?(#{method_name.inspect})"
|
81
|
+
assert Scope.new.respond_to?(method_name),
|
82
|
+
"scope.respond_to?(#{method_name.inspect})"
|
83
|
+
end
|
84
|
+
|
74
85
|
test "passing locals" do
|
75
86
|
template = Tilt::HamlTemplate.new { "%p= 'Hey ' + name + '!'" }
|
76
87
|
assert_equal "<p>Hey Joe!</p>\n", template.render(Scope.new, :name => 'Joe')
|
@@ -80,7 +80,7 @@ class CompiledStringTemplateTest < Test::Unit::TestCase
|
|
80
80
|
test "compiling template source to a method" do
|
81
81
|
template = Tilt::StringTemplate.new { |t| "Hello World!" }
|
82
82
|
template.render(Scope.new)
|
83
|
-
method_name = template.send(:compiled_method_name, []
|
83
|
+
method_name = template.send(:compiled_method_name, [])
|
84
84
|
method_name = method_name.to_sym if Symbol === Kernel.methods.first
|
85
85
|
assert Tilt::CompileSite.instance_methods.include?(method_name),
|
86
86
|
"CompileSite.instance_methods.include?(#{method_name.inspect})"
|
data/test/tilt_template_test.rb
CHANGED
data/tilt.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'tilt'
|
6
|
-
s.version = '0.
|
7
|
-
s.date = '2010-03-
|
6
|
+
s.version = '0.8'
|
7
|
+
s.date = '2010-03-07'
|
8
8
|
|
9
9
|
s.description = "Generic interface to multiple Ruby template engines"
|
10
10
|
s.summary = s.description
|
@@ -42,11 +42,11 @@ Gem::Specification.new do |s|
|
|
42
42
|
]
|
43
43
|
# = MANIFEST =
|
44
44
|
|
45
|
-
s.test_files = s.files.select {|path| path =~ /^test
|
45
|
+
s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/}
|
46
46
|
s.add_development_dependency 'contest'
|
47
47
|
s.add_development_dependency 'builder'
|
48
48
|
s.add_development_dependency 'erubis'
|
49
|
-
s.add_development_dependency 'haml'
|
49
|
+
s.add_development_dependency 'haml', '>= 2.2.11'
|
50
50
|
s.add_development_dependency 'mustache'
|
51
51
|
s.add_development_dependency 'rdiscount'
|
52
52
|
s.add_development_dependency 'liquid'
|
metadata
CHANGED
@@ -4,8 +4,8 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
version: "0.
|
7
|
+
- 8
|
8
|
+
version: "0.8"
|
9
9
|
platform: ruby
|
10
10
|
authors:
|
11
11
|
- Ryan Tomayko
|
@@ -13,7 +13,7 @@ autorequire:
|
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
15
|
|
16
|
-
date: 2010-03-
|
16
|
+
date: 2010-03-07 00:00:00 -08:00
|
17
17
|
default_executable:
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
@@ -60,8 +60,10 @@ dependencies:
|
|
60
60
|
- - ">="
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
segments:
|
63
|
-
-
|
64
|
-
|
63
|
+
- 2
|
64
|
+
- 2
|
65
|
+
- 11
|
66
|
+
version: 2.2.11
|
65
67
|
type: :development
|
66
68
|
version_requirements: *id004
|
67
69
|
- !ruby/object:Gem::Dependency
|
@@ -193,5 +195,21 @@ rubygems_version: 1.3.6
|
|
193
195
|
signing_key:
|
194
196
|
specification_version: 2
|
195
197
|
summary: Generic interface to multiple Ruby template engines
|
196
|
-
test_files:
|
197
|
-
|
198
|
+
test_files:
|
199
|
+
- test/tilt_buildertemplate_test.rb
|
200
|
+
- test/tilt_cache_test.rb
|
201
|
+
- test/tilt_coffeetemplate_test.rb
|
202
|
+
- test/tilt_compilesite_test.rb
|
203
|
+
- test/tilt_erbtemplate_test.rb
|
204
|
+
- test/tilt_erubistemplate_test.rb
|
205
|
+
- test/tilt_hamltemplate_test.rb
|
206
|
+
- test/tilt_lesstemplate_test.rb
|
207
|
+
- test/tilt_liquidtemplate_test.rb
|
208
|
+
- test/tilt_mustachetemplate_test.rb
|
209
|
+
- test/tilt_rdiscounttemplate_test.rb
|
210
|
+
- test/tilt_rdoctemplate_test.rb
|
211
|
+
- test/tilt_redclothtemplate_test.rb
|
212
|
+
- test/tilt_sasstemplate_test.rb
|
213
|
+
- test/tilt_stringtemplate_test.rb
|
214
|
+
- test/tilt_template_test.rb
|
215
|
+
- test/tilt_test.rb
|