class Template
Base class for template implementations. Subclasses must implement the prepare
method and one of the evaluate
or precompiled_template
methods.
Constants
- CLASS_METHOD
Attributes
Template
source; loaded from a file or given directly.
The name of the file where the template data was loaded from.
The line number in file
where template data was loaded from.
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
@deprecated Use ‘.metadata` instead.
# File lib/tilt/template.rb 44 def default_mime_type 45 metadata[:mime_type] 46 end
@deprecated Use ‘.metadata = val` instead.
# File lib/tilt/template.rb 49 def default_mime_type=(value) 50 metadata[:mime_type] = value 51 end
An empty Hash that the template engine can populate with various metadata.
# File lib/tilt/template.rb 39 def metadata 40 @metadata ||= {} 41 end
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
The basename of the template file.
# File lib/tilt/template.rb 115 def basename(suffix='') 116 File.basename(file, suffix) if file 117 end
The filename used in backtraces to describe the template.
# File lib/tilt/template.rb 125 def eval_file 126 file || '(__TEMPLATE__)' 127 end
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
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 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
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 lib/tilt/template.rb 147 def default_encoding 148 @default_encoding 149 end
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 168 def evaluate(scope, locals, &block) 169 locals_keys = locals.keys 170 locals_keys.sort!{|x, y| x.to_s <=> y.to_s} 171 case scope 172 when Object 173 method = compiled_method(locals_keys, Module === scope ? scope : scope.class) 174 else 175 if RUBY_VERSION >= '2' 176 method = compiled_method(locals_keys, CLASS_METHOD.bind(scope).call) 177 else 178 method = compiled_method(locals_keys, Object) 179 end 180 end 181 method.bind(scope).call(locals, &block) 182 end
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 193 def precompiled(local_keys) 194 preamble = precompiled_preamble(local_keys) 195 template = precompiled_template(local_keys) 196 postamble = precompiled_postamble(local_keys) 197 source = String.new 198 199 # Ensure that our generated source code has the same encoding as the 200 # the source code generated by the template engine. 201 if source.respond_to?(:force_encoding) 202 template_encoding = extract_encoding(template) 203 204 source.force_encoding(template_encoding) 205 template.force_encoding(template_encoding) 206 end 207 208 source << preamble << "\n" << template << "\n" << postamble 209 210 [source, preamble.count("\n")+1] 211 end
# File lib/tilt/template.rb 227 def precompiled_postamble(local_keys) 228 '' 229 end
# File lib/tilt/template.rb 223 def precompiled_preamble(local_keys) 224 '' 225 end
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 219 def precompiled_template(local_keys) 220 raise NotImplementedError 221 end
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 156 def prepare 157 raise NotImplementedError 158 end
Private Instance Methods
# File lib/tilt/template.rb 300 def binary(string) 301 original_encoding = string.encoding 302 string.force_encoding(Encoding::BINARY) 303 yield 304 ensure 305 string.force_encoding(original_encoding) 306 end
# File lib/tilt/template.rb 261 def compile_template_method(local_keys, scope_class=nil) 262 source, offset = precompiled(local_keys) 263 local_code = local_extraction(local_keys) 264 265 method_name = "__tilt_#{Thread.current.object_id.abs}" 266 method_source = String.new 267 268 if method_source.respond_to?(:force_encoding) 269 method_source.force_encoding(source.encoding) 270 end 271 272 method_source << <<-RUBY 273 TOPOBJECT.class_eval do 274 def #{method_name}(locals) 275 #{local_code} 276 RUBY 277 offset += method_source.count("\n") 278 method_source << source 279 method_source << "\nend;end;" 280 (scope_class || Object).class_eval(method_source, eval_file, line - offset) 281 unbind_compiled_method(method_name) 282 end
The compiled method for the locals keys provided.
# File lib/tilt/template.rb 245 def compiled_method(locals_keys, scope_class=nil) 246 LOCK.synchronize do 247 @compiled_method[[scope_class, locals_keys]] ||= compile_template_method(locals_keys, scope_class) 248 end 249 end
# File lib/tilt/template.rb 290 def extract_encoding(script) 291 extract_magic_comment(script) || script.encoding 292 end
# File lib/tilt/template.rb 294 def extract_magic_comment(script) 295 binary(script) do 296 script[/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/n, 1] 297 end 298 end
# File lib/tilt/template.rb 251 def local_extraction(local_keys) 252 local_keys.map do |k| 253 if k.to_s =~ /\A[a-z_][a-zA-Z_0-9]*\z/ 254 "#{k} = locals[#{k.inspect}]" 255 else 256 raise "invalid locals key: #{k.inspect} (keys must be variable names)" 257 end 258 end.join("\n") 259 end
!@endgroup
# File lib/tilt/template.rb 235 def read_template_file 236 data = File.open(file, 'rb') { |io| io.read } 237 if data.respond_to?(:force_encoding) 238 # Set it to the default external (without verifying) 239 data.force_encoding(Encoding.default_external) if Encoding.default_external 240 end 241 data 242 end
# File lib/tilt/template.rb 284 def unbind_compiled_method(method_name) 285 method = TOPOBJECT.instance_method(method_name) 286 TOPOBJECT.class_eval { remove_method(method_name) } 287 method 288 end