tilt 2.0.11 → 2.6.1
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.
- checksums.yaml +4 -4
- data/COPYING +1 -0
- data/bin/tilt +2 -120
- data/lib/tilt/_emacs_org.rb +2 -0
- data/lib/tilt/_handlebars.rb +2 -0
- data/lib/tilt/_jbuilder.rb +2 -0
- data/lib/tilt/_org.rb +2 -0
- data/lib/tilt/asciidoc.rb +11 -23
- data/lib/tilt/babel.rb +5 -13
- data/lib/tilt/builder.rb +18 -13
- data/lib/tilt/cli.rb +134 -0
- data/lib/tilt/coffee.rb +12 -31
- data/lib/tilt/commonmarker.rb +82 -75
- data/lib/tilt/creole.rb +10 -19
- data/lib/tilt/csv.rb +6 -18
- data/lib/tilt/erb.rb +23 -21
- data/lib/tilt/erubi.rb +29 -6
- data/lib/tilt/etanni.rb +5 -4
- data/lib/tilt/haml.rb +73 -65
- data/lib/tilt/kramdown.rb +8 -20
- data/lib/tilt/liquid.rb +10 -17
- data/lib/tilt/livescript.rb +8 -20
- data/lib/tilt/mapping.rb +228 -109
- data/lib/tilt/markaby.rb +5 -7
- data/lib/tilt/nokogiri.rb +11 -10
- data/lib/tilt/pandoc.rb +33 -51
- data/lib/tilt/pipeline.rb +19 -0
- data/lib/tilt/plain.rb +4 -15
- data/lib/tilt/prawn.rb +10 -25
- data/lib/tilt/radius.rb +15 -22
- data/lib/tilt/rdiscount.rb +17 -33
- data/lib/tilt/rdoc.rb +6 -35
- data/lib/tilt/redcarpet.rb +20 -75
- data/lib/tilt/redcloth.rb +9 -19
- data/lib/tilt/rst-pandoc.rb +7 -20
- data/lib/tilt/sass.rb +43 -43
- data/lib/tilt/slim.rb +5 -0
- data/lib/tilt/string.rb +9 -3
- data/lib/tilt/template.rb +392 -89
- data/lib/tilt/typescript.rb +11 -18
- data/lib/tilt/yajl.rb +5 -11
- data/lib/tilt.rb +68 -43
- metadata +21 -18
- data/lib/tilt/bluecloth.rb +0 -24
- data/lib/tilt/dummy.rb +0 -3
- data/lib/tilt/erubis.rb +0 -43
- data/lib/tilt/less.rb +0 -30
- data/lib/tilt/maruku.rb +0 -22
- data/lib/tilt/sigil.rb +0 -34
- data/lib/tilt/wikicloth.rb +0 -22
data/lib/tilt/template.rb
CHANGED
@@ -1,17 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
3
2
|
module Tilt
|
4
3
|
# @private
|
5
|
-
|
6
|
-
# @private
|
7
|
-
module CompiledTemplates
|
8
|
-
self
|
9
|
-
end
|
10
|
-
elsif RUBY_VERSION >= '1.9'
|
11
|
-
BasicObject
|
12
|
-
else
|
13
|
-
Object
|
4
|
+
module CompiledTemplates
|
14
5
|
end
|
6
|
+
|
7
|
+
# @private
|
8
|
+
TOPOBJECT = CompiledTemplates
|
9
|
+
|
15
10
|
# @private
|
16
11
|
LOCK = Mutex.new
|
17
12
|
|
@@ -33,6 +28,12 @@ module Tilt
|
|
33
28
|
# interface.
|
34
29
|
attr_reader :options
|
35
30
|
|
31
|
+
# A path ending in .rb that the template code will be written to, then
|
32
|
+
# required, instead of being evaled. This is useful for determining
|
33
|
+
# coverage of compiled template code, or to use static analysis tools
|
34
|
+
# on the compiled template code.
|
35
|
+
attr_reader :compiled_path
|
36
|
+
|
36
37
|
class << self
|
37
38
|
# An empty Hash that the template engine can populate with various
|
38
39
|
# metadata.
|
@@ -40,12 +41,12 @@ module Tilt
|
|
40
41
|
@metadata ||= {}
|
41
42
|
end
|
42
43
|
|
43
|
-
#
|
44
|
+
# Use `.metadata[:mime_type]` instead.
|
44
45
|
def default_mime_type
|
45
46
|
metadata[:mime_type]
|
46
47
|
end
|
47
48
|
|
48
|
-
#
|
49
|
+
# Use `.metadata[:mime_type] = val` instead.
|
49
50
|
def default_mime_type=(value)
|
50
51
|
metadata[:mime_type] = value
|
51
52
|
end
|
@@ -56,38 +57,63 @@ module Tilt
|
|
56
57
|
# it should read template data and return as a String. When file is nil,
|
57
58
|
# a block is required.
|
58
59
|
#
|
59
|
-
# All arguments are optional.
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
60
|
+
# All arguments are optional. The following options are respected and
|
61
|
+
# are used by Tilt::Template itself and not the underlying template
|
62
|
+
# libraries:
|
63
|
+
#
|
64
|
+
# :default_encoding :: Force the encoding of the template to the given
|
65
|
+
# encoding.
|
66
|
+
# :skip_compiled_encoding_detection :: Do not scan template code for
|
67
|
+
# an encoding magic comment.
|
68
|
+
# :fixed_locals :: Force a specific method parameter signature, and call
|
69
|
+
# the method with a splat of locals, instead of passing
|
70
|
+
# the locals hash as a positional argument, and
|
71
|
+
# extracting locals from that. Should be a string
|
72
|
+
# containing the parameters for the compiled method,
|
73
|
+
# surrounded by parentheses. Can be set to false to
|
74
|
+
# disable the scan for embedded fixed locals.
|
75
|
+
# :extract_fixed_locals :: Whether embedded fixed locals should be scanned for
|
76
|
+
# and extracted from the template code.
|
77
|
+
# :default_fixed_locals :: Similar to fixed_locals, but lowest priority,
|
78
|
+
# only used if :fixed_locals is not provided
|
79
|
+
# and no embedded locals are found (or scanned for).
|
80
|
+
# :scope_class :: Force the scope class used for the method. By default,
|
81
|
+
# uses the class of the scope provided to render.
|
82
|
+
def initialize(file=nil, line=nil, options=nil)
|
83
|
+
@file, @line, @options = nil, 1, nil
|
84
|
+
|
85
|
+
process_arg(options)
|
86
|
+
process_arg(line)
|
87
|
+
process_arg(file)
|
88
|
+
|
89
|
+
raise ArgumentError, "file or block required" unless @file || block_given?
|
90
|
+
|
91
|
+
@options ||= {}
|
92
|
+
|
93
|
+
# Force a specific scope class, instead of using the class of the provided
|
94
|
+
# scope as the scope class.
|
95
|
+
@scope_class = @options.delete :scope_class
|
96
|
+
|
97
|
+
# Force the encoding of the input data
|
98
|
+
@default_encoding = @options.delete :default_encoding
|
76
99
|
|
77
|
-
#
|
78
|
-
|
100
|
+
# Skip encoding detection from magic comments and forcing that encoding
|
101
|
+
# for compiled templates
|
102
|
+
@skip_compiled_encoding_detection = @options.delete :skip_compiled_encoding_detection
|
79
103
|
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
104
|
+
# Compiled path to use. This must be specified as an option if
|
105
|
+
# providing the :scope_class option and using fixed locals,
|
106
|
+
# since template compilation occurs during initialization in that case.
|
107
|
+
if compiled_path = @options.delete(:compiled_path)
|
108
|
+
self.compiled_path = compiled_path
|
109
|
+
end
|
83
110
|
|
84
111
|
# load template data and prepare (uses binread to avoid encoding issues)
|
85
|
-
@
|
86
|
-
@data = @reader.call(self)
|
112
|
+
@data = block_given? ? yield(self) : read_template_file
|
87
113
|
|
88
114
|
if @data.respond_to?(:force_encoding)
|
89
115
|
if default_encoding
|
90
|
-
@data = @data
|
116
|
+
@data = _dup_string_if_frozen(@data)
|
91
117
|
@data.force_encoding(default_encoding)
|
92
118
|
end
|
93
119
|
|
@@ -96,34 +122,38 @@ module Tilt
|
|
96
122
|
end
|
97
123
|
end
|
98
124
|
|
125
|
+
set_fixed_locals
|
99
126
|
prepare
|
127
|
+
set_compiled_method_cache
|
100
128
|
end
|
101
129
|
|
102
130
|
# Render the template in the given scope with the locals specified. If a
|
103
131
|
# block is given, it is typically available within the template via
|
104
132
|
# +yield+.
|
105
|
-
def render(scope=nil, locals=
|
106
|
-
scope
|
107
|
-
current_template = Thread.current[:tilt_current_template]
|
108
|
-
Thread.current[:tilt_current_template] = self
|
109
|
-
evaluate(scope, locals || {}, &block)
|
110
|
-
ensure
|
111
|
-
Thread.current[:tilt_current_template] = current_template
|
133
|
+
def render(scope=nil, locals=nil, &block)
|
134
|
+
evaluate(scope || Object.new, locals || EMPTY_HASH, &block)
|
112
135
|
end
|
113
136
|
|
114
137
|
# The basename of the template file.
|
115
138
|
def basename(suffix='')
|
116
|
-
File.basename(file, suffix) if file
|
139
|
+
File.basename(@file, suffix) if @file
|
117
140
|
end
|
118
141
|
|
119
142
|
# The template file's basename with all extensions chomped off.
|
120
143
|
def name
|
121
|
-
|
144
|
+
if bname = basename
|
145
|
+
bname.split('.', 2).first
|
146
|
+
end
|
122
147
|
end
|
123
148
|
|
124
149
|
# The filename used in backtraces to describe the template.
|
125
150
|
def eval_file
|
126
|
-
file || '(__TEMPLATE__)'
|
151
|
+
@file || '(__TEMPLATE__)'
|
152
|
+
end
|
153
|
+
|
154
|
+
# Whether the template uses fixed locals.
|
155
|
+
def fixed_locals?
|
156
|
+
@fixed_locals ? true : false
|
127
157
|
end
|
128
158
|
|
129
159
|
# An empty Hash that the template engine can populate with various
|
@@ -136,6 +166,53 @@ module Tilt
|
|
136
166
|
end
|
137
167
|
end
|
138
168
|
|
169
|
+
# Set the prefix to use for compiled paths, similar to using the
|
170
|
+
# :compiled_path template option. Note that this only
|
171
|
+
# has affect for future template compilations. When using the
|
172
|
+
# :scope_class template option, and using fixed_locals, calling
|
173
|
+
# this after the template is created has no effect, since the
|
174
|
+
# template is compiled during initialization in that case. It
|
175
|
+
# is recommended to use the :compiled_path template option
|
176
|
+
# instead of this method in new code.
|
177
|
+
def compiled_path=(path)
|
178
|
+
if path
|
179
|
+
# Use expanded paths when loading, since that is helpful
|
180
|
+
# for coverage. Remove any .rb suffix, since that will
|
181
|
+
# be added back later.
|
182
|
+
path = File.expand_path(path.sub(/\.rb\z/i, ''))
|
183
|
+
end
|
184
|
+
@compiled_path = path
|
185
|
+
end
|
186
|
+
|
187
|
+
# The compiled method for the locals keys and scope_class provided.
|
188
|
+
# Returns an UnboundMethod, which can be used to define methods
|
189
|
+
# directly on the scope class, which are much faster to call than
|
190
|
+
# Tilt's normal rendering.
|
191
|
+
def compiled_method(locals_keys, scope_class=nil)
|
192
|
+
if @fixed_locals
|
193
|
+
if @scope_class
|
194
|
+
return @compiled_method
|
195
|
+
else
|
196
|
+
key = scope_class
|
197
|
+
end
|
198
|
+
elsif @scope_class
|
199
|
+
key = locals_keys.dup.freeze
|
200
|
+
else
|
201
|
+
key = [scope_class, locals_keys].freeze
|
202
|
+
end
|
203
|
+
|
204
|
+
LOCK.synchronize do
|
205
|
+
if meth = @compiled_method[key]
|
206
|
+
return meth
|
207
|
+
end
|
208
|
+
end
|
209
|
+
meth = compile_template_method(locals_keys, scope_class)
|
210
|
+
LOCK.synchronize do
|
211
|
+
@compiled_method[key] = meth
|
212
|
+
end
|
213
|
+
meth
|
214
|
+
end
|
215
|
+
|
139
216
|
protected
|
140
217
|
|
141
218
|
# @!group For template implementations
|
@@ -144,20 +221,22 @@ module Tilt
|
|
144
221
|
# default_encoding-option if present. You may override this method
|
145
222
|
# in your template class if you have a better hint of the data's
|
146
223
|
# encoding.
|
147
|
-
|
148
|
-
|
224
|
+
attr_reader :default_encoding
|
225
|
+
|
226
|
+
def skip_compiled_encoding_detection?
|
227
|
+
@skip_compiled_encoding_detection
|
149
228
|
end
|
150
229
|
|
151
230
|
# Do whatever preparation is necessary to setup the underlying template
|
152
231
|
# engine. Called immediately after template data is loaded. Instance
|
153
232
|
# variables set in this method are available when #evaluate is called.
|
154
233
|
#
|
155
|
-
#
|
234
|
+
# Empty by default as some subclasses do not need separate preparation.
|
156
235
|
def prepare
|
157
|
-
raise NotImplementedError
|
158
236
|
end
|
159
237
|
|
160
238
|
CLASS_METHOD = Kernel.instance_method(:class)
|
239
|
+
USE_BIND_CALL = RUBY_VERSION >= '3'
|
161
240
|
|
162
241
|
# Execute the compiled template and return the result string. Template
|
163
242
|
# evaluation is guaranteed to be performed in the scope object with the
|
@@ -166,19 +245,25 @@ module Tilt
|
|
166
245
|
# This method is only used by source generating templates. Subclasses that
|
167
246
|
# override render() may not support all features.
|
168
247
|
def evaluate(scope, locals, &block)
|
169
|
-
|
170
|
-
|
171
|
-
case scope
|
172
|
-
when Object
|
173
|
-
method = compiled_method(locals_keys, Module === scope ? scope : scope.class)
|
248
|
+
if @fixed_locals
|
249
|
+
locals_keys = EMPTY_ARRAY
|
174
250
|
else
|
175
|
-
|
176
|
-
|
251
|
+
locals_keys = locals.keys
|
252
|
+
locals_keys.sort!{|x, y| x.to_s <=> y.to_s}
|
253
|
+
end
|
254
|
+
|
255
|
+
unless scope_class = @scope_class
|
256
|
+
scope_class = case scope
|
257
|
+
when Object
|
258
|
+
Module === scope ? scope : scope.class
|
177
259
|
else
|
178
|
-
|
260
|
+
# :nocov:
|
261
|
+
USE_BIND_CALL ? CLASS_METHOD.bind_call(scope) : CLASS_METHOD.bind(scope).call
|
262
|
+
# :nocov:
|
179
263
|
end
|
180
264
|
end
|
181
|
-
|
265
|
+
|
266
|
+
evaluate_method(compiled_method(locals_keys, scope_class), scope, locals, &block)
|
182
267
|
end
|
183
268
|
|
184
269
|
# Generates all template source by combining the preamble, template, and
|
@@ -196,15 +281,19 @@ module Tilt
|
|
196
281
|
postamble = precompiled_postamble(local_keys)
|
197
282
|
source = String.new
|
198
283
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
template_encoding = extract_encoding(template)
|
284
|
+
unless skip_compiled_encoding_detection?
|
285
|
+
# Ensure that our generated source code has the same encoding as the
|
286
|
+
# the source code generated by the template engine.
|
287
|
+
template_encoding = extract_encoding(template){|t| template = t}
|
203
288
|
|
204
|
-
|
205
|
-
|
289
|
+
if template.encoding != template_encoding
|
290
|
+
# template should never be frozen here. If it was frozen originally,
|
291
|
+
# then extract_encoding should yield a dup.
|
292
|
+
template.force_encoding(template_encoding)
|
293
|
+
end
|
206
294
|
end
|
207
295
|
|
296
|
+
source.force_encoding(template.encoding)
|
208
297
|
source << preamble << "\n" << template << "\n" << postamble
|
209
298
|
|
210
299
|
[source, preamble.count("\n")+1]
|
@@ -232,71 +321,231 @@ module Tilt
|
|
232
321
|
|
233
322
|
private
|
234
323
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
# Set it to the default external (without verifying)
|
239
|
-
data.force_encoding(Encoding.default_external) if Encoding.default_external
|
324
|
+
if RUBY_VERSION >= '2.3'
|
325
|
+
def _dup_string_if_frozen(string)
|
326
|
+
+string
|
240
327
|
end
|
328
|
+
# :nocov:
|
329
|
+
else
|
330
|
+
def _dup_string_if_frozen(string)
|
331
|
+
string.frozen? ? string.dup : string
|
332
|
+
end
|
333
|
+
end
|
334
|
+
# :nocov:
|
335
|
+
|
336
|
+
def process_arg(arg)
|
337
|
+
if arg
|
338
|
+
case
|
339
|
+
when arg.respond_to?(:to_str) ; @file = arg.to_str
|
340
|
+
when arg.respond_to?(:to_int) ; @line = arg.to_int
|
341
|
+
when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
|
342
|
+
when arg.respond_to?(:path) ; @file = arg.path
|
343
|
+
when arg.respond_to?(:to_path) ; @file = arg.to_path
|
344
|
+
else raise TypeError, "Can't load the template file. Pass a string with a path " +
|
345
|
+
"or an object that responds to 'to_str', 'path' or 'to_path'"
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def read_template_file
|
351
|
+
data = File.binread(file)
|
352
|
+
# Set it to the default external (without verifying)
|
353
|
+
# :nocov:
|
354
|
+
data.force_encoding(Encoding.default_external) if Encoding.default_external
|
355
|
+
# :nocov:
|
241
356
|
data
|
242
357
|
end
|
243
358
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
359
|
+
def set_compiled_method_cache
|
360
|
+
@compiled_method = if @fixed_locals && @scope_class
|
361
|
+
# No hash needed, only a single compiled method per template.
|
362
|
+
compile_template_method(EMPTY_ARRAY, @scope_class)
|
363
|
+
else
|
364
|
+
{}
|
248
365
|
end
|
249
366
|
end
|
250
367
|
|
251
368
|
def local_extraction(local_keys)
|
252
|
-
local_keys.map do |k|
|
369
|
+
assignments = local_keys.map do |k|
|
253
370
|
if k.to_s =~ /\A[a-z_][a-zA-Z_0-9]*\z/
|
254
371
|
"#{k} = locals[#{k.inspect}]"
|
255
372
|
else
|
256
373
|
raise "invalid locals key: #{k.inspect} (keys must be variable names)"
|
257
374
|
end
|
258
|
-
end
|
375
|
+
end
|
376
|
+
|
377
|
+
s = "locals = locals[:locals]"
|
378
|
+
if assignments.delete(s)
|
379
|
+
# If there is a locals key itself named `locals`, delete it from the ordered keys so we can
|
380
|
+
# assign it last. This is important because the assignment of all other locals depends on the
|
381
|
+
# `locals` local variable still matching the `locals` method argument given to the method
|
382
|
+
# created in `#compile_template_method`.
|
383
|
+
assignments << s
|
384
|
+
end
|
385
|
+
|
386
|
+
assignments.join("\n")
|
387
|
+
end
|
388
|
+
|
389
|
+
if USE_BIND_CALL
|
390
|
+
def evaluate_method(method, scope, locals, &block)
|
391
|
+
if @fixed_locals
|
392
|
+
method.bind_call(scope, **locals, &block)
|
393
|
+
else
|
394
|
+
method.bind_call(scope, locals, &block)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
# :nocov:
|
398
|
+
else
|
399
|
+
def evaluate_method(method, scope, locals, &block)
|
400
|
+
if @fixed_locals
|
401
|
+
if locals.empty?
|
402
|
+
# Empty keyword splat on Ruby 2.0-2.6 passes empty hash
|
403
|
+
method.bind(scope).call(&block)
|
404
|
+
else
|
405
|
+
method.bind(scope).call(**locals, &block)
|
406
|
+
end
|
407
|
+
else
|
408
|
+
method.bind(scope).call(locals, &block)
|
409
|
+
end
|
410
|
+
end
|
259
411
|
end
|
412
|
+
# :nocov:
|
260
413
|
|
261
414
|
def compile_template_method(local_keys, scope_class=nil)
|
262
415
|
source, offset = precompiled(local_keys)
|
263
|
-
|
416
|
+
if @fixed_locals
|
417
|
+
method_args = @fixed_locals
|
418
|
+
else
|
419
|
+
method_args = "(locals)"
|
420
|
+
local_code = local_extraction(local_keys)
|
421
|
+
end
|
264
422
|
|
265
423
|
method_name = "__tilt_#{Thread.current.object_id.abs}"
|
266
424
|
method_source = String.new
|
425
|
+
method_source.force_encoding(source.encoding)
|
267
426
|
|
268
|
-
if
|
269
|
-
method_source
|
427
|
+
if freeze_string_literals?
|
428
|
+
method_source << "# frozen-string-literal: true\n"
|
270
429
|
end
|
271
430
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
#{local_code}
|
276
|
-
RUBY
|
431
|
+
# Don't indent method source, to avoid indentation warnings when using compiled paths
|
432
|
+
method_source << "::Tilt::TOPOBJECT.class_eval do\ndef #{method_name}#{method_args}\n#{local_code}\n"
|
433
|
+
|
277
434
|
offset += method_source.count("\n")
|
278
435
|
method_source << source
|
279
436
|
method_source << "\nend;end;"
|
280
|
-
|
437
|
+
|
438
|
+
bind_compiled_method(method_source, offset, scope_class)
|
281
439
|
unbind_compiled_method(method_name)
|
282
440
|
end
|
283
441
|
|
442
|
+
def bind_compiled_method(method_source, offset, scope_class)
|
443
|
+
path = compiled_path
|
444
|
+
if path && scope_class.name
|
445
|
+
path = path.dup
|
446
|
+
|
447
|
+
if defined?(@compiled_path_counter)
|
448
|
+
path << '-' << @compiled_path_counter.succ!
|
449
|
+
else
|
450
|
+
@compiled_path_counter = "0".dup
|
451
|
+
end
|
452
|
+
path << ".rb"
|
453
|
+
|
454
|
+
# Wrap method source in a class block for the scope, so constant lookup works
|
455
|
+
if freeze_string_literals?
|
456
|
+
method_source_prefix = "# frozen-string-literal: true\n"
|
457
|
+
method_source = method_source.sub(/\A# frozen-string-literal: true\n/, '')
|
458
|
+
end
|
459
|
+
method_source = "#{method_source_prefix}class #{scope_class.name}\n#{method_source}\nend"
|
460
|
+
|
461
|
+
load_compiled_method(path, method_source)
|
462
|
+
else
|
463
|
+
if path
|
464
|
+
warn "compiled_path (#{compiled_path.inspect}) ignored on template with anonymous scope_class (#{scope_class.inspect})"
|
465
|
+
end
|
466
|
+
|
467
|
+
eval_compiled_method(method_source, offset, scope_class)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
def eval_compiled_method(method_source, offset, scope_class)
|
472
|
+
(scope_class || Object).class_eval(method_source, eval_file, line - offset)
|
473
|
+
end
|
474
|
+
|
475
|
+
def load_compiled_method(path, method_source)
|
476
|
+
# Write to a temporary path specific to the current process, and
|
477
|
+
# rename after writing. This prevents issues during parallel
|
478
|
+
# coverage testing.
|
479
|
+
tmp_path = "#{path}-#{$$}"
|
480
|
+
File.binwrite(tmp_path, method_source)
|
481
|
+
File.rename(tmp_path, path)
|
482
|
+
|
483
|
+
# Use load and not require, so unbind_compiled_method does not
|
484
|
+
# break if the same path is used more than once.
|
485
|
+
load path
|
486
|
+
end
|
487
|
+
|
284
488
|
def unbind_compiled_method(method_name)
|
285
489
|
method = TOPOBJECT.instance_method(method_name)
|
286
490
|
TOPOBJECT.class_eval { remove_method(method_name) }
|
287
491
|
method
|
288
492
|
end
|
289
493
|
|
290
|
-
|
291
|
-
|
494
|
+
# Set the fixed locals for the template, which may be nil if no fixed locals can
|
495
|
+
# be determined.
|
496
|
+
def set_fixed_locals
|
497
|
+
fixed_locals = @options.delete(:fixed_locals)
|
498
|
+
extract_fixed_locals = @options.delete(:extract_fixed_locals)
|
499
|
+
default_fixed_locals = @options.delete(:default_fixed_locals)
|
500
|
+
|
501
|
+
if fixed_locals.nil?
|
502
|
+
if extract_fixed_locals.nil?
|
503
|
+
extract_fixed_locals = Tilt.extract_fixed_locals
|
504
|
+
end
|
505
|
+
|
506
|
+
if extract_fixed_locals
|
507
|
+
fixed_locals = extract_fixed_locals()
|
508
|
+
end
|
509
|
+
|
510
|
+
if fixed_locals.nil?
|
511
|
+
fixed_locals = default_fixed_locals
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
@fixed_locals = fixed_locals
|
516
|
+
end
|
517
|
+
|
518
|
+
# Extract fixed locals from the template code string. Should return nil
|
519
|
+
# if there are no fixed locals specified, or a method argument string
|
520
|
+
# surrounded by parentheses if there are fixed locals. The method
|
521
|
+
# argument string will be used when defining the template method if given.
|
522
|
+
def extract_fixed_locals
|
523
|
+
if @data.is_a?(String) && (match = /\#\s*locals:\s*(\(.*\))/.match(@data))
|
524
|
+
match[1]
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
def extract_encoding(script, &block)
|
529
|
+
extract_magic_comment(script, &block) || script.encoding
|
292
530
|
end
|
293
531
|
|
294
532
|
def extract_magic_comment(script)
|
533
|
+
was_frozen = script.frozen?
|
534
|
+
script = _dup_string_if_frozen(script)
|
535
|
+
|
536
|
+
if was_frozen
|
537
|
+
yield script
|
538
|
+
end
|
539
|
+
|
295
540
|
binary(script) do
|
296
541
|
script[/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/n, 1]
|
297
542
|
end
|
298
543
|
end
|
299
544
|
|
545
|
+
def freeze_string_literals?
|
546
|
+
false
|
547
|
+
end
|
548
|
+
|
300
549
|
def binary(string)
|
301
550
|
original_encoding = string.encoding
|
302
551
|
string.force_encoding(Encoding::BINARY)
|
@@ -305,4 +554,58 @@ module Tilt
|
|
305
554
|
string.force_encoding(original_encoding)
|
306
555
|
end
|
307
556
|
end
|
557
|
+
|
558
|
+
# Static templates are templates that return the same output for every render
|
559
|
+
#
|
560
|
+
# Instead of inheriting from the StaticTemplate class, you will use the .subclass
|
561
|
+
# method with a block which processes @data and returns the transformed value.
|
562
|
+
#
|
563
|
+
# Basic example which transforms the template to uppercase:
|
564
|
+
#
|
565
|
+
# UppercaseTemplate = Tilt::StaticTemplate.subclass do
|
566
|
+
# @data.upcase
|
567
|
+
# end
|
568
|
+
class StaticTemplate < Template
|
569
|
+
def self.subclass(mime_type: 'text/html', &block)
|
570
|
+
Class.new(self) do
|
571
|
+
self.default_mime_type = mime_type
|
572
|
+
|
573
|
+
private
|
574
|
+
|
575
|
+
define_method(:_prepare_output, &block)
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
# Static templates always return the prepared output.
|
580
|
+
def render(scope=nil, locals=nil)
|
581
|
+
@output
|
582
|
+
end
|
583
|
+
|
584
|
+
# Raise NotImplementedError, since static templates
|
585
|
+
# do not support compiled methods.
|
586
|
+
def compiled_method(locals_keys, scope_class=nil)
|
587
|
+
raise NotImplementedError
|
588
|
+
end
|
589
|
+
|
590
|
+
# Static templates never allow script.
|
591
|
+
def allows_script?
|
592
|
+
false
|
593
|
+
end
|
594
|
+
|
595
|
+
protected
|
596
|
+
|
597
|
+
def prepare
|
598
|
+
@output = _prepare_output
|
599
|
+
end
|
600
|
+
|
601
|
+
private
|
602
|
+
|
603
|
+
# Do nothing, since compiled method cache is not used.
|
604
|
+
def set_compiled_method_cache
|
605
|
+
end
|
606
|
+
|
607
|
+
# Do nothing, since fixed locals are not used.
|
608
|
+
def set_fixed_locals
|
609
|
+
end
|
610
|
+
end
|
308
611
|
end
|
data/lib/tilt/typescript.rb
CHANGED
@@ -1,26 +1,19 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'template'
|
2
3
|
require 'typescript-node'
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
self.default_mime_type = 'application/javascript'
|
5
|
+
Tilt::TypeScriptTemplate = Tilt::StaticTemplate.subclass(mime_type: 'application/javascript') do
|
6
|
+
option_args = []
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
@options.each do |key, value|
|
9
|
+
next unless value
|
10
10
|
|
11
|
-
|
12
|
-
next unless value
|
11
|
+
option_args << "--#{key}"
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
if value != true
|
17
|
-
@option_args << value.to_s
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def evaluate(scope, locals, &block)
|
23
|
-
@output ||= TypeScript::Node.compile(data, *@option_args)
|
13
|
+
if value != true
|
14
|
+
option_args << value.to_s
|
24
15
|
end
|
25
16
|
end
|
17
|
+
|
18
|
+
TypeScript::Node.compile(@data, *option_args)
|
26
19
|
end
|