[Jifty-commit] r7567 - in Template-Declare/trunk: . lib/Template t
Jifty commits
jifty-commit at lists.jifty.org
Fri Oct 16 16:42:37 EDT 2009
Author: theory
Date: Fri Oct 16 16:42:36 2009
New Revision: 7567
Added:
Template-Declare/trunk/t/composition.t
- copied unchanged from r7566, /Template-Declare/branches/mixmaster/t/composition.t
Template-Declare/trunk/t/deep_mixing.t
- copied unchanged from r7566, /Template-Declare/branches/mixmaster/t/deep_mixing.t
Template-Declare/trunk/t/mixing.t
- copied unchanged from r7566, /Template-Declare/branches/mixmaster/t/mixing.t
Template-Declare/trunk/t/pod-spelling.t
- copied unchanged from r7566, /Template-Declare/branches/mixmaster/t/pod-spelling.t
Template-Declare/trunk/t/relative-mixing.t
- copied unchanged from r7566, /Template-Declare/branches/mixmaster/t/relative-mixing.t
Modified:
Template-Declare/trunk/ (props changed)
Template-Declare/trunk/Changes
Template-Declare/trunk/MANIFEST.SKIP
Template-Declare/trunk/lib/Template/Declare.pm
Template-Declare/trunk/lib/Template/Declare/TagSet.pm
Template-Declare/trunk/lib/Template/Declare/Tags.pm
Template-Declare/trunk/t/aliasing.t
Template-Declare/trunk/t/dispatch_order.t
Template-Declare/trunk/t/importing.t
Template-Declare/trunk/t/smart_tag_wrapper.t
Log:
Merged from mixmaster. That branch is now defunct.
Modified: Template-Declare/trunk/Changes
==============================================================================
--- Template-Declare/trunk/Changes (original)
+++ Template-Declare/trunk/Changes Fri Oct 16 16:42:36 2009
@@ -1,12 +1,20 @@
0.41
-* Documented aliasing and template importing (mixins).
-* Reworked all the documentation, neatening things, fixing bugs in the
- examples, and adding missing docs for various functions and methods.
+* Reworked all the documentation, neatening things, expanding the "USAGE"
+ section, fixing bugs in the examples, and adding missing docs for various
+ functions and methods.
* Added "dispatch_to" to replace "roots", which is now deprecated. Note that
"dispatch_to" resolves to template classes in the opposite order to "roots".
This won't be an issue if you only use a single temlate class.
-* Deprecated "path_for()", since it only works for the last class into which a
- template is imported.
+* Converted the implementation of "alias" to be the same as that used for
+ "import_templates", which is much more efficient.
+* Added the "into" parameter to "alias" and "import_templates".
+* Added the "setting" syntactical sugar keyword for use with "alias".
+* Renamed "import_templates" to "mix". The former is still around, but is
+ deprecated.
+* Added support for package variables with "mix".
+* Deprecated the undocumented "aliases()" and "alias_metadata()" methods, as
+ they are no longer needed. They're now no-ops that issue warnings. To be
+ removed altogether in a future version.
0.40_01 2009-08-12
* Support for inline tagset definitions. Thanks to Olivier 'dolmen' Mengué
Modified: Template-Declare/trunk/MANIFEST.SKIP
==============================================================================
--- Template-Declare/trunk/MANIFEST.SKIP (original)
+++ Template-Declare/trunk/MANIFEST.SKIP Fri Oct 16 16:42:36 2009
@@ -43,3 +43,4 @@
\.svn
\.git
\.t_$
+pod-spelling.t$
Modified: Template-Declare/trunk/lib/Template/Declare.pm
==============================================================================
--- Template-Declare/trunk/lib/Template/Declare.pm (original)
+++ Template-Declare/trunk/lib/Template/Declare.pm Fri Oct 16 16:42:36 2009
@@ -1,7 +1,6 @@
use 5.006;
use warnings;
use strict;
-use Carp;
package Template::Declare;
use Template::Declare::Buffer;
@@ -13,8 +12,6 @@
use base 'Class::Data::Inheritable';
__PACKAGE__->mk_classdata('dispatch_to');
__PACKAGE__->mk_classdata('postprocessor');
-__PACKAGE__->mk_classdata('aliases');
-__PACKAGE__->mk_classdata('alias_metadata');
__PACKAGE__->mk_classdata('templates');
__PACKAGE__->mk_classdata('private_templates');
__PACKAGE__->mk_classdata('buffer');
@@ -23,8 +20,6 @@
__PACKAGE__->dispatch_to( [] );
__PACKAGE__->postprocessor( sub { return wantarray ? @_ : $_[0] } );
-__PACKAGE__->aliases( {} );
-__PACKAGE__->alias_metadata( {} );
__PACKAGE__->templates( {} );
__PACKAGE__->private_templates( {} );
__PACKAGE__->buffer( String::BufferStack->new );
@@ -39,15 +34,7 @@
return $ref->buffer;
};
-use vars qw/$TEMPLATE_VARS/;
-
-# Backwards-compatibility support.
-sub roots {
- # warn "roots() has been deprecated; use dispatch_to() instead\n";
- my $class = shift;
- $class->dispatch_to( [ reverse @{ +shift } ] ) if @_;
- return [ reverse @{ $class->dispatch_to } ];
-}
+our $TEMPLATE_VARS;
=head1 NAME
@@ -110,7 +97,7 @@
=item *
-"Native" XML namespace and declarator support
+"Native" XML namespace and declaration support
=item *
@@ -122,75 +109,119 @@
=item *
+Delegation
+
+=item *
+
Public and private templates
=back
-=head1 USAGE
+=head1 GLOSSARY
-=head2 Basic usage
+=over
-A simple HTML example is in the L<SYNOPSIS/SYNOPSIS>. So let's do XUL!
+=item template class
- package MyApp::Templates;
- use base 'Template::Declare';
- use Template::Declare::Tags 'XUL';
+A subclass of Template::Declare in which one or more templates are defined
+using the C<template> keyword, or that inherits templates from a super class.
- template main => sub {
- xml_decl { 'xml', version => '1.0' };
- xml_decl { 'xml-stylesheet', href => "chrome://global/skin/", type => "text/css" };
- groupbox {
- caption { attr { label => 'Colors' } }
- radiogroup {
- for my $id ( qw< orange violet yellow > ) {
- radio {
- attr {
- id => $id,
- label => ucfirst($id),
- $id eq 'violet' ? (selected => 'true') : ()
- }
- }
- } # for
- }
- }
- };
+=item template
- package main;
- Template::Declare->init( dispatch_to => ['MyApp::Templates'] );
- print Template::Declare->show( 'main' );
+Created with the C<template> keyword, a template is a subroutine that uses
+C<tags> to generate output.
-The output:
+=item attribute
- <?xml version="1.0"?>
- <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+An XML element attribute. For example, in C<< <img src="foo.png" /> >>, C<src>
+is an attribute of the C<img> element.
- <groupbox>
- <caption label="Colors" />
- <radiogroup>
- <radio id="orange" label="Orange" />
- <radio id="violet" label="Violet" selected="true" />
- <radio id="yellow" label="Yellow" />
- </radiogroup>
- </groupbox>
+=item tag
+
+A subroutine that generates XML element-style output. Tag subroutines execute
+blocks that generate the output, and can call other tags to generate a
+properly hierarchical structure.
+
+=item tag set
+
+A collection of related tags defined in a subclass of
+L<Template::Declare::TagSet> for a particular purpose, and which can be
+imported into a template class. For example,
+L<Template::Declare::TagSet::HTML> defines tags for emitting HTML elements.
+
+=item wrapper
+
+A subroutine that wraps the output from a template. Useful for wrapping
+template output in common headers and footers, for example.
+
+=item dispatch class
+
+A template class that has been passed to L<C<init()>|/init> via the
+C<dispatch_to> parameter. When L<show|/"show TEMPLATE"> is called, only
+templates defined in or mixed into the dispatch classes will be executed.
+
+=item path
+
+The name specified for a template when it is created by the C<template>
+keyword, or when a template is mixed into a template class.
+
+=item mixin
+
+A template mixed into a template class via L</mix>. Mixed-in templates may be
+mixed in under prefix paths to distinguish them from the templates defined in
+the dispatch classes.
+
+=item alias
+
+A template aliased into a template class via L</alias>. Aliased templates may
+be added under prefix paths to distinguish them from the templates defined in
+the dispatch classes.
+
+=item package variable
+
+Variables defined when mixing templates into a template class. These variables
+are available only to the mixed-in templates; they are not even accessible
+from the template class in which the templates were defined.
+
+=item helper
+
+A subroutine used in templates to assist in the generation of output, or in
+template classes to assist in the mixing-in of templates. Output helpers
+include C<outs()> for rending text output and C<xml_decl()> for rendering XML
+declarations. Mixin helpers include C<into> for specifying a template class to
+mix into, and C<under> for specifying a path prefix under which to mix
+templates.
+
+=back
+
+=head1 Basics
+
+Like other Perl templating systems, there are two parts to Template::Declare:
+the templates and the code that loads and executes the templates. Unlike other
+template systems, the templates are written in Perl classes. A simple HTML
+example is in the L</SYNOPSIS>.
=head2 A slightly more advanced example
In this example, we'll show off how to set attributes on HTML tags, how to
-call other templates and how to declare a I<private> template that can't be
-called directly. We'll also show passing arguments to templates.
+call other templates, and how to declare a I<private> template that can't be
+called directly. We'll also show passing arguments to templates. First, the
+template class:
package MyApp::Templates;
- use Template::Declare::Tags;
use base 'Template::Declare';
+ use Template::Declare::Tags;
- private template 'header' => sub {
+ private template 'util/header' => sub {
head {
title { 'This is a webpage' };
- meta { attr { generator => "This is not your father's frontpage" } }
+ meta {
+ attr { generator => "This is not your father's frontpage" }
+ }
}
};
- private template 'footer' => sub {
+ private template 'util/footer' => sub {
my $self = shift;
my $time = shift || gmtime;
@@ -205,7 +236,7 @@
my $user = shift || 'world wide web';
html {
- show('header');
+ show('util/header');
body {
img { src is 'hello.jpg' }
p {
@@ -213,16 +244,79 @@
"Hello, $user!"
};
};
- show('footer');
+ show('util/footer', 'noon');
}
};
+A few notes on this example:
+
+=over
+
+=item *
+
+Since no parameter was passed to C<use Template::Declare::Tags>, the HTML tags
+are imported by default.
+
+=item *
+
+The C<private> keyword indicates that a template is private. That means that
+it can only be executed by other templates within the template class in which
+it's declared. By default, C<< Template::Declare->show >> will not dispatch to
+it.
+
+=item *
+
+The two private templates have longer paths than we've seen before:
+C<util/header> and C<util/footer>. They must of course be called by their full
+path names. You can put any characters you like into template names, but the
+use of Unix filesystem-style paths is the most common (following on the
+example of L<HTML::Mason|HTML::Mason>).
+
+=item *
+
+The first argument to a template is a class name. This can be useful for
+calling methods defined in the class.
+
+=item *
+
+The C<show> sub executes another template. In this example, the C<simple>
+template calls C<show('util/header')> and C<show('util/footer')> in order to
+execute those private templates in the appropriate places.
+
+=item *
+
+Additional arguments to C<show> are passed on to the template being executed.
+here, C<show('util/footer', 'noon')> is passing "noon" to the C<util/footer>
+template, with the result that the "last generated at" string will display
+"noon" instead of the default C<gmtime>.
+
+=item *
+
+In the same way, note that the C<simple> template expects an additional
+argument, a user name.
+
+=item *
+
+In addition to using C<attr> to declare attributes for an element, you can
+use C<is>, as in
+
+ img { src is 'hello.jpg' }
+
+=back
+
+Now for executing the template:
+
package main;
use Template::Declare;
Template::Declare->init( dispatch_to => ['MyApp::Templates'] );
- print Template::Declare->show( 'simple', 'TD user');
+ print Template::Declare->show( '/simple', 'TD user');
+
+We've told Template::Declare to dispatch to templates defined in our template
+class. And note how an additional argument is passed to C<show()>; that
+argument, "TD user", will be passed to the C<simple> template, where it will
+be used in the C<$user> variable.
-And the output:
+The output looks like this:
<html>
<head>
@@ -236,8 +330,108 @@
<div id="footer">Page last generated at Thu Sep 3 20:56:14 2009.</div>
</html>
-For more options, especially the "native" XML namespace support, C<is> syntax
-for attributes, and more samples, see L<Template::Declare::Tags>.
+Note that the single quote in C<father's> was quoted for you. We sanitize
+your output for you to help prevent cross-site scripting attacks.
+
+=head2 XUL
+
+Template::Declare isn't limited to just HTML. Let's do XUL!
+
+ package MyApp::Templates;
+ use base 'Template::Declare';
+ use Template::Declare::Tags 'XUL';
+
+ template main => sub {
+ xml_decl { 'xml', version => '1.0' };
+ xml_decl {
+ 'xml-stylesheet',
+ href => "chrome://global/skin/",
+ type => "text/css"
+ };
+ groupbox {
+ caption { attr { label => 'Colors' } }
+ radiogroup {
+ for my $id ( qw< orange violet yellow > ) {
+ radio {
+ attr {
+ id => $id,
+ label => ucfirst($id),
+ $id eq 'violet' ? (selected => 'true') : ()
+ }
+ }
+ } # for
+ }
+ }
+ };
+
+The first thing to do in a template class is to subclass Template::Declare
+itself. This is required so that Template::Declare always knows that it's
+dealing with templates. The second thing is to C<use Template::Declare::Tags>
+to import the set of tag subroutines you need to generate the output you want.
+In this case, we've imported tags to support the creation of XUL. Other tag
+sets include HTML (the default), and RDF.
+
+Templates are created using the C<template> keyword:
+
+ template main => sub { ... };
+
+The first argument is the name of the template, also known as its I<path>. In
+this case, the template's path is C<main> (or C</main>, both are allowed (to
+keep both PHP and L<HTML::Mason> fans happy). The second argument is an
+anonymous subroutine that uses the tag subs (and any other necessary code) to
+generate the output for the template.
+
+The tag subs imported into your class take blocks as arguments, while a
+number of helper subs take other arguments. For example, the C<xml_decl>
+helper takes as its first argument the name of the XML declaration to be
+output, and then a hash of the attributes of that declaration:
+
+ xml_decl { 'xml', version => '1.0' };
+
+Tag subs are used by simply passing a block to them that generates the output.
+Said block may of course execute other tag subs in order to represent the
+hierarchy required in your output. Here, the C<radiogroup> tag calls the
+C<radio> tag for each of three different colors:
+
+ radiogroup {
+ for my $id ( qw< orange violet yellow > ) {
+ radio {
+ attr {
+ id => $id,
+ label => ucfirst($id),
+ $id eq 'violet' ? (selected => 'true') : ()
+ }
+ }
+ } # for
+ }
+
+Note the C<attr> sub. This helper function is used to add attributes to the
+element created by the tag in which they appear. In the previous example, the
+the C<id>, C<label>, and C<selected> attributes are added to each C<radio>
+output.
+
+Once you've written your templates, you'll want to execute them. You do so by
+telling Template::Declare what template classes to dispatch to and then asking
+it to show you the output from a template:
+
+ package main;
+ Template::Declare->init( dispatch_to => ['MyApp::Templates'] );
+ print Template::Declare->show( 'main' );
+
+The path passed to C<show> can be either C<main> or </main>, as you prefer. In
+either event, the output would look like this:
+
+ <?xml version="1.0"?>
+ <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+ <groupbox>
+ <caption label="Colors" />
+ <radiogroup>
+ <radio id="orange" label="Orange" />
+ <radio id="violet" label="Violet" selected="true" />
+ <radio id="yellow" label="Yellow" />
+ </radiogroup>
+ </groupbox>
=head2 Postprocessing
@@ -259,10 +453,21 @@
};
template after => sub {
- h1 { "Welcome to _my_ site. It's _great_!" };
- h2 { outs_raw "This is _not_ emphasized." };
+ h1 { "Welcome to _my_ site. It's _great_!" };
+ h2 { outs_raw "This is _not_ emphasized." };
+ img { src is '/foo/_bar_baz.png' };
};
+Here we've defined two templates in our template class, with the paths
+C<before> and C<after>. The one new thing to note is the use of the C<outs>
+and C<outs_raw> subs. C<outs> XML-encodes its argument and outputs it. You can
+also just specify a string to be output within a tag call, but if you need to
+mix tags and plain text within a tag call, as in the C<before> template here,
+you'll need to use C<outs> to get things to output as you would expect.
+C<outs_raw> is the same, except that it does no XML encoding.
+
+Now let's have a look at how we use these templates with a post-processor:
+
package main;
use Template::Declare;
Template::Declare->init(
@@ -270,8 +475,8 @@
postprocessor => \&emphasize,
);
- print Template::Declare->show( 'before');
- print Template::Declare->show( 'after');
+ print Template::Declare->show( 'before' );
+ print Template::Declare->show( 'after' );
sub emphasize {
my $text = shift;
@@ -279,18 +484,31 @@
return $text;
}
-And the output:
+As usual, we've told Template::Declare to dispatch to our template class. A
+new parameter to C<init()> is C<postprocessor>, which is a code reference that
+should expect the template output as an argument. It can then transform that
+text however it sees fit before returning it for final output. In this
+example, the C<emphasize> subroutine looks for text that's emphasized using
+_underscores_ and turns them into C<< <em>emphasis</em> >> HTML elements.
+
+We then execute both the C<before> and the C<after> templates with the output
+ending up as:
<h1>Welcome to
<em>my</em> site. It's
<em>great</em>!</h1>
<h1>Welcome to <em>my</em> site. It's <em>great</em>!</h1>
<h2>This is _not_ emphasized.</h2>
+ <img src="/foo/_bar_baz.png" />
+
+The thing to note here is that text passed to C<outs_raw> is not passed
+through the postprocessor, and neither are attribute values (like the C<img>'s
+C<src>).
=head2 Inheritance
Templates are really just methods. You can subclass your template packages to
-override some of those methods. See also L<Jifty::View::Declare::CRUD>.
+override some of those methods:
package MyApp::Templates::GenericItem;
use Template::Declare::Tags;
@@ -311,38 +529,493 @@
use Template::Declare::Tags;
use base 'MyApp::Templates::GenericItem';
- template 'item' => sub {
- my ($self, $post) = @_;
- h1 { $post->title }
- div { $post->body }
+ template 'item' => sub {
+ my ($self, $post) = @_;
+ h1 { $post->title }
+ div { $post->body }
+ };
+
+Here we have two template classes; the second, C<MyApp::Templates::BlogPost>,
+inherits from the first, C<MyApp::Templates::GeniricItem>. Note also that
+C<MyApp::Templates::BlogPost> overrides the C<item> template. So execute these
+templates:
+
+ package main;
+ use Template::Declare;
+
+ Template::Declare->init( dispatch_to => ['MyApp::Templates::GenericItem'] );
+ print Template::Declare->show( 'list', 'foo', 'bar', 'baz' );
+
+ Template::Declare->init( dispatch_to => ['MyApp::Templates::BlogPost'] );
+ my $post = My::Post->new(title => 'Hello', body => 'first post');
+ print Template::Declare->show( 'item', $post );
+
+First we execute the C<list> template in the base class, passing in some
+items, and then we re-C<init()> Template::Declare and execute I<its> C<list>
+template with an appropriate argument. Here's the output:
+
+ <div>
+ <span>foo</span>
+ <span>bar</span>
+ <span>baz</span>
+ </div>
+
+ <h1>Hello</h1>
+ <div>first post</div>
+
+So the override of the C<list> template in the subclass works as expected. For
+another example, see L<Jifty::View::Declare::CRUD>.
+
+=head2 Wrappers
+
+There are two levels of wrappers in Template::Declare: template wrappers and
+smart tag wrappers.
+
+=head3 Template Wrappers
+
+C<create_wrapper> declares a wrapper subroutine that can be called like a tag
+sub, but can optionally take arguments to be passed to the wrapper sub. For
+example, if you wanted to wrap all of the output of a template in the usual
+HTML headers and footers, you can do something like this:
+
+ package MyApp::Templates;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ BEGIN {
+ create_wrapper wrap => sub {
+ my $code = shift;
+ my %params = @_;
+ html {
+ head { title { outs "Hello, $params{user}!"} };
+ body {
+ $code->();
+ div { outs 'This is the end, my friend' };
+ };
+ }
+ };
+ }
+
+ template inner => sub {
+ wrap {
+ h1 { outs "Hello, Jesse, s'up?" };
+ } user => 'Jesse';
+ };
+
+Note how the C<wrap> wrapper function is available for calling after it has
+been declared in a C<BEGIN> block. Also note how you can pass arguments to the
+function after the closing brace (you don't need a comma there!).
+
+The output from the "inner" template will look something like this:
+
+ <html>
+ <head>
+ <title>Hello, Jesse!</title>
+ </head>
+ <body>
+ <h1>Hello, Jesse, s'up?</h1>
+ <div>This is the end, my friend</div>
+ </body>
+ </html>
+
+=head3 Tag Wrappers
+
+Tag wrappers are similar to template wrappers, but mainly function as syntax
+sugar for creating subroutines that behave just like tags but are allowed to
+contain arbitrary Perl code and to dispatch to other tag. To create one,
+simply create a named subroutine with the prototype C<(&)> so that its
+interface is the same as tags. Within it, use
+L<C<smart_tag_wrapper>|Template::Declare::Tags/"smart_tag_wrapper"> to do the
+actual execution, like so:
+
+ package My::Template;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ sub myform (&) {
+ my $code = shift;
+
+ smart_tag_wrapper {
+ my %params = @_; # set using 'with'
+ form {
+ attr { %{ $params{attr} } };
+ $code->();
+ input { attr { type => 'submit', value => $params{value} } };
+ };
+ };
+ }
+
+ template edit_prefs => sub {
+ with(
+ attr => { id => 'edit_prefs', action => 'edit.html' },
+ value => 'Save'
+ ), myform {
+ label { 'Time Zone' };
+ input { type is 'text'; name is 'tz' };
+ };
+ };
+
+Note in the C<edit_prefs> template that we've used
+L<C<with>|Template::Declare::Tags/"with"> to set up parameters to be passed to
+the smart wrapper. C<smart_tag_wrapper()> is the device that allows you to
+receive those parameters, and also handles the magic of making sure that the
+tags you execute within it are properly output. Here we've used C<myform>
+similarly to C<form>, only C<myform> does something different with the
+C<with()> arguments and outputs a submit element.
+
+Executing this template:
+
+ Template::Declare->init( dispatch_to => ['My::Template'] );
+ print Template::Declare->show('edit_prefs');
+
+Yields this output:
+
+ <form action="edit.html" id="edit_prefs">
+ <label>Time Zone</label>
+ <input type="text" name="tz" />
+ <input type="submit" value="Save" />
+ </form>
+
+=head2 Class Search Dispatching
+
+The classes passed via the C<dispatch_to> parameter to C<init()> specify all
+of the templates that can be executed by subsequent calls to C<show()>.
+Template searches through these classes in order to find those templates. Thus
+it can be useful, when you're creating your template classes and determining
+which to use for particular class to C<show()>, to have templates that
+override other templates. This is similar to how an operating system will
+search all the paths in the C<$PATH> environment variable for a program to
+run, and to L<HTML::Mason> component roots or L<Template::Toolkit>'s
+C<INCLUDE_PATH> parameter.
+
+For example, say you have this template class that defines a template that
+you'll use for displaying images on your Web site.
+
+ package MyApp::UI::Standard;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ template image => sub {
+ my ($self, $src, $title) = @_;
+ img {
+ src is $src;
+ title is $title;
+ };
+ };
+
+As usual, you can use it like so:
+
+ my @template_classes = 'MyApp::UI::Standard';
+ Template::Declare->init( dispatch_to => \@template_classes );
+ print Template::Declare->show('image', 'foo.png', 'Foo');
+
+We're explicitly using a reference to C<@template_classes> so that we can
+manage this list ourselves.
+
+The output of this will be:
+
+ <div class="std">
+ <img src="foo.png" title="Foo" />
+ <p class="caption"></p>
+ </div>
+
+But say that in some sections of your site you need to have a more formal
+treatment of your photos. Maybe you publish photos from a wire service and
+need to provide an appropriate credit. You might write the template class like
+so:
+
+ package MyApp::UI::Formal;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ template image => sub {
+ my ($self, $src, $title, $credit, $caption) = @_;
+ div {
+ class is 'formal';
+ img {
+ src is $src;
+ title is $title;
+ };
+ p {
+ class is 'credit';
+ outs "Photo by $credit";
+ };
+ p {
+ class is 'caption';
+ outs $caption;
+ };
+ };
+ };
+
+
+This, too, will work as expected, but the useful bit that comes in when you're
+mixing and matching template classes to pass to C<dispatch_to> before
+rendering a page. Maybe you always pass have MyApp::UI::Standard to
+C<dispatch_to> because it has all of your standard formatting templates.
+But when the code realizes that a particular page needs the more formal
+treatment, you can prepend the formal class to the list:
+
+ unshift @template_classes, 'MyApp::UI::Formal';
+ print Template::Declare->show(
+ 'image',
+ 'ap.png',
+ 'AP Photo',
+ 'Clark Kent',
+ 'Big news'
+ );
+ shift @template_classes;
+
+In this way, made the formal C<image> template will be found first, yielding
+this output:
+
+ <div class="formal">
+ <img src="ap.png" title="AP Photo" />
+ <p class="credit">Photo by Clark Kent</p>
+ <p class="caption">Big news</p>
+ </div>
+
+At the end, we've shifted the formal template class off the C<dispatch_to>
+list in order to restore the template classes the default configuration, ready
+for the next request.
+
+=head2 Template Composition
+
+There are two methods of template composition: mixins and delegation. Their
+interfaces are very similar, the only difference being the template invocant.
+
+=head2 Mixins
+
+Let's start with a mixin.
+
+ package MyApp::UtilTemplates;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ template content => sub {
+ my $self = shift;
+ my @paras = @_;
+ h1 { $self->get_title };
+ div {
+ id is 'content';
+ p { $_ } for @paras;
+ };
+ };
+
+ package MyApp::Templates;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+ mix MyApp::UtilTemplates under '/util';
+
+ sub get_title { 'Kashmir' }
+
+ template story => sub {
+ my $self = shift;
+ html {
+ head {
+ title { "My Site: " . $self->get_title };
+ };
+ body {
+ show( 'util/content' => 'first paragraph', 'second paragraph' );
+ };
+ };
+ };
+
+The first template class, C<MyApp::UtilTemplates>, defines a utility template,
+called C<content>, for outputting the contents of page. Note its call to
+C<< $self->get_title >> even though it doesn't have a C<get_title> method. This
+is part of the mixin's "contract": it requires that the class it's mixed into
+have a C<get_title()> method.
+
+The second template class, C<MyApp::Templates>, mixes C<MyApp::UtilTemplates>
+into itself under the path C</util> and defines a C<get_title()> method as
+required by the mixin. Then, its C<story> template calls the mixed-in template
+as C<util/content>, because the C<content> template was mixed into the current
+template under C</util>. Get it?
+
+Now we can use the usual template invocation:
+
+ package main;
+ Template::Declare->init( dispatch_to => ['MyApp::Templates'] );
+ print Template::Declare->show('story');
+
+To appreciate our output:
+
+ <html>
+ <head>
+ <title>My Site: Kashmir</title>
+ </head>
+ <body>
+ <h1>Kashmir</h1>
+ <div id="content">
+ <p>fist paragraph</p>
+ <p>second paragraph</p>
+ </div>
+ </body>
+ </html>
+
+Mixins are a very useful tool for template authors to add reusable
+functionality to their template classes. But it's important to pay attention to
+the mixin contracts so that you're sure to implement the required API in your
+template class (here, the C<get_title()> method).
+
+=head3 Aliases
+
+Aliases are very similar to mixins, but implement delegation as a composition
+pattern, rather than mixins. The upshot is that there is no contract provided
+by an aliased class: it just works. This is because the invocant is the class
+from which the aliases are imported, and therefore it will dispatch to methods
+defined in the aliased class.
+
+For example, say that you wanted to output a sidebar on pages that need one
+(perhaps your CMS has sidebar things). We can define a template class that
+has a template for that:
+
+ package MyApp::UI::Stuff;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ sub img_path { '/ui/css' }
+
+ template sidebar => sub {
+ my ($self, $thing) = @_;
+ div {
+ class is 'sidebar';
+ img { src is $self->img_path . '/sidebar.png' };
+ p { $_->content } for $thing->get_things;
+ };
+ };
+
+Note the use of the C<img_path()> method defined in the template class and
+used by the C<sidebar> template. Now let's use it:
+
+ package MyApp::Render;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+ alias MyApp::UI::Stuff under '/stuff';
+
+ template page => sub {
+ my ($self, $page) = @_;
+ h1 { $page->title };
+ for my $thing ($page->get_things) {
+ if ($thing->is('paragraph')) {
+ p { $thing->content };
+ } elsif ($thing->is('sidebar')) {
+ show( '/stuff/sidebar' => $thing );
+ }
+ }
+ };
+
+Here our rendering template class has aliased C<MyApp::UI::Stuff> under
+C</stuff>. So the C<page> template calls C<show('/stuff/sidebar')> to invoke
+the sidebar template. If we run this:
+
+ Template::Declare->init( dispatch_to => ['MyApp::Render'] );
+ print Template::Declare->show( page => $page );
+
+We get output as you might expect:
+
+ <h1>My page title</h1>
+ <p>Page paragraph</p>
+ <div class="sidebar">
+ <img src="/ui/css/sidebar.png" />
+ <p>Sidebar paragraph</p>
+ <p>Another paragraph</p>
+ </div>
+
+Now, let's say that you have political stuff that you want to use a different
+image for in the sidebar. If that's the only difference, we can subclass
+C<MyApp::UI::Stuff> and just override the C<img_path()> method:
+
+ package MyApp::UI::Stuff::Politics;
+ use Template::Declare::Tags;
+ use base 'MyApp::UI::Stuff';
+
+ sub img_path { '/politics/ui/css' }
+
+Now let's mix that into a politics template class:
+
+ package MyApp::Render::Politics;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+ alias MyApp::UI::Stuff::Politics under '/politics';
+
+ template page => sub {
+ my ($self, $page) = @_;
+ h1 { $page->title };
+ for my $thing ($page->get_things) {
+ if ($thing->is('paragraph')) {
+ p { $thing->content };
+ } elsif ($thing->is('sidebar')) {
+ show( '/politics/sidebar' => $thing );
+ }
+ }
};
- package main;
- use Template::Declare;
+The only difference between this template class and C<MyApp::Render> is that
+it aliases C<MyApp::UI::Stuff::Politics> under C</politics>, and then calls
+C<show('/politics/sidebar')> in the C<page> template. Running this template:
+
+ Template::Declare->init( dispatch_to => ['MyApp::Render::Politics'] );
+ print Template::Declare->show( page => $page );
+
+Yields output using the value of the subclass's C<img_path()> method -- that
+is, the sidebar image is now F</politics/ui/css/sidebar.png> instead of
+F</ui/css/sidebar.png>:
+
+ <h1>My page title</h1>
+ <p>Page paragraph</p>
+ <div class="sidebar">
+ <img src="/politics/ui/css/sidebar.png" />
+ <p>Sidebar paragraph</p>
+ <p>Another paragraph</p>
+ </div>
- Template::Declare->init(
- dispatch_to => ['MyApp::Templates::GenericItem']
- );
- print Template::Declare->show( 'list', 'foo', 'bar', 'baz' );
+=head3 Other Tricks
- Template::Declare->init( dispatch_to => ['MyApp::Templates::BlogPost'] );
- my $post = My::Post->new(title => 'Hello', body => 'first post');
- print Template::Declare->show( 'item', $post );
+The delegation behavior of C<alias> actually makes it a decent choice for
+template authors to mix and match libraries of template classes as
+appropriate, without worrying about side effects. You can even alias templates
+in one template class into another template class if you're not the author of
+that class by using the C<into> keyword:
+
+ alias My::UI::Widgets into Your::UI::View under '/widgets';
+
+Now the templates defined in C<Your::UI::View> are available in
+C<My::UI::Widgets> under C</widgets>. The C<mix> method supports this syntax
+as well, though it's not necessarily recommended, given that you would not be
+able to fulfill any contracts unless you re-opened the class into which you
+mixed the templates. But in any case, authors of framework view classes might
+find this functionality useful for automatically aliasing template classes
+into a single dispatch template class.
+
+Another trick is to alias or mix your templates with package variables
+specific to the composition. Do so via the C<setting> keyword:
+
+ package My::Templates;
+ mix Some::Mixin under '/mymix', setting { name => 'Larry' };
+
+The templates mixed from C<Some::Mixin> into C<My::Templates> have package
+variables set for them that are accessible I<only> from their mixed-in paths.
+For example, if this template was defined in C<Some::Mixin>:
-And the output:
+ template howdy => sub {
+ my $self = shift;
+ outs "Howdy, " . $self->package_variable('name') || 'Jesse';
+ };
- <div>
- <span>foo</span>
- <span>bar</span>
- <span>baz</span>
- </div>
+Then C<show('mymix/howdy')> called on C<My::Templates> will output "Howdy,
+Larry", while the output from C<show('howdy')> will output "Howdy, Jesse". In
+other words, package variables defined for the mixed-in templates are
+available only to the mixins and not to the original. The same functionality
+exists for C<alias> as well.
- <h1>Hello</h1>
- <div>first post</div>
+=begin comment
-=head2 Aliasing and Mixins
+=head2 Tag Sets
-=head2 Class Search Dispatching
+Wherein we will eventually provide a brief tutorial on creating custom tag sets.
+
+=end comment
=head1 METHODS
@@ -407,71 +1080,6 @@
}
-=head2 buffer
-
-Gets or sets the L<String::BufferStack> object; this is a class method. You
-can use it to manipulate the output from tags as they are output. It's used
-internally to make the tags nest correctly, and be output to the right place.
-We're not sure if there's ever a need for you to frob it by hand, but it does
-enable things like the following:
-
- template simple => sub {
- html {
- head {}
- body {
- Template::Declare->buffer->set_filter( sub {uc shift} );
- p { 'Whee!' }
- p { 'Hello, world wide web!' }
- Template::Declare->buffer->clear_top if rand() < 0.5;
- }
- }
- };
-
-...which outputs, with equal regularity, either:
-
- <html>
- <head></head>
- <body>
- <P>WHEE!</P>
- <P>HELLO, WORLD WIDE WEB!</P>
- </body>
- </html>
-
-...or:
-
- <html>
- <head></head>
- <body></body>
- </html>
-
-We'll leave it to you to judge whether or not that's actually useful.
-
-=head2 new_buffer_frame
-
- $td->new_buffer_frame();
-
-Creates a new buffer frame, using L<String::BufferStack/push> with C<private>.
-This use is deprecated in favor of dealing with L</buffer> directly.
-
-=cut
-
-sub new_buffer_frame {
- __PACKAGE__->buffer->push( private => 1 );
-}
-
-=head2 end_buffer_frame
-
- my $buf = $td->end_buffer_frame();
-
-Deletes and returns the topmost buffer, using L<String::BufferStack/pop>. This
-use is deprecated in favor of dealing with L</buffer> directly..
-
-=cut
-
-sub end_buffer_frame {
- __PACKAGE__->buffer->pop;
-}
-
=head2 show TEMPLATE_NAME
Template::Declare->show( 'howdy', name => 'Larry' );
@@ -494,24 +1102,130 @@
return Template::Declare::Tags::show_page($template => @_);
}
-=head2 path_for $template
+=head2 Template Composition
- my $path = Template::Declare->path_for('index');
+Sometimes you want to mix templates from one class into another class, or
+delegate template execution to a class of templates. C<alias()> and C<mix()>
+are your keys to doing so.
+
+=head3 mix
+
+ mix Some::Clever::Mixin under '/mixin';
+ mix Some::Other::Mixin under '/otmix', setting { name => 'Larry' };
+ mix My::Mixin into My::View, under '/mymix';
+
+Mixes templates from one template class into another class. When the mixed-in
+template is called, its invocant will be the class into which it was mixed.
+This type of composition is known as a "mixin" in object-oriented parlance.
+See L<Template Composition|/"Template Composition"> for extended examples and
+a comparision to C<alias>.
+
+The first parameter is the name of the template class to be mixed in. The
+C<under> keyword tells C<mix> where to put the templates. For example,
+a C<foo> template in C<Some::Clever::Mixin> will be mixed in as C<mymixin/foo>.
+
+The C<setting> keyword specifies package variables available only to the
+mixed-in copies of templates. These are available to the templates as
+C<< $self->package_variable($varname) >>.
+
+The C<into> keyword tells C<mix> into what class to mix the templates. Without
+theis keyword, C<mix> will mix them into the calling class.
+
+For those who prefer a direct OO syntax for mixins, just call C<mix()> as a
+method on the class to be mixed in. To replicate the above three examples
+without the use of the sugar:
+
+ Some::Clever::Mixin->mix( '/mixin' );
+ Some::Other::Mixin->mix( '/otmix', { name => 'Larry' } );
+ My::Mixin->mix( 'My::View', '/mymix' );
-Returns the path for the template name to be used for show, adjusted with
-paths used in C<import_templates>. Note that this will only work for the last
-class into which you imported the template. This method is, therefore,
-deprecated.
+=cut
+
+sub mix {
+ my $mixin = shift;
+ my ($into, @args) = _into(@_);
+ $mixin->_import($into, $into, @args);
+}
+
+=head3 alias
+
+ alias Some::Clever:Templates under '/delegate';
+ alias Some::Other::Templates under '/send_to', { name => 'Larry' };
+ alias UI::Stuff into My::View, under '/mystuff';
+
+Aliases templates from one template class into another class. When an alias
+called, its invocant will be the class from which it was aliased. This type of
+composition is known as "delegation" in object-oriented parlance. See
+L<Template Composition|/"Template Composition"> for extended examples and a
+comparison to C<mix>.
+
+The first parameter is the name of the template class to alias. The C<under>
+keyword tells C<alias> where to put the templates. For example, a C<foo>
+template in C<Some::Clever::Templates> will be aliased as C<delegate/foo>.
+
+The C<setting> keyword specifies package variables available only to the
+aliases. These are available to the templates as
+C<< $self->package_variable($varname) >>.
+
+The C<into> keyword tells C<alias> into what class to aliase the templates.
+Without this keyword, C<alias> will alias them into the calling class.
+
+For those who prefer a direct OO syntax for mixins, just call C<alias()> as a
+method on the class to be mixed in. To replicate the above three examples
+without the use of the sugar:
+
+ Some::Clever:Templates->alias( '/delegate' );
+ Some::Other::Templates->alias( '/send_to', { name => 'Larry' } );
+ UI::Stuff->alias( 'My::View', '/mystuff' );
=cut
-sub path_for {
- my $class = shift;
- my $template = shift;
- return ($class->imported_into ||'') . '/' . $template;
+sub alias {
+ my $mixin = shift;
+ my ($into, @args) = _into(@_);
+ $mixin->_import($into, undef, @args);
+}
+
+=head3 package_variable( VARIABLE )
+
+ $td->package_variable( $varname => $value );
+ $value = $td->package_variable( $varname );
+
+Returns a value set for a mixed-in template's variable, if any were specified
+when the template was mixed-in. See L</mix> for details.
+
+=cut
+
+sub package_variable {
+ my $self = shift;
+ my $var = shift;
+ if (@_) {
+ $TEMPLATE_VARS->{$self}->{$var} = shift;
+ }
+ return $TEMPLATE_VARS->{$self}->{$var};
+}
+
+=head3 package_variables( VARIABLE )
+
+ $td->package_variables( $variables );
+ $variables = $td->package_variables;
+
+Get or set a hash reference of variables for a mixed-in template. See
+L</mix> for details.
+
+=cut
+
+sub package_variables {
+ my $self = shift;
+ if (@_) {
+ %{ $TEMPLATE_VARS->{$self} } = shift;
+ }
+ return $TEMPLATE_VARS->{$self};
}
-=head2 resolve_template TEMPLATE_PATH INCLUDE_PRIVATE_TEMPLATES
+=head2 Templates registration and lookup
+
+=head3 resolve_template TEMPLATE_PATH INCLUDE_PRIVATE_TEMPLATES
my $code = Template::Declare->resolve_template($template);
my $code = Template::Declare->has_template($template, 1);
@@ -522,13 +1236,7 @@
First it looks through all the valid Template::Declare classes defined via
C<dispatch_to>. For each class, it looks to see if it has a template called
-$template_name directly (or via an C<import_templates>. Then it looks to see
-if there are any L<C<alias>/"alias"> paths for the class with prefixes that
-match the template we're looking for.
-
-=head2 has_template TEMPLATE_PATH INCLUDE_PRIVATE_TEMPLATES
-
-An alias for C<resolve_template>.
+$template_name directly (or via a mixin).
=cut
@@ -551,15 +1259,19 @@
next unless ( $package and $package->isa(__PACKAGE__) );
if ( my $coderef = $package->_has_template( $template_name, $show_private ) ) {
return $coderef;
- } elsif ( $coderef = $package->_has_aliased_template($template_name, $show_private) ) {
- return $coderef;
}
}
}
+=head3 has_template TEMPLATE_PATH INCLUDE_PRIVATE_TEMPLATES
+
+An alias for C<resolve_template>.
+
+=cut
+
sub has_template { resolve_template(@_) }
-=head2 register_template( TEMPLATE_NAME, CODEREF )
+=head3 register_template( TEMPLATE_NAME, CODEREF )
MyApp::Templates->register_template( howdy => sub { ... } );
@@ -578,7 +1290,7 @@
_register_template( $class, _template_name_to_sub($template_name), $code )
}
-=head2 register_private_template( TEMPLATE_NAME, CODEREF )
+=head3 register_private_template( TEMPLATE_NAME, CODEREF )
MyApp::Templates->register_private_template( howdy => sub { ... } );
@@ -602,130 +1314,158 @@
}
-=head2 alias TEMPLATE_CLASS under PATH
-
- alias Some::Clever::Mixin under '/mixin';
- alias Some::Other::Mixin under '/mymix', { name => 'Larry' };
+=head3 buffer
-Sometimes you want to alias templates to a subpath, or mix them into an
-existing template path. Use C<alias> to do so. In the first example, if
-Some::Clever::Mixin creates templates named "foo" and "bar", they will be
-aliased to "mixin/foo" and "mixin/bar".
+Gets or sets the L<String::BufferStack> object; this is a class method.
-The second example mixes in the templates defined in Some::Other::Mixin into
-the "/mymix" path and defines a package variable for use only by the alias.
-If this template was defined in Some::Other::Mixin:
+You can use it to manipulate the output from tags as they are output. It's used
+internally to make the tags nest correctly, and be output to the right place.
+We're not sure if there's ever a need for you to frob it by hand, but it does
+enable things like the following:
- template 'howdy' => sub {
- my $self = shift;
- outs "Howdy, " . $self->package_variable('name') || 'Jesse';
+ template simple => sub {
+ html {
+ head {}
+ body {
+ Template::Declare->buffer->set_filter( sub {uc shift} );
+ p { 'Whee!' }
+ p { 'Hello, world wide web!' }
+ Template::Declare->buffer->clear_top if rand() < 0.5;
+ }
+ }
};
-Then use of the "mymixin/howdy" template will output "Howdy, Lary", while use
-of the original template, "howdy", will output "Howdy, Jesse". In other words,
-package variables defined for the alias are available only to the alias, and
-not to the original.
-
-In either case, ineritance continues to work. A template package that inherits
-from Some::Other::Mixin, for example, will be able to access both
-"mymixin/howdy" and "howdy".
+...which outputs, with equal regularity, either:
-=cut
+ <html>
+ <head></head>
+ <body>
+ <P>WHEE!</P>
+ <P>HELLO, WORLD WIDE WEB!</P>
+ </body>
+ </html>
-sub alias {
- my $alias_into = caller(0);
- my $mixin = shift;
- my $prepend_path = shift;
- my $package_vars = shift;
+...or:
+
+ <html>
+ <head></head>
+ <body></body>
+ </html>
+
+We'll leave it to you to judge whether or not that's actually useful.
- $prepend_path =~ s|/+/|/|g;
- $prepend_path =~ s|/$||;
+=head2 Helpers
- my $alias_key = $mixin . " " . $prepend_path;
- push @{ Template::Declare->aliases->{$alias_into} }, $alias_key;
- $alias_into->alias_metadata()->{$alias_key} = {
- class => $mixin,
- path => $prepend_path,
- package_vars => $package_vars
- };
+You don't need to call any of this directly.
-}
+=head3 into
+
+ $class = into $class;
-=head2 import_templates
+C<into> is a helper method providing semantic sugar for the L</mix> method.
+All it does is return the name of the class on which it was called.
+
+=cut
+
+sub into { shift }
+
+=head2 Old, deprecated or just better to avoid
+
+=head3 import_templates
import_templates MyApp::Templates under '/something';
-Import the templates defined in a template class into a subpath via the
-C<import_templates> function. In this example, the templates defined in
-MyApp::Templates will be imported into the "/something" path. Thus, a template
-deined in MyApp::Templates named "foo" will also be accessible via
-"something/foo". This is not unlike mixing in templates with C<alias>, but is
-lighter-weight and package variables cannot be assigned. This is because it is
-really like a hard link to the template, whereas an alias is a copy with the
-variable information attached to it.
+Like C<mix()>, but without support for the C<into> or C<setting> keywords.
+That is, it mixes templates into the calling template class and does not
+support package variables for those mixins.
+
+B<Deprecated> in favor of L</mix>. Will be supported for a long time, but
+new code should use C<mix()>.
=cut
sub import_templates {
- return undef if $_[0] eq __PACKAGE__;
- my $import_into = caller(0);
- my $import_from_base = shift;
- my $prepend_path = shift;
-
- $prepend_path =~ s|/+/|/|g;
- $prepend_path =~ s|/$||;
- $import_from_base->imported_into($prepend_path);
+ my $caller = scalar caller(0);
+ shift->_import($caller, $caller, @_);
+}
- my @packages = reverse grep { $_->isa(__PACKAGE__) }
- Class::ISA::self_and_super_path( $import_from_base );
+=head3 new_buffer_frame
- foreach my $import_from (@packages) {
- foreach my $template_name ( __PACKAGE__->_templates_for($import_from) ) {
- my $code = $import_from->_find_template_sub( _template_name_to_sub($template_name));
- $import_into->register_template( $prepend_path . "/" . $template_name, $code );
- }
- foreach my $template_name ( __PACKAGE__->_private_templates_for($import_from) ) {
- my $code = $import_from->_find_template_sub( _template_name_to_private_sub($template_name) );
- $import_into->register_private_template( $prepend_path . "/" . $template_name, $code );
- }
- }
+ $td->new_buffer_frame;
+ # same as
+ $td->buffer->push( private => 1 );
+
+Creates a new buffer frame, using L<String::BufferStack/push> with C<private>.
+
+B<Deprecated> in favor of dealing with L</buffer> directly.
+
+=cut
+
+sub new_buffer_frame {
+ __PACKAGE__->buffer->push( private => 1 );
}
-=head2 package_variable( VARIABLE )
+=head3 end_buffer_frame
- $td->package_variable( $varname => $value );
- $value = $td->package_variable( $varname );
+ my $buf = $td->end_buffer_frame;
+ # same as
+ my $buf = $td->buffer->pop;
-Returns the value set for a template alias's variable. See L<alias/alias> for
-details.
+Deletes and returns the topmost buffer, using L<String::BufferStack/pop>.
+
+B<Deprecated> in favor of dealing with L</buffer> directly.
=cut
-sub package_variable {
- my $self = shift;
- my $var = shift;
- if (@_) {
- $TEMPLATE_VARS->{$self}->{$var} = shift;
- }
- return $TEMPLATE_VARS->{$self}->{$var};
+sub end_buffer_frame {
+ __PACKAGE__->buffer->pop;
}
-=head2 package_variables( VARIABLE )
+=head3 path_for $template
- $td->package_variables( $variables );
- $variables = $td->package_variables( );
+ my $path = Template::Declare->path_for('index');
-Get or set a hash reference of variables for a template alias. See
-L<alias/alias> for details.
+Returns the path for the template name to be used for show, adjusted with
+paths used in C<mix>. Note that this will only work for the last class into
+which you imported the template. This method is, therefore, deprecated.
=cut
-sub package_variables {
- my $self = shift;
- if (@_) {
- %{ $TEMPLATE_VARS->{$self} } = shift;
- }
- return $TEMPLATE_VARS->{$self};
+# Deprecated in favor of dispatch_to().
+sub roots {
+ # warn "roots() has been deprecated; use dispatch_to() instead\n";
+ my $class = shift;
+ $class->dispatch_to( [ reverse @{ +shift } ] ) if @_;
+ return [ reverse @{ $class->dispatch_to } ];
+}
+
+# Removed methods that no longer work (and were never documented anyway).
+# Remove these no-ops after a few releases (added for 0.41).
+
+=begin comment
+
+=head3 aliases
+
+=head3 alias_metadata
+
+=end comment
+
+=cut
+
+sub aliases {
+ require Carp;
+ Carp::cluck( 'aliases() is a deprecated no-op' );
+}
+
+sub alias_metadata {
+ require Carp;
+ Carp::cluck( 'alias_metadata() is a deprecated no-op' );
+}
+
+sub path_for {
+ my $class = shift;
+ my $template = shift;
+ return ($class->imported_into ||'') . '/' . $template;
}
sub _templates_for {
@@ -753,42 +1493,6 @@
return undef;
}
-sub _has_aliased_template {
- my $package = shift;
- my $template_name = shift;
- my $show_private = shift;
-
- # XXX Should we consider normalizing the path in a more standard way?
- $template_name = "/$template_name" unless $template_name =~ m{^/};
-
- foreach my $alias_key ( @{ Template::Declare->aliases->{$package} } ) {
- my $alias_info = $package->alias_metadata->{$alias_key};
-
- my $alias_prefix = $alias_info->{path};
- $alias_prefix = "/$alias_prefix" unless $alias_prefix =~ m{^/};
- # handle the case where we alias something under '/'. the regex appends
- # a '/' so we need to prevent matching against m{^//};
- $alias_prefix = '' if $alias_prefix eq '/';
-
- # starts with fast check
- next unless rindex($template_name, "$alias_prefix/", 0) == 0;
-
- my $dispatch_to = substr $template_name, length($alias_prefix)+1;
- my $alias_class = $alias_info->{class};
- my $coderef = $alias_class->resolve_template(
- $dispatch_to, $show_private,
- );
- next unless $coderef;
-
- my $package_vars = $alias_info->{package_vars};
- return sub {
- shift @_; # Get rid of the passed-in "$self" class.
- local $TEMPLATE_VARS->{$alias_class} = $package_vars;
- $coderef->($alias_class, at _);
- };
- }
-}
-
sub _dispatch_template {
my $class = shift;
my $code = shift;
@@ -828,6 +1532,61 @@
*{ $class . '::' . $subname } = $coderef;
}
+sub _into {
+ my ($into, $under);
+ if ( eval { $_[0]->isa(__PACKAGE__) } ) {
+ ($into, $under) = (shift, shift);
+ } elsif ( eval { $_[1]->isa(__PACKAGE__) } ) {
+ ($under, $into) = (shift, shift);
+ } else {
+ $into = caller(1);
+ $under = shift;
+ }
+ return $into, $under, @_;
+}
+
+sub _import {
+ return undef if $_[0] eq __PACKAGE__;
+ my ($mixin, $into, $invocant, $prefix, $vars) = @_;
+
+
+ $prefix =~ s|/+/|/|g;
+ $prefix =~ s|/$||;
+ $mixin->imported_into($prefix);
+
+ my @packages = reverse grep { $_->isa(__PACKAGE__) }
+ Class::ISA::self_and_super_path( $mixin );
+
+ foreach my $from (@packages) {
+ for my $tname ( __PACKAGE__->_templates_for($from) ) {
+ my $sname = _template_name_to_sub($tname);
+ $into->register_template(
+ "$prefix/$tname",
+ _import_code( $sname, $from, $invocant || $mixin, $vars )
+ );
+ }
+ for my $tname ( __PACKAGE__->_private_templates_for($from) ) {
+ my $sname = _template_name_to_private_sub($tname);
+ $into->register_private_template(
+ "$prefix/$tname",
+ _import_code( $sname, $from, $invocant || $mixin, $vars )
+ );
+ }
+ }
+}
+
+sub _import_code {
+ my ($sname, $from, $mixin, $vars) = @_;
+ my $code = $from->_find_template_sub( $sname );
+ return $mixin eq $from ? $code : sub { shift; $code->($mixin, @_) }
+ unless $vars;
+ return sub {
+ shift @_; # Get rid of the passed-in "$self" class.
+ local $TEMPLATE_VARS->{$mixin} = $vars;
+ $code->($mixin, @_);
+ };
+}
+
=head1 PITFALLS
We're reusing the perl interpreter for our templating langauge, but Perl was
@@ -860,7 +1619,7 @@
};
But C<xml_decl> is a notable exception. Please always put a trailing semicolon
-after C<xml_decl { ... }>, or you'll mess up the outputs.
+after C<xml_decl { ... }>, or you'll mess up the order of output.
=item *
@@ -931,7 +1690,7 @@
<p></p>
-This's because C<if ( 0 )> is the last expression, so it's returned as the
+This's because C<if ( 0 )> is the last expression, so C<0> is returned as the
value of the whole block, which is used as the content of <p> tag.
To get rid of this, just put an empty string at the end so it returns empty
@@ -945,7 +1704,7 @@
Crawling all over, baby. Be very, very careful. This code is so cutting edge,
it can only be fashioned from carbon nanotubes. But we're already using this
-thing in production :) Make sure you have read the L<PITFALLS/PITFALLS>
+thing in production :) Make sure you have read the L</PITFALLS>
section above :)
Some specific bugs and design flaws that we'd love to see fixed.
Modified: Template-Declare/trunk/lib/Template/Declare/TagSet.pm
==============================================================================
--- Template-Declare/trunk/lib/Template/Declare/TagSet.pm (original)
+++ Template-Declare/trunk/lib/Template/Declare/TagSet.pm Fri Oct 16 16:42:36 2009
@@ -59,7 +59,7 @@
Template::Declare::TagSet is the base class for declaring packages of
Template::Delcare tags. If you need to create new tags for use in your
-temlates, this is the base class for you! Review the source code of
+templates, this is the base class for you! Review the source code of
L<Template::Declare::TagSet::HTML|Template::Declare::TagSet::HTML> for a
useful example.
Modified: Template-Declare/trunk/lib/Template/Declare/Tags.pm
==============================================================================
--- Template-Declare/trunk/lib/Template/Declare/Tags.pm (original)
+++ Template-Declare/trunk/lib/Template/Declare/Tags.pm Fri Oct 16 16:42:36 2009
@@ -9,23 +9,31 @@
our $VERSION = '0.40';
use Template::Declare;
-use vars qw( @EXPORT_OK $PRIVATE $self @TagSubs );
use base 'Exporter';
use Carp qw(carp croak);
use Symbol 'qualify_to_ref';
-our @EXPORT
- = qw( with template private show show_page attr outs
- outs_raw in_isolation $self under
- get_current_attr xml_decl
- smart_tag_wrapper current_template create_wrapper );
+our $self;
+
+our @EXPORT = qw(
+ template private current_template
+ show show_page
+ attr with get_current_attr
+ outs outs_raw
+ xml_decl
+ under setting
+ smart_tag_wrapper create_wrapper
+ $self
+);
+
our @TAG_SUB_LIST;
+our @TagSubs;
*TagSubs = \@TAG_SUB_LIST; # For backward compatibility only
our %ATTRIBUTES = ();
our %ELEMENT_ID_CACHE = ();
our $TAG_NEST_DEPTH = 0;
-our @TEMPLATE_STACK;
+our @TEMPLATE_STACK = ();
our $SKIP_XML_ESCAPING = 0;
@@ -70,7 +78,7 @@
Template::Declare::Tags::install_tag($_, $tagset)
for @$tag_list;
}
- __PACKAGE__->export_to_level(1, $self);
+ __PACKAGE__->export_to_level(1, $self);
}
sub _install {
@@ -174,14 +182,14 @@
C<Template::Declare::TagSet::Foo>, you can load it into a template module like
so:
- use Template::Declare::Tags 'Foo';
+ use Template::Declare::Tags 'Foo';
If your tag set module is not under the
L<Template::Declare::TagSet|Template::Declare::TagSet> namespace, use the
C<from> option to load it. Fore example, if you created a tag set named
C<MyTag::Foo>, then you could load it like so:
- use Template::Declare::Tags Foo => { from => 'MyTag::Foo' };
+ use Template::Declare::Tags Foo => { from => 'MyTag::Foo' };
XML namespaces are emulated by Perl packages. For example, to embed HTML tags
within XUL using the C<html> namespace:
@@ -215,7 +223,7 @@
C<MyApp::Templates> in the previous example.
There may be cases when you want to specify a different Perl package for a
-perticular XML namespace. For instance, if the C<html> Perl package has
+particular XML namespace. For instance, if the C<html> Perl package has
already been used for other purposes in your application and you don't want to
install subs there and mess things up, use the C<package> option to install
them elsewhere:
@@ -247,7 +255,9 @@
=head1 METHODS AND SUBROUTINES
-=head2 template TEMPLATENAME => sub { 'Implementation' };
+=head2 Declaring templates
+
+=head3 template TEMPLATENAME => sub { 'Implementation' };
template select_list => sub {
my $self = shift;
@@ -256,7 +266,7 @@
}
};
-Declares a template in the current package. The first agument to the template
+Declares a template in the current package. The first argument to the template
subroutine will always be a C<Template::Declare> object. Subsequent arguments
will be all those passed to C<show()>. For example, to use the above example
to output a select list of colors, you'd call it like so:
@@ -298,99 +308,94 @@
$codesub,
);
}
-
}
-=head2 create_wrapper WRAPPERNAME => sub { 'Implementation' };
+=head3 private template TEMPLATENAME => sub { 'Implementation' };
- create_wrapper basics => sub {
- my $code = shift;
- html {
- head { title { 'Welcome' } };
- body { $code->() }
+ private template select_list => sub {
+ my $self = shift;
+ select {
+ option { $_ } for @_;
}
};
-C<create_wrapper> declares a wrapper subroutine that can be called like a tag
-sub, but can optionally take arguments to be passed to the wrapper sub. For
-example, if you wanted to wrap all of the output of a template in the usual
-HTML headers and footers, you can do something like this:
+Declares that a template isn't available to be called directly from client
+code. The resulting template can instead only be called from the package in
+which it's created.
- package MyApp::Templates;
- use Template::Declare::Tags;
- use base 'Template::Declare';
+=cut
- BEGIN {
- create_wrapper wrap => sub {
- my $code = shift;
- my %params = @_;
- html {
- head { title { outs "Hello, $params{user}!"} };
- body {
- $code->();
- div { outs 'This is the end, my friend' };
- };
- }
- };
- }
+sub private (@) {
+ my $class = shift;
+ my $subname = shift;
+ my $code = shift;
+ Template::Declare::register_private_template( $class, $subname, $code );
+}
- template inner => sub {
- wrap {
- h1 { outs "Hello, Jesse, s'up?" };
- } user => 'Jesse';
- };
+=head2 Showing templates
-Note how the C<wrap> wrapper function is available for calling after it has
-been declared in a C<BEGIN> block. Also note how you can pass arguments to the
-function after the closing brace (you don't need a comma there!).
+=head3 show [$template_name or $template_coderef], args
-The output from the "inner" template will look something like this:
+ show( main => { user => 'Bob' } );
- <html>
- <head>
- <title>Hello, Jesse!</title>
- </head>
- <body>
- <h1>Hello, Jesse, s'up?</h1>
- <div>This is the end, my friend</div>
- </body>
- </html>
+Displays templates. The first argument is the name of the template to be
+displayed. Any additional arguments will be passed directly to the template.
+
+C<show> can either be called with a template name or a package/object and a
+template. (It's both functional and OO.)
+
+If called from within a Template::Declare subclass, then private templates are
+accessible and visible. If called from something that isn't a
+Template::Declare, only public templates will be visible.
+
+From the outside world, users can either call C<< Template::Declare->show() >>,
+C<< show() >> exported from Template::Declare::Tags or
+C<Template::Declare::Tags::show()> directly to render a publicly visible template.
+
+Private templates may only be called from within the C<Template::Declare>
+package.
=cut
-sub create_wrapper ($$) {
- my $wrapper_name = shift;
- my $coderef = shift;
- my $template_class = caller;
+sub show {
+ my $template = shift;
+
+ # if we're inside a template, we should show private templates
+ if ( caller->isa('Template::Declare') ) {
+ _show_template( $template, 1, \@_ );
+ return Template::Declare->buffer->data;
+ } else {
+ show_page( $template, @_);
+ }
- # Shove the code ref into the calling class.
- no strict 'refs';
- *{"$template_class\::$wrapper_name"} = sub (&;@) { goto $coderef };
}
-=head2 private template TEMPLATENAME => sub { 'Implementation' };
+=head3 show_page
- private template select_list => sub {
- my $self = shift;
- select {
- option { $_ } for @_;
- }
- };
+ show_page( main => { user => 'Bob' } );
-Declares that a template isn't available to be called directly from client
-code. The resulting template can instead only be called from the package in
-which it's created.
+Like C<show()>, but does not dispatch to private templates. It's used
+internally by C<show()> when when that method is called from outside a
+template class.
=cut
-sub private (@) {
- my $class = shift;
- my $subname = shift;
- my $code = shift;
- Template::Declare::register_private_template( $class, $subname, $code );
+sub show_page {
+ my $template = shift;
+ my $args = \@_;
+
+ Template::Declare->buffer->push(
+ private => defined wantarray,
+ from => "T::D path $template",
+ );
+ _show_template( $template, 0, $args );
+ %ELEMENT_ID_CACHE = ();
+ return Template::Declare->buffer->pop;
}
-=head2 attr HASH
+=head2 Attributes
+
+=head3 attr HASH
attr { src => 'logo.png' };
@@ -405,14 +410,6 @@
'This is a welcoming paragraph';
}
-Attributes can also be specified by using C<is>, as in
-
- p {
- class is 'greeting text';
- id is 'welcome';
- 'This is a welcoming paragraph';
- }
-
=cut
sub attr (&;@) {
@@ -426,46 +423,70 @@
return @_;
}
-=head2 xml_decl HASH
+=head3 ATTR is VALUE
- xml_decl { 'xml', version => '1.0' };
+Attributes can also be specified by using C<is>, as in
-Emits an XML declaration. For example:
+ p {
+ class is 'greeting text';
+ id is 'welcome';
+ 'This is a welcoming paragraph';
+ }
- xml_decl { 'xml', version => '1.0' };
- xml_decl { 'xml-stylesheet', href => "chrome://global/skin/", type => "text/css" };
+A few tricks work for 'is':
-Produces:
+ http_equiv is 'foo'; # => http-equiv="foo"
+ xml__lang is 'foo'; # => xml:lang="foo"
- <?xml version="1.0"?>
- <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+So double underscore replaced with colon and single underscore with dash.
=cut
-sub xml_decl (&;$) {
- my $code = shift;
- my @rv = $code->();
- my $name = shift @rv;
- outs_raw("<?$name");
- while ( my ( $field, $val ) = splice( @rv, 0, 2 ) ) {
- # only defined whle in a tag context
- outs_raw(qq/ $field="$val"/);
+# 'is' is declared later, when needed, using 'local *is::AUTOLOAD = sub {};'
+
+=head3 with
+
+ with ( id => 'greeting', class => 'foo' ),
+ p { 'Hello, World wide web' };
+
+An alternative way to specify attributes for a tag, just for variation. The
+standard way to do the same as this example using C<attr> is:
+
+ p { attr { id => 'greeting', class => 'foo' }
+ 'Hello, World wide web' };
+
+=cut
+
+sub with (@) {
+ %ATTRIBUTES = ();
+ while ( my ( $key, $val ) = splice( @_, 0, 2 ) ) {
+ no warnings 'uninitialized';
+ $ATTRIBUTES{$key} = $val;
+
+ if ( lc($key) eq 'id' ) {
+ if ( $ELEMENT_ID_CACHE{$val}++ ) {
+ warn
+ "HTML appears to contain illegal duplicate element id: $val";
+ }
+ }
+
}
- outs_raw("?>\n");
- return @_;
+ wantarray ? () : '';
}
-=head2 outs STUFF
+=head2 Displaying text and raw data
+
+=head3 outs STUFF
p { outs 'Grettings & welcome pyoonie hyoomon.' }
HTML-encodes its arguments and appends them to C<Template::Declare>'s output
buffer. This is similar to simply returning a string from a tag function call,
-but is occaisionally useful when you need to output a mix of things, as in:
+but is occasionally useful when you need to output a mix of things, as in:
p { outs 'hello'; em { 'world' } }
-=head2 outs_raw STUFF
+=head3 outs_raw STUFF
p { outs_raw "That's what <em>I'm</em> talking about!' }
@@ -477,40 +498,9 @@
sub outs { _outs( 0, @_ ); }
sub outs_raw { _outs( 1, @_ ); }
-sub _outs {
- my $raw = shift;
- my @phrases = (@_);
-
- Template::Declare->buffer->push(
- private => (defined wantarray and not wantarray), from => "T::D outs"
- );
-
- foreach my $item ( grep {defined} @phrases ) {
- my $returned = ref($item) eq 'CODE'
- ? $item->()
- : $raw
- ? $item
- : _postprocess($item);
- Template::Declare->buffer->append( $returned );
- }
- return Template::Declare->buffer->pop;
-}
-
-=begin comment
-
-=head2 get_current_attr
-
-Deprecated.
-
-=end comment
-
-=cut
+=head2 Installing tags and wrapping stuff
-sub get_current_attr ($) {
- $ATTRIBUTES{ $_[0] };
-}
-
-=head2 install_tag TAGNAME, TAGSET
+=head3 install_tag TAGNAME, TAGSET
install_tag video => 'Template::Declare::TagSet::HTML';
@@ -575,39 +565,9 @@
);
}
-=head2 with
-
- with ( id => 'greeting', class => 'foo' ),
- p { 'Hello, World wide web' };
-
-An alternative way to specify attributes for a tag, just for variation. The
-standard way to do the same as this example using C<attr> is:
-
- p { attr { id => 'greeting', class => 'foo' }
- 'Hello, World wide web' };
-
-=cut
-
-sub with (@) {
- %ATTRIBUTES = ();
- while ( my ( $key, $val ) = splice( @_, 0, 2 ) ) {
- no warnings 'uninitialized';
- $ATTRIBUTES{$key} = $val;
-
- if ( lc($key) eq 'id' ) {
- if ( $ELEMENT_ID_CACHE{$val}++ ) {
- warn
- "HTML appears to contain illegal duplicate element id: $val";
- }
- }
-
- }
- wantarray ? () : '';
-}
-
-=head2 smart_tag_wrapper
+=head3 smart_tag_wrapper
- # create a tag that has access to the arguments set with with.
+ # create a tag that has access to the arguments set with L</with>.
sub sample_smart_tag (&) {
my $code = shift;
@@ -625,14 +585,14 @@
The output would be
- keys: baz, foo
- Hello, World!
+ keys: baz, foo
+ Hello, World!
The smart tag wrapper allows you to create code that has access to the
attribute arguments specified via C<with>. It passes those arguments in to the
wrapped code in C<@_>. It also takes care of putting the output in the right
place and tidying up after itself. This might be useful to change the behavior
-of a template based on attributs passed to C<with>.
+of a template based on attributes passed to C<with>.
=cut
@@ -645,27 +605,169 @@
my %attr = %ATTRIBUTES;
%ATTRIBUTES = (); # prevent leakage
- my $last = join '', #
- map { ref($_) ? $_ : _postprocess($_) } #
+ my $last = join '',
+ map { ref($_) ? $_ : _postprocess($_) }
$coderef->(%attr);
my $content = Template::Declare->buffer->pop;
$content .= "$last" if not length $content and length $last;
- Template::Declare->buffer->append( $content ) ;
+ Template::Declare->buffer->append( $content );
return '';
}
+=head3 create_wrapper WRAPPERNAME => sub { 'Implementation' };
+
+ create_wrapper basics => sub {
+ my $code = shift;
+ html {
+ head { title { 'Welcome' } };
+ body { $code->() }
+ }
+ };
+
+C<create_wrapper> declares a wrapper subroutine that can be called like a tag
+sub, but can optionally take arguments to be passed to the wrapper sub. For
+example, if you wanted to wrap all of the output of a template in the usual
+HTML headers and footers, you can do something like this:
+
+ package MyApp::Templates;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ BEGIN {
+ create_wrapper wrap => sub {
+ my $code = shift;
+ my %params = @_;
+ html {
+ head { title { outs "Hello, $params{user}!"} };
+ body {
+ $code->();
+ div { outs 'This is the end, my friend' };
+ };
+ }
+ };
+ }
+
+ template inner => sub {
+ wrap {
+ h1 { outs "Hello, Jesse, s'up?" };
+ } user => 'Jesse';
+ };
+
+Note how the C<wrap> wrapper function is available for calling after it has
+been declared in a C<BEGIN> block. Also note how you can pass arguments to the
+function after the closing brace (you don't need a comma there!).
+
+The output from the "inner" template will look something like this:
+
+ <html>
+ <head>
+ <title>Hello, Jesse!</title>
+ </head>
+ <body>
+ <h1>Hello, Jesse, s'up?</h1>
+ <div>This is the end, my friend</div>
+ </body>
+ </html>
+
+=cut
+
+sub create_wrapper ($$) {
+ my $wrapper_name = shift;
+ my $coderef = shift;
+ my $template_class = caller;
+
+ # Shove the code ref into the calling class.
+ no strict 'refs';
+ *{"$template_class\::$wrapper_name"} = sub (&;@) { goto $coderef };
+}
+
+=head2 Helpers
+
+=head3 xml_decl HASH
+
+ xml_decl { 'xml', version => '1.0' };
+
+Emits an XML declaration. For example:
+
+ xml_decl { 'xml', version => '1.0' };
+ xml_decl { 'xml-stylesheet', href => "chrome://global/skin/", type => "text/css" };
+
+Produces:
+
+ <?xml version="1.0"?>
+ <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+=cut
+
+sub xml_decl (&;$) {
+ my $code = shift;
+ my @rv = $code->();
+ my $name = shift @rv;
+ outs_raw("<?$name");
+ while ( my ( $field, $val ) = splice( @rv, 0, 2 ) ) {
+ outs_raw(qq/ $field="$val"/);
+ }
+ outs_raw("?>\n");
+ return @_;
+}
+
+=head3 current_template
+
+ my $path = current_template();
+
+Returns the absolute path of the current template
+
+=cut
+
+sub current_template {
+ return $TEMPLATE_STACK[-1] || '';
+}
+
+=head3 under
+
+C<under> is a helper function providing semantic sugar for the C<mix> method
+of L<Template::Declare|Template::Declare/"mix">.
+
+=cut
+
+sub under ($) { return shift }
+
+=head3 setting
+
+C<setting> is a helper function providing semantic sugar for the C<mix> method
+of L<Template::Declare|Template::Declare/"mix">.
+
+=cut
+
+sub setting ($) { return shift }
+
+=begin comment
+
+=head2 get_current_attr
+
+Deprecated.
+
+=end comment
+
+=cut
+
+sub get_current_attr ($) {
+ $ATTRIBUTES{ $_[0] };
+}
+
sub _tag {
my $tagset = shift;
- my $tag = shift;
+ my $tag = shift;
my $code = shift;
my $more_code = shift;
$tag = $tagset->namespace . ":$tag" if defined $tagset->namespace;
Template::Declare->buffer->append(
"\n"
- . ( " " x $TAG_NEST_DEPTH ) . "<$tag"
+ . ( " " x $TAG_NEST_DEPTH )
+ . "<$tag"
. join( '',
map { qq{ $_="} . ( $ATTRIBUTES{$_} || '' ) . qq{"} }
sort keys %ATTRIBUTES )
@@ -686,7 +788,7 @@
$field =~ s/_/-/g; # http_equiv is 'bar' ====> http-equiv="bar"
# Squash empty values, but not '0' values
- my $val = join( ' ', grep { defined $_ && $_ ne '' } @_ );
+ my $val = join ' ', grep { defined $_ && $_ ne '' } @_;
append_attr( $field, $val );
};
@@ -706,7 +808,7 @@
}
my $content = Template::Declare->buffer->pop;
$content .= "$last" if not length $content and length $last;
- Template::Declare->buffer->append($attrs);
+ Template::Declare->buffer->append($attrs) if length $attrs;
if (length $content) {
Template::Declare->buffer->append(">$content");
@@ -724,69 +826,6 @@
: '';
}
-=head2 show [$template_name or $template_coderef], args
-
- show( main => { user => 'Bob' } );
-
-Displays templates. The first agument is the name of the emplate to be
-displayed. Any additional arguments will be passed directly to the template.
-
-C<show> can either be called with a template name or a package/object and a
-template. (It's both functional and OO.)
-
-If called from within a Template::Declare subclass, then private templates are
-accessible and visible. If called from something that isn't a
-Template::Declare, only public templates wil be visible.
-
-From the outside world, users can either call C<< Template::Declare->show() >>
-or C<Template::Declare::Tags::show()> to render a publicly visible template.
-
-Private templates may only be called from within the C<Template::Declare>
-package.
-
-=cut
-
-sub show {
- my $template = shift;
-
- # if we're inside a template, we should show private templates
- if ( caller->isa('Template::Declare') ) {
- _show_template( $template, 1, \@_ );
- return Template::Declare->buffer->data;
- } else {
- show_page( $template, @_);
- }
-
-}
-
-=head2 show_page
-
- show_page( main => { user => 'Bob' } );
-
-Like C<show()>, but does not dispatch to private templates. It's used
-internally by C<show()> when when that method is called from outside a
-template class.
-
-=cut
-
-sub show_page {
- my $template = shift;
- my $args = \@_;
-
- if (defined wantarray) {
- Template::Declare->buffer->push( private => 1, from => "T::D path $template" );
- _show_template( $template, 0, $args );
- %ELEMENT_ID_CACHE = ();
- return Template::Declare->buffer->pop;
- } else {
- Template::Declare->buffer->push( from => "T::D path $template" );
- _show_template( $template, 0, $args );
- %ELEMENT_ID_CACHE = ();
- Template::Declare->buffer->pop;
- return undef;
- }
-}
-
sub _resolve_template_path {
my $template = shift;
@@ -811,19 +850,6 @@
return join '/', @parts;
}
-=head2 current_template
-
- my $path = current_template();
-
-Returns the absolute path of the current template
-
-=cut
-
-sub current_template {
- return $TEMPLATE_STACK[-1] || '';
-}
-
-
sub _show_template {
my $template = shift;
my $inside_template = shift;
@@ -859,6 +885,25 @@
return;
}
+sub _outs {
+ my $raw = shift;
+ my @phrases = (@_);
+
+ Template::Declare->buffer->push(
+ private => (defined wantarray and not wantarray), from => "T::D outs"
+ );
+
+ foreach my $item ( grep {defined} @phrases ) {
+ my $returned = ref($item) eq 'CODE'
+ ? $item->()
+ : $raw
+ ? $item
+ : _postprocess($item);
+ Template::Declare->buffer->append( $returned );
+ }
+ return Template::Declare->buffer->pop;
+}
+
sub _postprocess {
my $val = shift;
my $skip_postprocess = shift;
@@ -883,16 +928,6 @@
return $val;
}
-=head2 under
-
-C<under> is a helper function providing semantic sugar for the
-C<import_templates> and C<alias> methods of
-L<Template::Declare|Template::Declare>.
-
-=cut
-
-sub under ($) { return shift }
-
=begin comment
=head2 append_attr
@@ -912,7 +947,7 @@
=head1 VARIABLES
-=over
+=over 4
=item C<@Template::Declare::Tags::EXPORT>
@@ -972,7 +1007,7 @@
=head1 COPYRIGHT
-Copyright 2006-2009 Best Practical Solutions, LLC
+Copyright 2006-2009 Best Practical Solutions, LLC.
=cut
Modified: Template-Declare/trunk/t/aliasing.t
==============================================================================
--- Template-Declare/trunk/t/aliasing.t (original)
+++ Template-Declare/trunk/t/aliasing.t Fri Oct 16 16:42:36 2009
@@ -8,10 +8,12 @@
template 'aliased' => sub {
my $self = shift;
- div { outs( 'This is aliased from ' . $self ) };
+ div { outs_raw "Invocant: '$self'" };
div { 'Variable ', $self->package_variable('VARIABLE') };
};
+private template shhhh => sub { };
+
##############################################################################
package Wifty::UI::aliased_subclass_pkg;
use base qw/Wifty::UI::aliased_pkg/;
@@ -27,7 +29,7 @@
html {
head {};
body { show 'private-content'; };
- }
+ }
};
@@ -38,17 +40,23 @@
};
};
-
-alias Wifty::UI::aliased_pkg under '/aliased_pkg', { VARIABLE => 'SET' } ;
+alias Wifty::UI::aliased_pkg under '/aliased_pkg', setting { VARIABLE => 'SET' } ;
alias Wifty::UI::aliased_pkg under '/aliased_pkg2';
alias Wifty::UI::aliased_subclass_pkg under '/aliased_subclass_pkg';
##############################################################################
package main;
use Template::Declare::Tags;
+
+# Alias from outside the class.
+alias Wifty::UI::aliased_pkg into Wifty::UI, under '/aliased_pkg3';
+# And reverse.
+alias Wifty::UI::aliased_pkg under '/aliased_pkg4', into Wifty::UI
+
+# Fire it up.
Template::Declare->init( dispatch_to => ['Wifty::UI'] );
-use Test::More tests => 19;
+use Test::More tests => 30;
require "t/utils.pl";
ok( Wifty::UI::aliased_pkg->has_template('aliased'), 'Aliased package should have template' );
@@ -56,7 +64,7 @@
ok( Wifty::UI::aliased_subclass_pkg->has_template('aliased'), 'Subclass should' );
ok( Template::Declare->has_template('aliased_pkg/aliased'), 'TD should find alias' );
-
+ok( Template::Declare->has_template('aliased_pkg2/shhhh', 1), 'TD should find private mix' );
ok( Template::Declare->has_template('aliased_subclass_pkg/aliased'),
'Alias should be visible in a subclass, too' );
@@ -64,9 +72,9 @@
{
# Try the first alias with a variable set.
ok my $simple = ( show('aliased_pkg/aliased') ), 'Should get output from alias template';
- like( $simple, qr'This is aliased', 'Its output should be right' );
+ like( $simple, qr'Invocant:', 'Its output should be right' );
like( $simple, qr'Variable SET' , "The variable was set");
- like( $simple, qr'Wifty::UI::aliased_pkg',
+ like( $simple, qr{'Wifty::UI::aliased_pkg'},
'$self is correct in template block' );
ok_lint($simple);
}
@@ -74,9 +82,29 @@
{
# Try the second alias with no variable.
ok my $simple = ( show('aliased_pkg2/aliased') ), 'Should get output from second alias';
- like( $simple, qr'This is aliased', 'Its output should be right' );
+ like( $simple, qr'Invocant:', 'Its output should be right' );
+ unlike( $simple, qr'Varialble SET' , 'But the variable should not be set');
+ like( $simple, qr{'Wifty::UI::aliased_pkg'},
+ '$self is correct in template block' );
+ ok_lint($simple);
+}
+
+{
+ # Try the third alias using `into`.
+ ok my $simple = ( show('aliased_pkg3/aliased') ), 'Should get output from third alias';
+ like( $simple, qr'Invocant:', 'Its output should be right' );
+ unlike( $simple, qr'Varialble SET' , 'But the variable should not be set');
+ like( $simple, qr{'Wifty::UI::aliased_pkg'},
+ '$self is correct in template block' );
+ ok_lint($simple);
+}
+
+{
+ # Try the fourth with `into` and `under` reversed.
+ ok my $simple = ( show('aliased_pkg4/aliased') ), 'Should get output from fourth alias';
+ like( $simple, qr'Invocant:', 'Its output should be right' );
unlike( $simple, qr'Varialble SET' , 'But the variable should not be set');
- like( $simple, qr'Wifty::UI::aliased_pkg',
+ like( $simple, qr{'Wifty::UI::aliased_pkg'},
'$self is correct in template block' );
ok_lint($simple);
}
@@ -86,12 +114,12 @@
'Should get output from superclass template';
like(
$simple,
- qr'This is aliased',
+ qr'Invocant:',
"We should get the aliased version in the subclass"
);
like(
$simple,
- qr'Wifty::UI::aliased_subclass_pkg',
+ qr{'Wifty::UI::aliased_subclass_pkg'},
'$self is correct in template block'
);
ok_lint($simple);
Modified: Template-Declare/trunk/t/dispatch_order.t
==============================================================================
--- Template-Declare/trunk/t/dispatch_order.t (original)
+++ Template-Declare/trunk/t/dispatch_order.t Fri Oct 16 16:42:36 2009
@@ -27,7 +27,8 @@
##############################################################################
package main;
-use Test::More tests => 16;
+use Test::More tests => 24;
+#use Test::More 'no_plan';
# Check template resolution with the deprecated `roots` parameterx.
ok !Template::Declare->init( roots => ['Wifty::Foo', 'Wifty::Bar'] ),
@@ -77,7 +78,7 @@
'Bip should now have precedence';
##############################################################################
-# Now try the dame stuff with aliases.
+# Now try the same stuff with aliases.
##############################################################################
package Mifty::Foo;
use base qw/Template::Declare/;
@@ -134,3 +135,137 @@
is +Template::Declare->show('hello'), 'hello from Bip',
'Mifty::Bip should now have precedence';
+
+##############################################################################
+# Now try the same stuff with mixes.
+##############################################################################
+package Sifty::Foo;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+template hello => sub { outs 'hello from Foo' };
+
+##############################################################################
+package Sifty::Bar;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+template hello => sub { outs 'hello from Bar' };
+
+##############################################################################
+package Sifty::Baz;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+template hello => sub { outs 'hello from Baz' };
+
+##############################################################################
+package Sifty::Bip;
+use base qw/Template::Declare/;
+use Template::Declare::Tags;
+template hello => sub { outs 'hello from Bip' };
+
+##############################################################################
+# Import the Baz templates into Bar.
+package Sifty::Bar;
+mix Sifty::Baz under '/';
+
+##############################################################################
+package main;
+ok !Template::Declare->init( dispatch_to => ['Sifty::Foo', 'Sifty::Bar'] ),
+ 'init to dispatch to Sifty::Foo and Sifty::Bar';
+
+is +Template::Declare->show('hello'), 'hello from Foo',
+ 'Sifty::Foo should have precedence';
+
+##############################################################################
+# Import the Baz templates into Foo.
+package Sifty::Foo;
+import_templates Sifty::Baz under '/';
+
+##############################################################################
+package main;
+ok !Template::Declare->init( dispatch_to => ['Sifty::Foo', 'Sifty::Bar'] ),
+ 'init to dispatch to Sifty::Foo and Sifty::Bar again';
+
+is +Template::Declare->show('hello'), 'hello from Baz',
+ 'Sifty::Baz::hello should have replaced Sifty::Foo::hello';
+
+# Now dispatch only to Bip and Foo.
+ok !Template::Declare->init( dispatch_to => ['Sifty::Bip', 'Sifty::Foo'] ),
+ 'init to dispatch to Sifty::Bip and Sifty::Foo';
+
+is +Template::Declare->show('hello'), 'hello from Bip',
+ 'Sifty::Bip should now have precedence';
+
+##############################################################################
+# Doc example.
+ package MyApp::UI::Standard;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ template image => sub {
+ my ($self, $src, $title) = @_;
+ img {
+ src is $src;
+ title is $title;
+ };
+ };
+
+ package MyApp::UI::Standard;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ template image => sub {
+ my ($self, $src, $title, $caption) = @_;
+ div {
+ class is 'std';
+ img {
+ src is $src;
+ title is $title;
+ };
+ p {
+ class is 'caption';
+ outs $caption;
+ };
+ };
+ };
+
+##############################################################################
+ package MyApp::UI::Formal;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ template image => sub {
+ my ($self, $src, $title, $credit, $caption) = @_;
+ div {
+ class is 'formal';
+ img {
+ src is $src;
+ title is $title;
+ };
+ p {
+ class is 'credit';
+ outs "Photo by $credit";
+ };
+ p {
+ class is 'caption';
+ outs $caption;
+ };
+ };
+ };
+
+##############################################################################
+package main;
+ my @template_classes = 'MyApp::UI::Standard';
+ Template::Declare->init( dispatch_to => \@template_classes );
+ is +Template::Declare->show('image', 'foo.png', 'Foo'), q{
+<div class="std">
+ <img src="foo.png" title="Foo" />
+ <p class="caption"></p>
+</div>}, 'Should get standard image output';
+
+ unshift @template_classes, 'MyApp::UI::Formal';
+ is +Template::Declare->show('image', 'ap.png', 'AP Photo', 'Clark Kent', 'Big news'), q{
+<div class="formal">
+ <img src="ap.png" title="AP Photo" />
+ <p class="credit">Photo by Clark Kent</p>
+ <p class="caption">Big news</p>
+</div>}, 'Should get formal image output';
Modified: Template-Declare/trunk/t/importing.t
==============================================================================
--- Template-Declare/trunk/t/importing.t (original)
+++ Template-Declare/trunk/t/importing.t Fri Oct 16 16:42:36 2009
@@ -8,7 +8,7 @@
template 'imported' => sub {
my $self = shift;
- div { outs( 'This is imported from ' . $self ) };
+ div { outs_raw "Invocant: '$self'" };
};
##############################################################################
@@ -22,7 +22,7 @@
use Template::Declare::Tags;
template simple => sub {
-
+ print '# ', ref +shift, $/;
html {
head {};
body { show 'private-content'; };
@@ -92,22 +92,21 @@
{
ok my $simple = ( show('imported_pkg/imported') ), 'Should get output for imported template';
- like( $simple, qr'This is imported', 'Its output should be correct' );
- like( $simple, qr'Wifty::UI', '$self is correct in template block' );
+ like( $simple, qr'Invocant:', 'Its output should be correct' );
+ like( $simple, qr{'Wifty::UI'}, '$self is correct in template block' );
ok_lint($simple);
}
-
{
ok my $simple = ( show('imported_subclass_pkg/imported') ),
'Should get output from imported template from subclass';
like(
$simple,
- qr'This is imported',
+ qr'Invocant:',
"We got the imported version in the subclass"
);
like(
$simple,
- qr'Wifty::UI',
+ qr{'Wifty::UI'},
'$self is correct in template block'
);
ok_lint($simple);
Modified: Template-Declare/trunk/t/smart_tag_wrapper.t
==============================================================================
--- Template-Declare/trunk/t/smart_tag_wrapper.t (original)
+++ Template-Declare/trunk/t/smart_tag_wrapper.t Fri Oct 16 16:42:36 2009
@@ -33,7 +33,7 @@
use Template::Declare::Tags;
Template::Declare->init( dispatch_to => ['Wifty::UI'] );
-use Test::More tests => 2;
+use Test::More tests => 4;
require "t/utils.pl";
my $simple = show('simple');
@@ -50,3 +50,47 @@
. "\nSTART \nsecond\nEND\n", #
"got correct output for simple"
);
+
+##############################################################################
+# Documentation example.
+ package My::Template;
+ use Template::Declare::Tags;
+ use base 'Template::Declare';
+
+ sub myform (&) {
+ my $code = shift;
+
+ smart_tag_wrapper {
+ my %params = @_; # set using 'with'
+ form {
+ attr { %{ $params{attr} } };
+ $code->();
+ input { attr { type => 'submit', value => $params{value} } };
+ };
+ };
+ }
+
+ template edit_prefs => sub {
+ with(
+ attr => { id => 'edit_prefs', action => 'edit.html' },
+ value => 'Save'
+ ), myform {
+ label { 'Time Zone' };
+ input { type is 'text'; name is 'tz' };
+ };
+ };
+
+ package main;
+ Template::Declare->init( dispatch_to => ['My::Template'] );
+
+ok my $output = Template::Declare->show('edit_prefs'), 'Get edit_prefs output';
+is(
+ $output,
+ qq{
+
+<form action="edit.html" id="edit_prefs">
+ <label>Time Zone</label>
+ <input type="text" name="tz" />
+ <input type="submit" value="Save" />
+</form>}, "got correct output for simple"
+);
More information about the Jifty-commit
mailing list