Class: Inversion::Template
- Inherits:
-
Object
- Object
- Inversion::Template
- Extended by:
- Configurability, Loggability
- Includes:
- DataUtilities, TemplateTiltAdditions
- Defined in:
- lib/inversion/tilt.rb,
lib/inversion/template.rb
Overview
The main template class.
Inversion templates are the primary objects you’ll be interacting with. Templates can be created from a string:
Inversion::Template.new( template_source )
or from a file:
Inversion::Template.load( 'path/to/template.tmpl' )
Template Options
Inversion supports the [Configurability](rubygems.org/gems/configurability) API, and registers itself with the ‘templates` key. This means you can either add a `templates` section to your Configurability config, or call ::configure yourself with a config Hash (or something that quacks like one).
To set options on a per-template basis, you can pass an options hash to either Inversion::Template::load or Inversion::Template::new, or set them from within the template itself using the [config tag](Tags@config).
The available options are:
- :ignore_unknown_tags
-
Setting to false causes unknown tags used in templates to raise an Inversion::ParseError. Defaults to ‘true`.
- :on_render_error
-
Dictates the behavior of exceptions during rendering. Defaults to ‘:comment`.
- :ignore
-
Exceptions are silently ignored.
- :comment
-
Exceptions are rendered inline as comments.
- :propagate
-
Exceptions bubble up to the caller of Inversion::Template#render.
- :debugging_comments
-
Insert various Inversion parse and render statements while rendering. Defaults to ‘false`.
- :comment_start
-
When rendering debugging comments, the comment is started with these characters. Defaults to
"<!--"
. - :comment_end
-
When rendering debugging comments, the comment is finished with these characters. Defaults to
"-->"
. - :template_paths
-
An array of filesystem paths to search for templates within, when loaded or included with a relative path. The current working directory is always the last checked member of this. Defaults to
[]
. - :escape_format
-
The escaping used by tags such as ‘escape` and `pp`. Default: `:html`.
- :strip_tag_lines
-
If a tag’s presence introduces a blank line into the output, this option removes it. Defaults to ‘true`.
- :stat_delay
-
Templates know when they’ve been altered on disk, and can dynamically reload themselves in long running applications. Setting this option creates a purposeful delay between reloads for busy servers. Defaults to ‘0` (disabled).
- :strict_attributes
-
Disable getting/setting attributes that aren’t explicitly declared with a tag. Trying to get/set an attribute that isn’t declared in the template with this option enabled will result in a NoMethodError being raised.
Defined Under Namespace
Modules: ContainerTag Classes: AttrTag, BeginTag, CallTag, CodeTag, CommentTag, ConfigTag, DefaultTag, ElseTag, ElsifTag, EndTag, EscapeTag, ForTag, FragmentTag, IfTag, ImportTag, IncludeTag, Node, PpTag, PublishTag, RescueTag, SubscribeTag, Tag, TextNode, TimeDeltaTag, UnlessTag, UriencodeTag, YieldTag
Constant Summary collapse
- VALID_ERROR_ACTIONS =
Valid actions for ‘on_render_error’
[ :ignore, :comment, :propagate, ]
- DEFAULT_CONFIG =
Default config values
{ # Loading/parsing options :ignore_unknown_tags => true, :template_paths => [], :stat_delay => 0, :strict_attributes => false, # Rendering options :on_render_error => :comment, :debugging_comments => false, :comment_start => '<!-- ', :comment_end => ' -->', :escape_format => :html, :strip_tag_lines => true, }.freeze
Class Attribute Summary collapse
-
.config ⇒ Object
Returns the value of attribute config.
-
.template_paths ⇒ Object
Returns the value of attribute template_paths.
Instance Attribute Summary collapse
-
#attributes ⇒ Object
readonly
The Hash of template attributes.
-
#fragments ⇒ Object
readonly
The Hash of rendered template fragments.
-
#node_tree ⇒ Object
readonly
The node tree parsed from the template source.
-
#options ⇒ Object
readonly
The Template’s configuration options hash.
-
#source ⇒ Object
readonly
The raw template source from which the object was parsed.
-
#source_file ⇒ Object
The Pathname of the file the source was read from.
Class Method Summary collapse
-
.add_extensions(*modules) ⇒ Object
Add one or more extension ‘modules` to Inversion::Template.
-
.configure(config) ⇒ Object
Configure the templating system.
-
.load(path, parsestate = nil, opts = {}) ⇒ Object
Read a template object from the specified ‘path`.
Instance Method Summary collapse
-
#changed? ⇒ Boolean
Returns ‘true` if the template was loaded from a file and the file’s mtime is after the time the template was created.
-
#initialize(source, parsestate = nil, opts = {}) ⇒ Template
constructor
Create a new Inversion:Template with the given ‘source`.
-
#initialize_copy(other) ⇒ Object
Copy constructor – make copies of some internal data structures, too.
-
#inspect ⇒ Object
Return a human-readable representation of the template object suitable for debugging.
-
#reload ⇒ Object
If the template was loaded from a file, reload and reparse it from the same file.
-
#render(parentstate = nil, &block) ⇒ Object
(also: #to_s)
Render the template, optionally passing a render state (if, for example, the template is being rendered inside another template).
Methods included from DataUtilities
Methods included from TemplateTiltAdditions
Constructor Details
#initialize(source, parsestate = nil, opts = {}) ⇒ Template
Create a new Inversion:Template with the given ‘source`.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/inversion/template.rb', line 260 def initialize( source, parsestate=nil, opts={} ) if parsestate.is_a?( Hash ) # self.log.debug "Shifting template options: %p" % [ parsestate ] opts = parsestate parsestate = nil else self.log.debug "Parse state is: %p" % [ parsestate ] end @source = source @node_tree = [] # Parser expects this to always be an Array @options = self.class.config.merge( opts ) @attributes = {} @fragments = {} @source_file = nil @created_at = Time.now @last_checked = @created_at self.parse( source, parsestate ) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args, &block) ⇒ Object (protected)
Proxy method: handle attribute readers/writers for attributes that aren’t yet defined.
393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/inversion/template.rb', line 393 def method_missing( sym, *args, &block ) return super unless sym.to_s =~ /^([a-z]\w+)=?$/i attribute = $1 raise NoMethodError, "no tag attribute '%s' (strict mode)" % [ attribute ] if self.[:strict_attributes] self.install_accessors( attribute ) # Call the new method via #method to avoid a method_missing loop. return self.method( sym ).call( *args, &block ) end |
Class Attribute Details
.config ⇒ Object
Returns the value of attribute config.
140 141 142 |
# File 'lib/inversion/template.rb', line 140 def config @config end |
.template_paths ⇒ Object
Returns the value of attribute template_paths.
145 146 147 |
# File 'lib/inversion/template.rb', line 145 def template_paths @template_paths end |
Instance Attribute Details
#attributes ⇒ Object (readonly)
The Hash of template attributes
301 302 303 |
# File 'lib/inversion/template.rb', line 301 def attributes @attributes end |
#fragments ⇒ Object (readonly)
The Hash of rendered template fragments
304 305 306 |
# File 'lib/inversion/template.rb', line 304 def fragments @fragments end |
#node_tree ⇒ Object (readonly)
The node tree parsed from the template source
310 311 312 |
# File 'lib/inversion/template.rb', line 310 def node_tree @node_tree end |
#options ⇒ Object (readonly)
The Template’s configuration options hash
307 308 309 |
# File 'lib/inversion/template.rb', line 307 def @options end |
#source ⇒ Object (readonly)
The raw template source from which the object was parsed.
295 296 297 |
# File 'lib/inversion/template.rb', line 295 def source @source end |
#source_file ⇒ Object
The Pathname of the file the source was read from
298 299 300 |
# File 'lib/inversion/template.rb', line 298 def source_file @source_file end |
Class Method Details
.add_extensions(*modules) ⇒ Object
Add one or more extension ‘modules` to Inversion::Template. This allows tags to decorate the template class with new functionality.
Each one of the given ‘modules` will be included as a mixin, and if it also contains a constant called ClassMethods and/or PrependedMethods, it will also be extended/prepended (respectively) with it.
Example
Add a layout attribute to templates from a ‘layout’ tag:
class Inversion::Template::LayoutTag < Inversion::Tag
module TemplateExtension
def layout
return @layout || 'default.tmpl'
end
module PrependedMethods
def initialize( * )
super
@layout = nil
end
end
Inversion::Template.add_extensions( TemplateExtension )
# ... more tag stuff
end
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/inversion/template.rb', line 241 def self::add_extensions( *modules ) self.log.info "Adding extensions to %p: %p" % [ self, modules ] modules.each do |mod| include( mod ) if mod.const_defined?( :ClassMethods ) submod = mod.const_get( :ClassMethods ) extend( submod ) end if mod.const_defined?( :PrependedMethods ) submod = mod.const_get( :PrependedMethods ) prepend( submod ) end end end |
.configure(config) ⇒ Object
Configure the templating system.
150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/inversion/template.rb', line 150 def self::configure( config ) if config Inversion.log.debug "Merging config %p with current config %p" % [ config, self.config ] merged_config = DEFAULT_CONFIG.merge( config ) self.template_paths = Array( merged_config.delete(:template_paths) ) self.config = merged_config else defaults = DEFAULT_CONFIG.dup self.template_paths = defaults.delete( :template_paths ) self.config = defaults end end |
.load(path, parsestate = nil, opts = {}) ⇒ Object
Read a template object from the specified ‘path`.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/inversion/template.rb', line 165 def self::load( path, parsestate=nil, opts={} ) # Shift the options hash over if there isn't a parse state if parsestate.is_a?( Hash ) opts = parsestate parsestate = nil end tmpl = nil path = Pathname( path ) opts[:template_paths] ||= self.template_paths search_path = opts[:template_paths] + [ Dir.pwd ] self.log.debug "Searching template paths: %p" % [ search_path ] # Unrestricted template location. if path.absolute? tmpl = path # Template files searched under paths specified in 'template_paths', then # the current working directory. First match wins. else tmpl = search_path.collect {|dir| Pathname(dir) + path }.find do |fullpath| fullpath.exist? end raise RuntimeError, "Unable to find template %p within configured paths %p" % [ path.to_s, search_path ] if tmpl.nil? end # We trust files read from disk source = if opts.key?( :encoding ) tmpl.read( encoding: opts[:encoding] ) else tmpl.read end # Load the instance and set the path to the source template = self.new( source, parsestate, opts ) template.source_file = tmpl return template end |
Instance Method Details
#changed? ⇒ Boolean
Returns ‘true` if the template was loaded from a file and the file’s mtime is after the time the template was created.
326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/inversion/template.rb', line 326 def changed? return false unless file = self.source_file now = Time.now if now > ( @last_checked + self.[ :stat_delay ].to_i ) if file.mtime > @last_checked @last_checked = now return true end end return false end |
#initialize_copy(other) ⇒ Object
Copy constructor – make copies of some internal data structures, too.
283 284 285 286 287 |
# File 'lib/inversion/template.rb', line 283 def initialize_copy( other ) @options = deep_copy( other. ) @attributes = deep_copy( other.attributes ) @fragments = deep_copy( other.fragments ) end |
#inspect ⇒ Object
Return a human-readable representation of the template object suitable for debugging.
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/inversion/template.rb', line 369 def inspect nodemap = if $DEBUG ", node_tree: %p" % [ self.node_tree.map(&:as_comment_body) ] else '' end return "#<%s:%08x (loaded from %s) attributes: %p, options: %p%s>" % [ self.class.name, self.object_id / 2, self.source_file ? self.source_file : "memory", self.attributes.keys, self., nodemap ] end |
#reload ⇒ Object
If the template was loaded from a file, reload and reparse it from the same file.
314 315 316 317 318 319 320 321 |
# File 'lib/inversion/template.rb', line 314 def reload file = self.source_file or raise Inversion::Error, "template was not loaded from a file" self.log.debug "Reloading from %s" % [ file ] source = file.read self.parse( source ) end |
#render(parentstate = nil, &block) ⇒ Object Also known as: to_s
Render the template, optionally passing a render state (if, for example, the template is being rendered inside another template).
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/inversion/template.rb', line 342 def render( parentstate=nil, &block ) self.log.info "rendering template %#x" % [ self.object_id * 2 ] opts = self. opts.merge!( parentstate. ) if parentstate self.fragments.clear state = Inversion::RenderState.new( parentstate, self.attributes, opts, &block ) # self.log.debug " rendering node tree: %p" % [ @node_tree ] self.walk_tree {|node| state << node } self.log.info " done rendering template %#x: %0.4fs" % [ self.object_id/2, state.time_elapsed ] if parentstate parentstate.fragments.merge!( state.fragments ) else self.fragments.replace( state.rendered_fragments ) end return state.to_s end |