tilt 1.3.7 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/README.md +21 -0
- data/lib/tilt.rb +1 -1
- data/lib/tilt/template.rb +67 -24
- data/test/tilt_markdown_test.rb +1 -1
- data/test/tilt_template_test.rb +97 -0
- data/tilt.gemspec +2 -2
- metadata +2 -2
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -194,6 +194,27 @@ template, but if you depend on a specific implementation, you should use #prefer
|
|
194
194
|
When a file extension has a preferred template class, Tilt will *always* use
|
195
195
|
that class, even if it raises an exception.
|
196
196
|
|
197
|
+
Encodings
|
198
|
+
---------
|
199
|
+
|
200
|
+
Tilt needs to know the encoding of the template in order to work properly:
|
201
|
+
|
202
|
+
Tilt will use `Encoding.default_external` as the encoding when reading external
|
203
|
+
files. If you're mostly working with one encoding (e.g. UTF-8) we *highly*
|
204
|
+
recommend setting this option. When providing a custom reader block (`Tilt.new
|
205
|
+
{ custom_string }`) you'll have ensure the string is properly encoded yourself.
|
206
|
+
|
207
|
+
Most of the template engines in Tilt also allows you to override the encoding
|
208
|
+
using the `:default_encoding`-option:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
tmpl = Tilt.new('hello.erb', :default_encoding => 'Big5')
|
212
|
+
```
|
213
|
+
|
214
|
+
Ultimately it's up to the template engine how to handle the encoding: It might
|
215
|
+
respect `:default_encoding`, it might always assume it's UTF-8 (like
|
216
|
+
CoffeScript), or it can do its own encoding detection.
|
217
|
+
|
197
218
|
Template Compilation
|
198
219
|
--------------------
|
199
220
|
|
data/lib/tilt.rb
CHANGED
data/lib/tilt/template.rb
CHANGED
@@ -65,11 +65,37 @@ module Tilt
|
|
65
65
|
@default_encoding = @options.delete :default_encoding
|
66
66
|
|
67
67
|
# load template data and prepare (uses binread to avoid encoding issues)
|
68
|
-
@reader = block || lambda { |t|
|
68
|
+
@reader = block || lambda { |t| read_template_file }
|
69
69
|
@data = @reader.call(self)
|
70
|
+
|
71
|
+
if @data.respond_to?(:force_encoding)
|
72
|
+
@data.force_encoding(default_encoding) if default_encoding
|
73
|
+
|
74
|
+
if !@data.valid_encoding?
|
75
|
+
raise Encoding::InvalidByteSequenceError, "#{eval_file} is not valid #{@data.encoding}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
70
79
|
prepare
|
71
80
|
end
|
72
81
|
|
82
|
+
# The encoding of the source data. Defaults to the
|
83
|
+
# default_encoding-option if present. You may override this method
|
84
|
+
# in your template class if you have a better hint of the data's
|
85
|
+
# encoding.
|
86
|
+
def default_encoding
|
87
|
+
@default_encoding
|
88
|
+
end
|
89
|
+
|
90
|
+
def read_template_file
|
91
|
+
data = File.open(file, 'rb') { |io| io.read }
|
92
|
+
if data.respond_to?(:force_encoding)
|
93
|
+
# Set it to the default external (without verifying)
|
94
|
+
data.force_encoding(Encoding.default_external) if Encoding.default_external
|
95
|
+
end
|
96
|
+
data
|
97
|
+
end
|
98
|
+
|
73
99
|
# Render the template in the given scope with the locals specified. If a
|
74
100
|
# block is given, it is typically available within the template via
|
75
101
|
# +yield+.
|
@@ -156,26 +182,29 @@ module Tilt
|
|
156
182
|
def precompiled(locals)
|
157
183
|
preamble = precompiled_preamble(locals)
|
158
184
|
template = precompiled_template(locals)
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
185
|
+
postamble = precompiled_postamble(locals)
|
186
|
+
source = ''
|
187
|
+
|
188
|
+
# Ensure that our generated source code has the same encoding as the
|
189
|
+
# the source code generated by the template engine.
|
190
|
+
if source.respond_to?(:force_encoding)
|
191
|
+
template_encoding = extract_encoding(template)
|
192
|
+
|
193
|
+
source.force_encoding(template_encoding)
|
194
|
+
template.force_encoding(template_encoding)
|
164
195
|
end
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
]
|
170
|
-
[parts.join("\n"), preamble.count("\n") + 1]
|
196
|
+
|
197
|
+
source << preamble << "\n" << template << "\n" << postamble
|
198
|
+
|
199
|
+
[source, preamble.count("\n")+1]
|
171
200
|
end
|
172
201
|
|
173
202
|
# A string containing the (Ruby) source code for the template. The
|
174
|
-
# default Template#evaluate implementation requires either this
|
175
|
-
# or the #precompiled method be overridden. When defined,
|
176
|
-
# Template guarantees correct file/line handling, locals
|
177
|
-
# scopes, and support for template
|
178
|
-
#
|
203
|
+
# default Template#evaluate implementation requires either this
|
204
|
+
# method or the #precompiled method be overridden. When defined,
|
205
|
+
# the base Template guarantees correct file/line handling, locals
|
206
|
+
# support, custom scopes, proper encoding, and support for template
|
207
|
+
# compilation.
|
179
208
|
def precompiled_template(locals)
|
180
209
|
raise NotImplementedError
|
181
210
|
end
|
@@ -212,8 +241,13 @@ module Tilt
|
|
212
241
|
def compile_template_method(locals)
|
213
242
|
source, offset = precompiled(locals)
|
214
243
|
method_name = "__tilt_#{Thread.current.object_id.abs}"
|
215
|
-
method_source =
|
216
|
-
|
244
|
+
method_source = ""
|
245
|
+
|
246
|
+
if method_source.respond_to?(:force_encoding)
|
247
|
+
method_source.force_encoding(source.encoding)
|
248
|
+
end
|
249
|
+
|
250
|
+
method_source << <<-RUBY
|
217
251
|
TOPOBJECT.class_eval do
|
218
252
|
def #{method_name}(locals)
|
219
253
|
Thread.current[:tilt_vars] = [self, locals]
|
@@ -234,13 +268,22 @@ module Tilt
|
|
234
268
|
method
|
235
269
|
end
|
236
270
|
|
271
|
+
def extract_encoding(script)
|
272
|
+
extract_magic_comment(script) || script.encoding
|
273
|
+
end
|
274
|
+
|
237
275
|
def extract_magic_comment(script)
|
238
|
-
|
239
|
-
|
240
|
-
comment
|
241
|
-
elsif @default_encoding
|
242
|
-
"# coding: #{@default_encoding}"
|
276
|
+
binary script do
|
277
|
+
script[/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/n, 1]
|
243
278
|
end
|
244
279
|
end
|
280
|
+
|
281
|
+
def binary(string)
|
282
|
+
original_encoding = string.encoding
|
283
|
+
string.force_encoding(Encoding::BINARY)
|
284
|
+
yield
|
285
|
+
ensure
|
286
|
+
string.force_encoding(original_encoding)
|
287
|
+
end
|
245
288
|
end
|
246
289
|
end
|
data/test/tilt_markdown_test.rb
CHANGED
data/test/tilt_template_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'contest'
|
2
3
|
require 'tilt'
|
3
4
|
require 'tempfile'
|
@@ -176,4 +177,100 @@ class TiltTemplateTest < Test::Unit::TestCase
|
|
176
177
|
inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{CONSTANT}!' }
|
177
178
|
assert_equal "Hey Bob!", inst.render(Person.new("Joe"))
|
178
179
|
end
|
180
|
+
|
181
|
+
##
|
182
|
+
# Encodings
|
183
|
+
|
184
|
+
class DynamicMockTemplate < MockTemplate
|
185
|
+
def precompiled_template(locals)
|
186
|
+
options[:code]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class UTF8Template < MockTemplate
|
191
|
+
def default_encoding
|
192
|
+
Encoding::UTF_8
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
if ''.respond_to?(:encoding)
|
197
|
+
original_encoding = Encoding.default_external
|
198
|
+
|
199
|
+
setup do
|
200
|
+
@file = Tempfile.open('template')
|
201
|
+
@file.puts "stuff"
|
202
|
+
@file.close
|
203
|
+
@template = @file.path
|
204
|
+
end
|
205
|
+
|
206
|
+
teardown do
|
207
|
+
Encoding.default_external = original_encoding
|
208
|
+
Encoding.default_internal = nil
|
209
|
+
@file.delete
|
210
|
+
end
|
211
|
+
|
212
|
+
test "reading from file assumes default external encoding" do
|
213
|
+
Encoding.default_external = 'Big5'
|
214
|
+
inst = MockTemplate.new(@template)
|
215
|
+
assert_equal 'Big5', inst.data.encoding.to_s
|
216
|
+
end
|
217
|
+
|
218
|
+
test "reading from file with a :default_encoding overrides default external" do
|
219
|
+
Encoding.default_external = 'Big5'
|
220
|
+
inst = MockTemplate.new(@template, :default_encoding => 'GBK')
|
221
|
+
assert_equal 'GBK', inst.data.encoding.to_s
|
222
|
+
end
|
223
|
+
|
224
|
+
test "reading from file with default_internal set does no transcoding" do
|
225
|
+
Encoding.default_internal = 'utf-8'
|
226
|
+
Encoding.default_external = 'Big5'
|
227
|
+
inst = MockTemplate.new(@template)
|
228
|
+
assert_equal 'Big5', inst.data.encoding.to_s
|
229
|
+
end
|
230
|
+
|
231
|
+
test "using provided template data verbatim when given as string" do
|
232
|
+
Encoding.default_internal = 'Big5'
|
233
|
+
inst = MockTemplate.new(@template) { "blah".force_encoding('GBK') }
|
234
|
+
assert_equal 'GBK', inst.data.encoding.to_s
|
235
|
+
end
|
236
|
+
|
237
|
+
test "uses the template from the generated source code" do
|
238
|
+
tmpl = "ふが"
|
239
|
+
code = tmpl.inspect.encode('Shift_JIS')
|
240
|
+
inst = DynamicMockTemplate.new(:code => code) { '' }
|
241
|
+
res = inst.render
|
242
|
+
assert_equal 'Shift_JIS', res.encoding.to_s
|
243
|
+
assert_equal tmpl, res.encode(tmpl.encoding)
|
244
|
+
end
|
245
|
+
|
246
|
+
test "uses the magic comment from the generated source code" do
|
247
|
+
tmpl = "ふが"
|
248
|
+
code = ("# coding: Shift_JIS\n" + tmpl.inspect).encode('Shift_JIS')
|
249
|
+
# Set it to an incorrect encoding
|
250
|
+
code.force_encoding('UTF-8')
|
251
|
+
|
252
|
+
inst = DynamicMockTemplate.new(:code => code) { '' }
|
253
|
+
res = inst.render
|
254
|
+
assert_equal 'Shift_JIS', res.encoding.to_s
|
255
|
+
assert_equal tmpl, res.encode(tmpl.encoding)
|
256
|
+
end
|
257
|
+
|
258
|
+
test "uses #default_encoding instead of default_external" do
|
259
|
+
Encoding.default_external = 'Big5'
|
260
|
+
inst = UTF8Template.new(@template)
|
261
|
+
assert_equal 'UTF-8', inst.data.encoding.to_s
|
262
|
+
end
|
263
|
+
|
264
|
+
test "uses #default_encoding instead of current encoding" do
|
265
|
+
tmpl = "".force_encoding('Big5')
|
266
|
+
inst = UTF8Template.new(@template) { tmpl }
|
267
|
+
assert_equal 'UTF-8', inst.data.encoding.to_s
|
268
|
+
end
|
269
|
+
|
270
|
+
test "raises error if the encoding is not valid" do
|
271
|
+
assert_raises(Encoding::InvalidByteSequenceError) do
|
272
|
+
UTF8Template.new(@template) { "\xe4" }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
179
276
|
end
|
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 = '1.
|
7
|
-
s.date = '2013-
|
6
|
+
s.version = '1.4.0'
|
7
|
+
s.date = '2013-05-01'
|
8
8
|
|
9
9
|
s.description = "Generic interface to multiple Ruby template engines"
|
10
10
|
s.summary = s.description
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tilt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: asciidoctor
|