tilt 1.3.4 → 1.4.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ ## master
2
+
3
+ ## 1.4.0 (2013-05-01)
4
+
5
+ * Better encoding support
6
+
7
+ ## 1.3.7 (2013-04-09)
8
+
9
+ * Erubis: Check for the correct constant (#183, mattwildig)
10
+ * Don't fail when BasicObject is defined in 1.8 (#182, technobrat, judofyr)
11
+
12
+ ## 1.3.6 (2013-03-17)
13
+
14
+ * Accept Hash that implements #path as options (#180, lawso017)
15
+ * Changed extension for CsvTemplate from '.csv' to '.rcsv' (#177, alexgb)
16
+
17
+ ## 1.3.5 (2013-03-06)
18
+
19
+ * Fixed extension for PlainTemplate (judofyr)
20
+ * Improved local variables regexp (#174, razorinc)
21
+ * Added CHANGELOG.md
22
+
23
+ ## 1.3.4 (2013-02-28)
24
+
25
+ * Support RDoc 4.0 (#168, judofyr)
26
+ * Add mention of Org-Mode support (#165, aslakknutsen)
27
+ * Add AsciiDoctorTemplate (#163, #164, aslakknutsen)
28
+ * Add PlainTextTemplate (nathanaeljones)
29
+ * Restrict locals to valid variable names (#158, thinkerbot)
30
+ * ERB: Improve trim mode support (#156, ssimeonov)
31
+ * Add CSVTemplate (#153, alexgb)
32
+ * Remove special case for 1.9.1 (#147, guilleiguaran)
33
+ * Add allows\_script? method to Template (#143, bhollis)
34
+ * Default to using Redcarpet2 (#139, DAddYE)
35
+ * Allow File/Tempfile as filenames (#134, jamesotron)
36
+ * Add EtanniTemplate (#131, manveru)
37
+ * Support RDoc 3.10 (#112, timfel)
38
+ * Always compile templates; remove old source evaluator (rtomayko)
39
+ * Less: Options are now being passed to the parser (#106, cowboyd)
40
+
data/README.md CHANGED
@@ -47,7 +47,7 @@ Support for these template engines is included with the package:
47
47
  Creole (Wiki markup) .wiki, .creole creole
48
48
  WikiCloth (Wiki markup) .wiki, .mediawiki, .mw wikicloth
49
49
  Yajl .yajl yajl-ruby
50
- CSV .csv none (Ruby >= 1.9), fastercsv (Ruby < 1.9)
50
+ CSV .rcsv none (Ruby >= 1.9), fastercsv (Ruby < 1.9)
51
51
 
52
52
  These template engines ship with their own Tilt integration:
53
53
 
@@ -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/erb.rb CHANGED
@@ -74,7 +74,7 @@ module Tilt
74
74
  # within <%= %> blocks will be automatically html escaped.
75
75
  class ErubisTemplate < ERBTemplate
76
76
  def self.engine_initialized?
77
- defined? ::Erubis
77
+ defined? ::Erubis::Eruby
78
78
  end
79
79
 
80
80
  def initialize_engine
data/lib/tilt/template.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Tilt
2
- TOPOBJECT = defined?(BasicObject) ? BasicObject : Object
2
+ TOPOBJECT = Object.superclass || Object
3
3
 
4
4
  # Base class for template implementations. Subclasses must implement
5
5
  # the #prepare method and one of the #evaluate or #precompiled_template
@@ -41,9 +41,9 @@ module Tilt
41
41
  [options, line, file].compact.each do |arg|
42
42
  case
43
43
  when arg.respond_to?(:to_str) ; @file = arg.to_str
44
- when arg.respond_to?(:path) ; @file = arg.path
45
44
  when arg.respond_to?(:to_int) ; @line = arg.to_int
46
45
  when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
46
+ when arg.respond_to?(:path) ; @file = arg.path
47
47
  else raise TypeError
48
48
  end
49
49
  end
@@ -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| File.respond_to?(:binread) ? File.binread(@file) : File.read(@file) }
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
- magic_comment = extract_magic_comment(template)
160
- if magic_comment
161
- # Magic comment e.g. "# coding: utf-8" has to be in the first line.
162
- # So we copy the magic comment to the first line.
163
- preamble = magic_comment + "\n" + preamble
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
- parts = [
166
- preamble,
167
- template,
168
- precompiled_postamble(locals)
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 method
175
- # or the #precompiled method be overridden. When defined, the base
176
- # Template guarantees correct file/line handling, locals support, custom
177
- # scopes, and support for template compilation when the scope object
178
- # allows it.
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
@@ -187,7 +216,7 @@ module Tilt
187
216
  # reporting in Kernel::caller and backtraces.
188
217
  def precompiled_preamble(locals)
189
218
  locals.map do |k,v|
190
- if k.to_s =~ /\A[a-z]\w*\z/
219
+ if k.to_s =~ /\A[a-z_][a-zA-Z_0-9]*\z/
191
220
  "#{k} = locals[#{k.inspect}]"
192
221
  else
193
222
  raise "invalid locals key: #{k.inspect} (keys must be variable names)"
@@ -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 = <<-RUBY
216
- #{extract_magic_comment source}
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
- comment = script.slice(/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/)
239
- if comment && !%w[ascii-8bit binary].include?($1.downcase)
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/lib/tilt.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Tilt
2
- VERSION = '1.3.4'
2
+ VERSION = '1.4.0'
3
3
 
4
4
  @preferred_mappings = Hash.new
5
5
  @template_mappings = Hash.new { |h, k| h[k] = [] }
@@ -154,7 +154,7 @@ module Tilt
154
154
  register LessTemplate, 'less'
155
155
 
156
156
  require 'tilt/csv'
157
- register CSVTemplate, 'csv'
157
+ register CSVTemplate, 'rcsv'
158
158
 
159
159
  require 'tilt/coffee'
160
160
  register CoffeeScriptTemplate, 'coffee'
@@ -200,5 +200,5 @@ module Tilt
200
200
  register AsciidoctorTemplate, 'ad', 'adoc', 'asciidoc'
201
201
 
202
202
  require 'tilt/plain'
203
- register PlainTemplate, 'htmf'
203
+ register PlainTemplate, 'html'
204
204
  end
@@ -10,12 +10,8 @@ begin
10
10
 
11
11
  class CSVTemplateTest < Test::Unit::TestCase
12
12
 
13
- test "is registered for '.csv' files" do
14
- assert_equal Tilt::CSVTemplate, Tilt['test.csv']
15
- end
16
-
17
- test "registered for '.csv' files" do
18
- assert Tilt.mappings['csv'].include?(Tilt::CSVTemplate)
13
+ test "registered for '.rcsv' files" do
14
+ assert Tilt.mappings['rcsv'].include?(Tilt::CSVTemplate)
19
15
  end
20
16
 
21
17
  test "compiles and evaluates the template on #render" do
@@ -18,7 +18,7 @@ module MarkdownTests
18
18
  end
19
19
 
20
20
  def normalize(html)
21
- Nokogiri::HTML.fragment(html).to_s
21
+ Nokogiri::HTML.fragment(html).to_s.strip
22
22
  end
23
23
 
24
24
  def nrender(text, options = {})
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  require 'contest'
2
3
  require 'tilt'
3
4
  require 'tempfile'
@@ -30,6 +31,17 @@ class TiltTemplateTest < Test::Unit::TestCase
30
31
  assert_equal File.basename(tempfile.path), inst.basename
31
32
  end
32
33
 
34
+ class SillyHash < Hash
35
+ def path(arg)
36
+ end
37
+ end
38
+
39
+ test "initialize with hash that implements #path" do
40
+ options = SillyHash[:key => :value]
41
+ inst = MockTemplate.new(options) {}
42
+ assert_equal :value, inst.options[:key]
43
+ end
44
+
33
45
  test "uses correct eval_file" do
34
46
  inst = MockTemplate.new('foo.erb', 55) {}
35
47
  assert_equal 'foo.erb', inst.eval_file
@@ -135,9 +147,10 @@ class TiltTemplateTest < Test::Unit::TestCase
135
147
  end
136
148
 
137
149
  test "template_source with locals having non-variable keys raises error" do
138
- inst = SourceGeneratingMockTemplate.new { |t| '1 + 2 = #{ANSWER}' }
139
- err = assert_raise(RuntimeError) { inst.render(Object.new, 'ANSWER' => '3') }
150
+ inst = SourceGeneratingMockTemplate.new { |t| '1 + 2 = #{_answer}' }
151
+ err = assert_raise(RuntimeError) { inst.render(Object.new, 'ANSWER' => 3) }
140
152
  assert_equal "invalid locals key: \"ANSWER\" (keys must be variable names)", err.message
153
+ assert_equal "1 + 2 = 3", inst.render(Object.new, '_answer' => 3)
141
154
  end
142
155
 
143
156
  class Person
@@ -164,4 +177,100 @@ class TiltTemplateTest < Test::Unit::TestCase
164
177
  inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{CONSTANT}!' }
165
178
  assert_equal "Hey Bob!", inst.render(Person.new("Joe"))
166
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
167
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.3.4'
7
- s.date = '2013-02-28'
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
@@ -15,6 +15,7 @@ Gem::Specification.new do |s|
15
15
 
16
16
  # = MANIFEST =
17
17
  s.files = %w[
18
+ CHANGELOG.md
18
19
  COPYING
19
20
  Gemfile
20
21
  HACKING
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.3.4
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-02-28 00:00:00.000000000 Z
12
+ date: 2013-05-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: asciidoctor
@@ -386,6 +386,7 @@ executables:
386
386
  extensions: []
387
387
  extra_rdoc_files: []
388
388
  files:
389
+ - CHANGELOG.md
389
390
  - COPYING
390
391
  - Gemfile
391
392
  - HACKING
@@ -481,7 +482,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
481
482
  version: '0'
482
483
  requirements: []
483
484
  rubyforge_project:
484
- rubygems_version: 1.8.23
485
+ rubygems_version: 1.8.25
485
486
  signing_key:
486
487
  specification_version: 2
487
488
  summary: Generic interface to multiple Ruby template engines