NAME
Template::Plugin::Heritable - OO dispatching and inheritance for templates
SYNOPSIS
[% USE Heritable %]
[%# searches providers for a "view" template method on
class (which should be a metamodel object, eg
someobj.meta in Perl 6) %]
[% Heritable.include(class, "view", { self = object }) %]
[%# return list of paths it would look %]
[% paths = Heritable.dispatch_paths(class, "view") %]
[%# if you don't have the class of the object handy, then
use 'invoke' instead %]
[% Heritable.invoke(object, "method", { self = object } %]
[%# call the next method in the inheritance tree from
inside a template method %]
[% next_template() %]
DESCRIPTION
Template::Plugin::Heritable
provides support for selecting an appropriate template based on the class of an object. It is also possible to call the next template in the inheritance heirarchy/chain.
This provides a form of inheritance for template display.
The core of this is the template dispatch mechanism, which deals in terms of a suitable metamodel class. The module currently deals in the following metamodels; but no doubt you could fool it with modules which encapsulate other metamodels (such as Perl 5, NEXT, Class::C3, DBIx::Class::Schema, etc) with minimal effort by conforming to one of their APIs.
Eventually, no doubt these will be plugins.
- T2::Class
-
T2 is the Tangram MetaModel that also drives Class::Tangram
- Perl6::MetaModel
-
This module implements the Perl6 MetaModel in terms of Perl 5 objects, and can be found in pugs. Currently TODO as this is a first release, but the intention is that this module will be the second primary supported metamodel.
CONSTRUCTION
Basic use:
[% USE Heritable %]
Specifying all options:
[% USE Heritable({ prefix = "mypath",
suffix = ".tt",
class2path = somefunc,
class_attr2path = somefunc,
schema = myschema
}) %]
Here all dispatch paths returned by Heritable
will be prepended with mypath/
. Also, a custom method is specified to convert from "Foo::Bar
"-style class names to a Template::Provider
path.
There is also a schema
object; this object is responsible for converting objects to classes.
Normally, you wouldn't specify most of this this - and indeed there is the issue there that this configuration information perhaps doesn't belong every place you make a Heritable object dispatch.
For this reason, it is recommended that you have a single template for object dispatching, and to pass through self
appropriately.
[% PROCESS invoke
object = SomeObject
method = "foo"
%]
the invoke template might look like:
[% USE Heritable({ suffix = ".tt"
});
Heritable.include(object.meta, method, { self = object }) -%]
METHODS
.dispatch_paths
.include
.invoke
[% paths = Heritable.dispatch_paths( what, "name" ) %]
[% Heritable.include( what, "name", { ... } ) %]
[% Heritable.invoke( object, "name", { ... } ) %]
.dispatch_paths
returns a list of dispatch paths for what
. what
is a metamodel object (see DESCRIPTION).
.include
calls the first one that actually exists in the available template providers. It throws a (trappable) not found error if it was not found.
.invoke
assumes that the metamodel object is either available as object.meta
or via $schema->class(ref $object)
. Convenient modules to make this Just Work™ with standard Perl 5 and 6 objects/classes are yet to be written, but for T2 this should work fine.
To figure out which template should be called to perform a function, the class names are turned into Template::Provider paths, with the template to call ("view
" in the example in the synopsis) appended to them.
For example, if the "class" object in the synopsis represents the "Foo::Bar" class, which has superclass "Foo", the following locations would be searched for a template (assuming you specified TEMPLATE_EXTENSION = ".tt"
during your Template object construction):
foo/bar/view.tt
foo/view.tt
object/view.tt
It is also possible to dispatch based on attribute or association types, by calling "attribute methods". In this case, the dispatch order also includes templates for the types of the attribute or association.
So, if you were using T2 classes and wrote:
[% Heritable.include(class.attribute("baz"), "show") %]
Then the first of these templates found would be called (assuming baz
is a property of the Foo
class, of type set
):
foo/baz/show.tt
object/baz/show.tt
foo/types/set/show.tt
object/types/set/show.tt
Note that foo/bar/baz/show.tt
was not searched for, even though class
is actually Foo::Bar
. If you wanted to do that, you should use a 'multiple invocant' include
:
[% Heritable.include([class, class.attribute("baz")],
"show", { ... }) %]
or simply
[% Heritable.include([class, "baz"], "show", { ... }) %]
Either of these would then search for:
foo/bar/baz/show.tt
foo/baz/show.tt
object/baz/show.tt
foo/bar/types/set/show.tt
foo/types/set/show.tt
object/types/set/show.tt
In principle, if an attribute's type is itself a type with an inheritance chain, that those extra templates will also be added to the list of checked template locations. This will apply later in later T2 versions and/or the Perl 6 MetaModel, when an actual type heirarchy is in place.
DEFINED VARIABLES
next_template
These methods let you find the next template to display in the inheritance chain.
The next template is [% next_template %]
[% next_template.include({ ... }) %]
Note that if there is no next template you will get a nasty error.
SEE ALSO
AUTHOR
Sam Vilain, <samv@cpan.org>
LICENSE
Copyright (c) 2005, Catalyst IT (NZ) Ltd. This program is free software; you may use it and/or redistribute it under the same terms as Perl itself.
2 POD Errors
The following errors were encountered while parsing the POD:
- Around line 65:
You forgot a '=back' before '=head1'
- Around line 140:
Non-ASCII character seen before =encoding in 'Work™'. Assuming UTF-8