Template::Plugin::Heritable - OO dispatching and inheritance for templates
[% 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() %]
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.
Template::Plugin::Heritable
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 is the Tangram MetaModel that also drives Class::Tangram
Initial support for Class::MOP classes. Note that this is currently only tested with Moose; in particular it assumes Moose-like type constraints. If you want support for plain Class::MOP, please send a test case.
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.
Heritable
mypath/
Foo::Bar
Template::Provider
There is also a schema object; this object is responsible for converting objects to classes. If you are using Class::MOP, you don't need to supply this; the metaclass is found via $object->meta.
schema
Class::MOP
$object->meta
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.
self
[% PROCESS invoke object = SomeObject method = "foo" %]
the invoke template might look like:
[% USE Heritable({ suffix = ".tt" }); Heritable.include(object.meta, method, { self = object }) -%]
[% 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).
.dispatch_paths
what
.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.
.include
.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 6 objects/classes are yet to be written, but for T2 and Class::MOP this should work fine.
.invoke
object.meta
$schema->class(ref $object)
new in 0.03: now supports 5.9.5+ 'mro' - if the symbol &mro::get_linear_isa is defined at runtime (for instance, you used the mro pragma or MRO::Compat for earlier Perl versions), then this will work. However, mro does not cover types of attributes, so only invoke with a method name (no attribute name) is currently supported.
&mro::get_linear_isa
mro
invoke
new in 0.03: also supports DBIx::Class - pass DBIx::Class::ResultSource objects to .include, and DBIx::Class::Row objects to .invoke.
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.
view
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):
TEMPLATE_EXTENSION = ".tt"
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):
baz
Foo
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:
foo/bar/baz/show.tt
class
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
Using Class::MOP, 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.
For instance, if you have two classes A and B, A having an attribute "att" of type "Str", and you write:
[% Heritable.invoke([ my_b, "att"], "show") %]
Then you get this dispatch path:
b/att/show.tt a/att/show.tt moose/object/att/show.tt object/att/show.tt b/types/str/show.tt a/types/str/show.tt moose/object/types/str/show.tt object/types/str/show.tt b/types/value/show.tt a/types/value/show.tt moose/object/types/value/show.tt object/types/value/show.tt b/types/defined/show.tt a/types/defined/show.tt moose/object/types/defined/show.tt object/types/defined/show.tt b/types/item/show.tt a/types/item/show.tt moose/object/types/item/show.tt object/types/item/show.tt
New in Template::Heritable 0.03: for convenience, the standard method for converting classes to "paths" can be customised, for instance if deep directory structures is inconvenient, and you like to be able to combine blocks into a single file, you can use:
[% USE Heritable({ path_delim = "_", use_blocks = 1 }) %]
This would make the first part of the above dispatch list look like this:
b_att_show.tt a_att_show.tt moose_object_att_show.tt object_att_show.tt ...
Not only that, but as use_blocks is set, if by some strange co-incidence the module can find similarly named blocks, it will just call those instead;
use_blocks
[% b_att_show = BLOCK %] Here we show some B attributes. But we don't want to miss out on showing the [% next_template() %] [% END %] [% a_att_show = BLOCK -%] A attributes. [% END %]
With the above block definitions in scope, calling
Would print:
Here we show some B attributes. But we don't want to miss out on showing the A attributes.
And calling:
[% Heritable.invoke([ my_a, "att"], "show") %]
A attributes
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.
T2, Template.
Sam Vilain, <samv@cpan.org>
Copyright (c) 2005, 2006, Catalyst IT (NZ) Ltd. This program is free software; you may use it and/or redistribute it under the same terms as Perl itself.
Add support for Class::MOP, though only Moose classes are currently tested; new test cases welcome.
Moose
2 POD Errors
The following errors were encountered while parsing the POD:
You forgot a '=back' before '=head1'
Non-ASCII character seen before =encoding in 'Work™'. Assuming UTF-8
To install Template::Plugin::Heritable, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Template::Plugin::Heritable
CPAN shell
perl -MCPAN -e shell install Template::Plugin::Heritable
For more information on module installation, please visit the detailed CPAN module installation guide.