class Tilt::Template

Base class for template implementations. Subclasses must implement the prepare method and one of the evaluate or precompiled_template methods.

Constants

CLASS_METHOD
USE_BIND_CALL

Attributes

compiled_path[R]

A path ending in .rb that the template code will be written to, then required, instead of being evaled. This is useful for determining coverage of compiled template code, or to use static analysis tools on the compiled template code.

data[R]

Template source; loaded from a file or given directly.

default_encoding[R]

The encoding of the source data. Defaults to the default_encoding-option if present. You may override this method in your template class if you have a better hint of the data's encoding.

file[R]

The name of the file where the template data was loaded from.

line[R]

The line number in file where template data was loaded from.

options[R]

A Hash of template engine specific options. This is passed directly to the underlying engine and is not used by the generic template interface.

Public Class Methods

default_mime_type() click to toggle source

@deprecated Use `.metadata` instead.

   # File lib/tilt/template.rb
44 def default_mime_type
45   metadata[:mime_type]
46 end
default_mime_type=(value) click to toggle source

@deprecated Use `.metadata = val` instead.

   # File lib/tilt/template.rb
49 def default_mime_type=(value)
50   metadata[:mime_type] = value
51 end
metadata() click to toggle source

An empty Hash that the template engine can populate with various metadata.

   # File lib/tilt/template.rb
39 def metadata
40   @metadata ||= {}
41 end
new(file=nil, line=1, options={}, &block) click to toggle source

Create a new template with the file, line, and options specified. By default, template data is read from the file. When a block is given, it should read template data and return as a String. When file is nil, a block is required.

All arguments are optional.

    # File lib/tilt/template.rb
 60 def initialize(file=nil, line=1, options={}, &block)
 61   @file, @line, @options = nil, 1, {}
 62 
 63   [options, line, file].compact.each do |arg|
 64     case
 65     when arg.respond_to?(:to_str)  ; @file = arg.to_str
 66     when arg.respond_to?(:to_int)  ; @line = arg.to_int
 67     when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
 68     when arg.respond_to?(:path)    ; @file = arg.path
 69     when arg.respond_to?(:to_path) ; @file = arg.to_path
 70     else raise TypeError, "Can't load the template file. Pass a string with a path " +
 71       "or an object that responds to 'to_str', 'path' or 'to_path'"
 72     end
 73   end
 74 
 75   raise ArgumentError, "file or block required" if (@file || block).nil?
 76 
 77   # used to hold compiled template methods
 78   @compiled_method = {}
 79 
 80   # used on 1.9 to set the encoding if it is not set elsewhere (like a magic comment)
 81   # currently only used if template compiles to ruby
 82   @default_encoding = @options.delete :default_encoding
 83 
 84   # load template data and prepare (uses binread to avoid encoding issues)
 85   @reader = block || lambda { |t| read_template_file }
 86   @data = @reader.call(self)
 87 
 88   if @data.respond_to?(:force_encoding)
 89     if default_encoding
 90       @data = @data.dup if @data.frozen?
 91       @data.force_encoding(default_encoding)
 92     end
 93 
 94     if !@data.valid_encoding?
 95       raise Encoding::InvalidByteSequenceError, "#{eval_file} is not valid #{@data.encoding}"
 96     end
 97   end
 98 
 99   prepare
100 end

Public Instance Methods

basename(suffix='') click to toggle source

The basename of the template file.

    # File lib/tilt/template.rb
115 def basename(suffix='')
116   File.basename(file, suffix) if file
117 end
compiled_path=(path) click to toggle source

Set the prefix to use for compiled paths.

    # File lib/tilt/template.rb
140 def compiled_path=(path)
141   if path
142     # Use expanded paths when loading, since that is helpful
143     # for coverage.  Remove any .rb suffix, since that will
144     # be added back later.
145     path = File.expand_path(path.sub(/\.rb\z/i, ''))
146   end
147   @compiled_path = path
148 end
eval_file() click to toggle source

The filename used in backtraces to describe the template.

    # File lib/tilt/template.rb
125 def eval_file
126   file || '(__TEMPLATE__)'
127 end
metadata() click to toggle source

An empty Hash that the template engine can populate with various metadata.

    # File lib/tilt/template.rb
131 def metadata
132   if respond_to?(:allows_script?)
133     self.class.metadata.merge(:allows_script => allows_script?)
134   else
135     self.class.metadata
136   end
137 end
name() click to toggle source

The template file's basename with all extensions chomped off.

    # File lib/tilt/template.rb
120 def name
121   basename.split('.', 2).first if basename
122 end
render(scope=nil, locals={}, &block) click to toggle source

Render the template in the given scope with the locals specified. If a block is given, it is typically available within the template via yield.

    # File lib/tilt/template.rb
105 def render(scope=nil, locals={}, &block)
106   scope ||= Object.new
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
112 end

Protected Instance Methods

evaluate(scope, locals, &block) click to toggle source

Execute the compiled template and return the result string. Template evaluation is guaranteed to be performed in the scope object with the locals specified and with support for yielding to the block.

This method is only used by source generating templates. Subclasses that override render() may not support all features.

    # File lib/tilt/template.rb
178 def evaluate(scope, locals, &block)
179   locals_keys = locals.keys
180   locals_keys.sort!{|x, y| x.to_s <=> y.to_s}
181 
182   case scope
183   when Object
184     scope_class = Module === scope ? scope : scope.class
185   else
186     scope_class = USE_BIND_CALL ? CLASS_METHOD.bind_call(scope) : CLASS_METHOD.bind(scope).call
187   end
188   method = compiled_method(locals_keys, scope_class)
189 
190   if USE_BIND_CALL
191     method.bind_call(scope, locals, &block)
192   else
193     method.bind(scope).call(locals, &block)
194   end
195 end
precompiled(local_keys) click to toggle source

Generates all template source by combining the preamble, template, and postamble and returns a two-tuple of the form: [source, offset], where source is the string containing (Ruby) source code for the template and offset is the integer line offset where line reporting should begin.

Template subclasses may override this method when they need complete control over source generation or want to adjust the default line offset. In most cases, overriding the precompiled_template method is easier and more appropriate.

    # File lib/tilt/template.rb
206 def precompiled(local_keys)
207   preamble = precompiled_preamble(local_keys)
208   template = precompiled_template(local_keys)
209   postamble = precompiled_postamble(local_keys)
210   source = String.new
211 
212   # Ensure that our generated source code has the same encoding as the
213   # the source code generated by the template engine.
214   if source.respond_to?(:force_encoding)
215     template_encoding = extract_encoding(template)
216 
217     source.force_encoding(template_encoding)
218     template.force_encoding(template_encoding)
219   end
220 
221   source << preamble << "\n" << template << "\n" << postamble
222 
223   [source, preamble.count("\n")+1]
224 end
precompiled_postamble(local_keys) click to toggle source
    # File lib/tilt/template.rb
240 def precompiled_postamble(local_keys)
241   ''
242 end
precompiled_preamble(local_keys) click to toggle source
    # File lib/tilt/template.rb
236 def precompiled_preamble(local_keys)
237   ''
238 end
precompiled_template(local_keys) click to toggle source

A string containing the (Ruby) source code for the template. The default Template#evaluate implementation requires either this method or the precompiled method be overridden. When defined, the base Template guarantees correct file/line handling, locals support, custom scopes, proper encoding, and support for template compilation.

    # File lib/tilt/template.rb
232 def precompiled_template(local_keys)
233   raise NotImplementedError
234 end
prepare() click to toggle source

Do whatever preparation is necessary to setup the underlying template engine. Called immediately after template data is loaded. Instance variables set in this method are available when evaluate is called.

Subclasses must provide an implementation of this method.

    # File lib/tilt/template.rb
165 def prepare
166   raise NotImplementedError
167 end

Private Instance Methods

binary(string) { || ... } click to toggle source
    # File lib/tilt/template.rb
357 def binary(string)
358   original_encoding = string.encoding
359   string.force_encoding(Encoding::BINARY)
360   yield
361 ensure
362   string.force_encoding(original_encoding)
363 end
bind_compiled_method(method_source, offset, scope_class, local_keys) click to toggle source
    # File lib/tilt/template.rb
300 def bind_compiled_method(method_source, offset, scope_class, local_keys)
301   path = compiled_path
302   if path && scope_class.name
303     path = path.dup
304 
305     if defined?(@compiled_path_counter)
306       path << '-' << @compiled_path_counter.succ!
307     else
308       @compiled_path_counter = "0".dup
309     end
310     path << ".rb"
311 
312     # Wrap method source in a class block for the scope, so constant lookup works
313     method_source = "class #{scope_class.name}\n#{method_source}\nend"
314 
315     load_compiled_method(path, method_source)
316   else
317     if path
318       warn "compiled_path (#{compiled_path.inspect}) ignored on template with anonymous scope_class (#{scope_class.inspect})"
319     end
320 
321     eval_compiled_method(method_source, offset, scope_class)
322   end
323 end
compile_template_method(local_keys, scope_class=nil) click to toggle source
    # File lib/tilt/template.rb
274 def compile_template_method(local_keys, scope_class=nil)
275   source, offset = precompiled(local_keys)
276   local_code = local_extraction(local_keys)
277 
278   method_name = "__tilt_#{Thread.current.object_id.abs}"
279   method_source = String.new
280 
281   if method_source.respond_to?(:force_encoding)
282     method_source.force_encoding(source.encoding)
283   end
284 
285   if freeze_string_literals?
286     method_source << "# frozen-string-literal: true\n"
287   end
288 
289   # Don't indent method source, to avoid indentation warnings when using compiled paths
290   method_source << "::Tilt::TOPOBJECT.class_eval do\ndef #{method_name}(locals)\n#{local_code}\n"
291 
292   offset += method_source.count("\n")
293   method_source << source
294   method_source << "\nend;end;"
295 
296   bind_compiled_method(method_source, offset, scope_class, local_keys)
297   unbind_compiled_method(method_name)
298 end
compiled_method(locals_keys, scope_class=nil) click to toggle source

The compiled method for the locals keys provided.

    # File lib/tilt/template.rb
258 def compiled_method(locals_keys, scope_class=nil)
259   LOCK.synchronize do
260     @compiled_method[[scope_class, locals_keys]] ||= compile_template_method(locals_keys, scope_class)
261   end
262 end
eval_compiled_method(method_source, offset, scope_class) click to toggle source
    # File lib/tilt/template.rb
325 def eval_compiled_method(method_source, offset, scope_class)
326   (scope_class || Object).class_eval(method_source, eval_file, line - offset)
327 end
extract_encoding(script) click to toggle source
    # File lib/tilt/template.rb
343 def extract_encoding(script)
344   extract_magic_comment(script) || script.encoding
345 end
extract_magic_comment(script) click to toggle source
    # File lib/tilt/template.rb
347 def extract_magic_comment(script)
348   binary(script) do
349     script[/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/n, 1]
350   end
351 end
freeze_string_literals?() click to toggle source
    # File lib/tilt/template.rb
353 def freeze_string_literals?
354   false
355 end
load_compiled_method(path, method_source) click to toggle source
    # File lib/tilt/template.rb
329 def load_compiled_method(path, method_source)
330   File.binwrite(path, method_source)
331 
332   # Use load and not require, so unbind_compiled_method does not
333   # break if the same path is used more than once.
334   load path
335 end
local_extraction(local_keys) click to toggle source
    # File lib/tilt/template.rb
264 def local_extraction(local_keys)
265   local_keys.map do |k|
266     if k.to_s =~ /\A[a-z_][a-zA-Z_0-9]*\z/
267       "#{k} = locals[#{k.inspect}]"
268     else
269       raise "invalid locals key: #{k.inspect} (keys must be variable names)"
270     end
271   end.join("\n")
272 end
read_template_file() click to toggle source

!@endgroup

    # File lib/tilt/template.rb
248 def read_template_file
249   data = File.open(file, 'rb') { |io| io.read }
250   if data.respond_to?(:force_encoding)
251     # Set it to the default external (without verifying)
252     data.force_encoding(Encoding.default_external) if Encoding.default_external
253   end
254   data
255 end
unbind_compiled_method(method_name) click to toggle source
    # File lib/tilt/template.rb
337 def unbind_compiled_method(method_name)
338   method = TOPOBJECT.instance_method(method_name)
339   TOPOBJECT.class_eval { remove_method(method_name) }
340   method
341 end