class Template
Base class for template implementations. Subclasses must implement the prepare
method and one of the evaluate
or precompiled_template
methods.
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 166 def evaluate(scope, locals, &block) 167 locals_keys = locals.keys 168 locals_keys.sort!{|x, y| x.to_s <=> y.to_s} 169 method = compiled_method(locals_keys) 170 method.bind(scope).call(locals, &block) 171 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 182 def precompiled(local_keys) 183 preamble = precompiled_preamble(local_keys) 184 template = precompiled_template(local_keys) 185 postamble = precompiled_postamble(local_keys) 186 source = String.new 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) 195 end 196 197 source << preamble << "\n" << template << "\n" << postamble 198 199 [source, preamble.count("\n")+1] 200 end
# File lib/tilt/template.rb 216 def precompiled_postamble(local_keys) 217 '' 218 end
# File lib/tilt/template.rb 212 def precompiled_preamble(local_keys) 213 '' 214 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 208 def precompiled_template(local_keys) 209 raise NotImplementedError 210 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 294 def binary(string) 295 original_encoding = string.encoding 296 string.force_encoding(Encoding::BINARY) 297 yield 298 ensure 299 string.force_encoding(original_encoding) 300 end
# File lib/tilt/template.rb 250 def compile_template_method(local_keys) 251 source, offset = precompiled(local_keys) 252 local_code = local_extraction(local_keys) 253 254 method_name = "__tilt_#{Thread.current.object_id.abs}" 255 method_source = String.new 256 257 if method_source.respond_to?(:force_encoding) 258 method_source.force_encoding(source.encoding) 259 end 260 261 method_source << <<-RUBY 262 TOPOBJECT.class_eval do 263 def #{method_name}(locals) 264 Thread.current[:tilt_vars] = [self, locals] 265 class << self 266 this, locals = Thread.current[:tilt_vars] 267 locals = locals 268 this.instance_eval do 269 #{local_code} 270 RUBY 271 offset += method_source.count("\n") 272 method_source << source 273 method_source << "\nend;end;end;end" 274 Object.class_eval(method_source, eval_file, line - offset) 275 unbind_compiled_method(method_name) 276 end
The compiled method for the locals keys provided.
# File lib/tilt/template.rb 234 def compiled_method(locals_keys) 235 LOCK.synchronize do 236 @compiled_method[locals_keys] ||= compile_template_method(locals_keys) 237 end 238 end
# File lib/tilt/template.rb 284 def extract_encoding(script) 285 extract_magic_comment(script) || script.encoding 286 end
# File lib/tilt/template.rb 288 def extract_magic_comment(script) 289 binary(script) do 290 script[/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/n, 1] 291 end 292 end
# File lib/tilt/template.rb 240 def local_extraction(local_keys) 241 local_keys.map do |k| 242 if k.to_s =~ /\A[a-z_][a-zA-Z_0-9]*\z/ 243 "#{k} = locals[#{k.inspect}]" 244 else 245 raise "invalid locals key: #{k.inspect} (keys must be variable names)" 246 end 247 end.join("\n") 248 end
!@endgroup
# File lib/tilt/template.rb 224 def read_template_file 225 data = File.open(file, 'rb') { |io| io.read } 226 if data.respond_to?(:force_encoding) 227 # Set it to the default external (without verifying) 228 data.force_encoding(Encoding.default_external) if Encoding.default_external 229 end 230 data 231 end
# File lib/tilt/template.rb 278 def unbind_compiled_method(method_name) 279 method = TOPOBJECT.instance_method(method_name) 280 TOPOBJECT.class_eval { remove_method(method_name) } 281 method 282 end