You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Thomas Klausner <do...@zsi.at> on 2002/11/15 23:42:22 UTC

RFC: Template::YetAnother

Hi!

Yes, this is a RFC for Yet Another Templating System. I know that
there are a lot of those around. But this own might be
different/interesting (at least I think/hope so...)

I also posted this on perlmonks:
http://www.perlmonks.org/index.pl?node_id=213300

So, here it is, in POD format:

=pod

=head1 NAME

Template::YetAnother

=head1 INTRODUCTION

=head2 Why another templating system?

There are a lot of templating modules on CPAN. Some are obvious, some
are hidden in very strange namespaces (eg HTML::Processor). Some are
used a lot, some not. I read a lot of manpages, but definitly not all
and none completly. If there is a module doing what I am proposing,
please inform me!

Before we continue, read Perrin Harkins' "Choosing a Templating
System", available here:
http://perl.apache.org/docs/tutorials/tmpl/comparison/comparison.html

There are different types of Templating Systems available. Some are
complete Application Frameworks, including stuff like Session
Management, Form Handling etc. Examples include Mason, AxKit and
Embperl. They are nice. They work. But that's not what I'm looking
for.

I want Just Templates.

Why?

Because IMO, the main reason for using templates is to seperate code
from markup. The code produces some data. The markup displays the
data. Those Application Frameworks don't seem to be too good at
seperating code and markup (I have to admit though, that I know next
to nothing about them, only that they are too big/powerfull). After
all, they B<embed> code in markup.

So I am looking for a "pipeline"-type, Just-Template System, which
reduces the number of available modules somewhat.

The best-known contestors here are TemplateToolkit
resp. Apache::Template and HTML::Template. But if you look at there
manpages, you'll quickly find references to stuff like C<TPL_LOOP>
(HTML::Template). TT2 even has it's own mini-language. So, once again,
code (even rather trivial) mixed with the markup.

There is one module, CGI::FastTemplate, that does seperate code from
markup completly. But the way different templates are strung together
seems rather comlicated to me.

But why is there no Templating System with a clean seperation of code
and markup?

There are two types of code (at least) that pollute nearly all
Templating Systems:

=over

=item * Loops

Loops are one of the things computers do best (for very good reasons,
mainly lazyness of humans). So, a template should be able to handle
large amounts of similar data using ... a template. Obvious. So a
Templating System must handle Loops. Most (all?) do it by adding some
sort of LOOP or FOREACH Syntax, thereby introducing code into the
markup. But there is another way to loop over data: Recursion.

=item * If-Blocks

As the template is rather general, you want it to handle slightly
different kinds of data differently. One very obvious example would be
to print something like "No Data" instead of C<undef>. Or to highlight
the current item in a list.

Often IF-Blocks are also used to present different kinds of data
differently, which can lead to long series of IF-ELSIF-ELSE
blocks. Which is a clear pointer that one should use Object
Orientation instead.

Another way would be to add something like attributes to the data. But as far as I know, attributes aren't included that thightly into Perl as OO.

=back

So I am looking for a Templating System that does Just Templating, no
code in the markup, maybe by using recursion and OO.

I didn't find anything.

So I am proposing this:

=head2 Template::YetAnother

The name is just a placeholder right now, other ideas are:

=over

=item * Template::Dumper

=item * Template::Stringify

=item * Template::OO

=item * Template::OO_Dumper

=item * any other ideas?

=back

Template::YetAnother is yet another Templating module, using a
slightly different approach than most of the other Templating modules.

The templates are completly dumb. There is B<absolutly no> piece of
code in a template - neither Perl nor "mini language". A template
consists of arbitrary text (e.g. HTML) and Template Tags, e.g.
[% title %]

Your application builds up a data structure. The data structure
consists of various Perl Data Types (Strings, Arrays, Hashes) and
Template::YetAnother Objects (or Data Structures marked with some
other kind of metainformation, e.g. with attributes)

The data structure gets passed to Template::YetAnother, which
magically find the right template for each object and replaces all
Template Tags (recursivly) with the dumped/stringified data structure.

Template::YetAnother is like Data::Dumper on steroids. It's the big
Stringifyer.

Template::YetAnother doesn't use one monolithic template, but a lot of
small template fragments, each one correlating to a data type
generated by the application.

Template::YetAnother is just an idea right now. I am trying the "write
documentation, write tests, write code" way of development... There is
only a small prove-of-concept type bit of code (I can send it/post it
if somebody cares..). I'll really appreciate feedback on this.

I hope that this descripction is clear enought. If not, let me know and I'll post some clarification / examples.

=head1 SYNOPSIS

  # generate a new template handler
  my $th=Template::YetAnother->new
                ({
                  namespace=>'acme',
                  template_dir=>'/projects/acme/templates/',
                 });
 
  # build up a data structure
  $data={
         title=>$th->title('this is the title'),
         breadcrumb=>[
                      $th->link({url=>'/index.html',text=>'Home'}),
                      $th->separator_breadcrumb,
                      $th->link({url=>'/acme/index.html',text=>'Acme'}),
                      $th->separator_breadcrumb,
                      $th->link({url=>'/acme/bleach.html',text=>'Bleach'}),
                     ],
         content=>[
                   $th->element({heading=>'SYNOPSIS',value=>'blabla'}),
                   $th->element({heading=>'DESCRIPTION',value=>'foo bar'}),
                  ],
         lastmod=>scalar localtime,
 
        };

   # fill template & print
   print $th->fill({data=>$data});
 
   ##################################################
   # for this to work, we need the following files in
   # /projects/acme/templates
 
   # file: main.tpl
   <html><head><title>[% title %]</title></head>
   <body>
   <center>[% breadcrumb %]</center>
   <h1>[% title %]</h1>
   [% content %]
   <hr>
   [% lastmod %]
 
   # file: link.tpl
   <a href='[% url %]'>[% text %]</a>
 
   # file: seperator.tpl
    / 
 
   # file: element.tpl
   <h3>[% heading %]</h3>
   <p>[% value %]</p>
 
   ##################################################
   # the finished template should look like this:
 
   <html><head><title>this is the title</title></head>
   <body>
   <center>
   <a href='/index.html'>Home</a> /
   <a href='/acme/index.html'>Acme</a> /
   <a href='/acme/bleach.html'>Bleach</a>
   </center>
   <h1>this is the title</h1>
   <h3>SYNOPSIS</h3>
   <p>blabla</p>
   <h3>DESCRIPTION</h3>
   <p>foo bar</p>
   <hr>
   Thu Nov  7 21:51:05 200


=head1 DESCRIPTION

=head2 new

  my $th=Template::YetAnother->new({
                            template_dir=>'/path/to/templates/',
                            # namespace=>'projectname',
                            # start_tag=>'<--',
                            # end_tag=>  '-->',
                            });

Generates a new Template::YetAnother Handler Object.

=head2 fill

  $th->fill($data);

Fill the template with the data in the data structure.

=head2 _gen

  my $fragment=$th->_gen('type',$data)

Generates a new Template Fragment

You usually do not have to call this. You just say

  $th->type($data)

and AUTOLOAD passes it to C<_gen>

=head1 EXPORT

Nothing.

=head1 SEE ALSO

Search for C<template> on http://search.cpan.org, if you dare.

=head1 AUTHOR

Thomas Klausner, domm@zsi.at, http://domm.zsi.at

=head1 COPYRIGHT

This module is Copyright (c) 2002 Thomas Klausner, ZSI.
All rights reserved.

You may use and distribute this module according to the same terms
that Perl is distributed under.

=cut


-- 
#!/usr/bin/perl                                http://domm.zsi.at
for(ref(bless[],just'another'perl'hacker)){s-:+-$"-g&&print$_.$/}

Re: RFC: Template::YetAnother

Posted by Robert Landrum <rl...@aol.net>.
I've said it before...  :)

http://mathforum.org/epigone/modperl/zhixswahar/v04210105b76eecf6c2be@%5b192.168.1.3%5d

Rob

On Sat, Nov 16, 2002 at 10:33:44PM +0100, Thomas Klausner wrote:
> Hi!
> 
> On Sat, Nov 16, 2002 at 03:31:39PM -0500, Perrin Harkins wrote:
> > >I also posted this on perlmonks:
> > >http://www.perlmonks.org/index.pl?node_id=213300
> > 
> > Ovid on Perlmonks already said some of the things I would have said 
> > about needing a presentation language.  For a templating system to be 
> > useful, it has to be able to deal with loops and conditionals.  Your 
> > system pushes those into the Perl code, splitting the presentation 
> > control into multiple places.  This fails one of my major goals: the 
> > template coders should be able to change the presentation without help 
> > from a Perl coder.  What if they decide they don't want to loop through 
> > the links, but rather just print a message saying there are some?  What 
> > if they only want to show the first two?  What if they want to display a 
> > list with commas between the elements where they didn't use a separator 
> > before?  A mature templating system can handle all of these issues 
> > without changing the main Perl module code.
> 
> That's a very good point I didn't really think about. I was thinking
> of templates as DUMB frontends that should just display
> data. Period. 
> 
> Thinking of them as tools that handle presentation logic (as Ovid
> pointed out) seems to make more sense. 
> 
> It does pay of to write RFCs before starting to code...
> 
> > >Because IMO, the main reason for using templates is to seperate code
> > >from markup.
> > 
> > It sounds to me like you want one of the HTML attribute ones, like Petal 
> > or HTML::Seamstress.  What was wrong with those?
> 
> One problem I see with HTML::Seamstress is that it uses the HTML
> attributes 'class' and 'id' for templating info. But those attributes
> are used by CSS and JavaScript and might conflict somehow. I do not
> know if you can change the behaviour of HTML::Seamstress to use other
> attributes, though. But this stopped my from looking further.
> 
> And I didn't look at Petal. For now.
>  
> > >There is one module, CGI::FastTemplate, that does seperate code from
> > >markup completly. But the way different templates are strung together
> > >seems rather comlicated to me.
> > 
> > It's not complicated, but it is somewhat confusing the first time you 
> > look at it.  I don't find your approach much easier though.
> 
> Well, I found mine easier :-)
> (But I guess this is the reason for the Template Flood on CPAN -
> everbody finds his own solution easier...)
> 
> > Isn't your fill method just doing a sort of multi-level join here?  All 
> > of the data is passed in already, so there is no reason to delay 
> > evaluation of the templates.
> 
> The main reason for doing this was to simplify testing. I planned to
> just test the plain data structures, that should be free of HTML at
> this time (before the fill). This (testing) was in fact the reason I
> started thinking about this proposal in the first place.
> 
> > More importantly, your use of AUTOLOAD to treat method calls as file 
> > names looks neat, but is not a good approach.  It's limiting (all 
> > templates in one directory!) and has potential security issues with 
> > namespace clashes.  These are all just the same method call with a 
> > single argument changed, so why not write them that way?
> 
> I planned to handle those namespace and filename issues.
>  
> > ..
> > force you to use them.  They are flexible modules.  I think you should 
> > look at them more closely before you go off on your own.
> 
> That's what I'll do. Thanks for the excellent feedback.
> 
> -- 
> #!/usr/bin/perl                                http://domm.zsi.at
> for(ref(bless[],just'another'perl'hacker)){s-:+-$"-g&&print$_.$/}

Re: RFC: Template::YetAnother

Posted by Thomas Klausner <do...@zsi.at>.
Hi!

On Sat, Nov 16, 2002 at 03:31:39PM -0500, Perrin Harkins wrote:
> >I also posted this on perlmonks:
> >http://www.perlmonks.org/index.pl?node_id=213300
> 
> Ovid on Perlmonks already said some of the things I would have said 
> about needing a presentation language.  For a templating system to be 
> useful, it has to be able to deal with loops and conditionals.  Your 
> system pushes those into the Perl code, splitting the presentation 
> control into multiple places.  This fails one of my major goals: the 
> template coders should be able to change the presentation without help 
> from a Perl coder.  What if they decide they don't want to loop through 
> the links, but rather just print a message saying there are some?  What 
> if they only want to show the first two?  What if they want to display a 
> list with commas between the elements where they didn't use a separator 
> before?  A mature templating system can handle all of these issues 
> without changing the main Perl module code.

That's a very good point I didn't really think about. I was thinking
of templates as DUMB frontends that should just display
data. Period. 

Thinking of them as tools that handle presentation logic (as Ovid
pointed out) seems to make more sense. 

It does pay of to write RFCs before starting to code...

> >Because IMO, the main reason for using templates is to seperate code
> >from markup.
> 
> It sounds to me like you want one of the HTML attribute ones, like Petal 
> or HTML::Seamstress.  What was wrong with those?

One problem I see with HTML::Seamstress is that it uses the HTML
attributes 'class' and 'id' for templating info. But those attributes
are used by CSS and JavaScript and might conflict somehow. I do not
know if you can change the behaviour of HTML::Seamstress to use other
attributes, though. But this stopped my from looking further.

And I didn't look at Petal. For now.
 
> >There is one module, CGI::FastTemplate, that does seperate code from
> >markup completly. But the way different templates are strung together
> >seems rather comlicated to me.
> 
> It's not complicated, but it is somewhat confusing the first time you 
> look at it.  I don't find your approach much easier though.

Well, I found mine easier :-)
(But I guess this is the reason for the Template Flood on CPAN -
everbody finds his own solution easier...)

> Isn't your fill method just doing a sort of multi-level join here?  All 
> of the data is passed in already, so there is no reason to delay 
> evaluation of the templates.

The main reason for doing this was to simplify testing. I planned to
just test the plain data structures, that should be free of HTML at
this time (before the fill). This (testing) was in fact the reason I
started thinking about this proposal in the first place.

> More importantly, your use of AUTOLOAD to treat method calls as file 
> names looks neat, but is not a good approach.  It's limiting (all 
> templates in one directory!) and has potential security issues with 
> namespace clashes.  These are all just the same method call with a 
> single argument changed, so why not write them that way?

I planned to handle those namespace and filename issues.
 
> ..
> force you to use them.  They are flexible modules.  I think you should 
> look at them more closely before you go off on your own.

That's what I'll do. Thanks for the excellent feedback.

-- 
#!/usr/bin/perl                                http://domm.zsi.at
for(ref(bless[],just'another'perl'hacker)){s-:+-$"-g&&print$_.$/}

Re: RFC: Template::YetAnother

Posted by Perrin Harkins <pe...@elem.com>.
Thomas Klausner wrote:
> Yes, this is a RFC for Yet Another Templating System. I know that
> there are a lot of those around. But this own might be
> different/interesting (at least I think/hope so...)
> 
> I also posted this on perlmonks:
> http://www.perlmonks.org/index.pl?node_id=213300

Ovid on Perlmonks already said some of the things I would have said 
about needing a presentation language.  For a templating system to be 
useful, it has to be able to deal with loops and conditionals.  Your 
system pushes those into the Perl code, splitting the presentation 
control into multiple places.  This fails one of my major goals: the 
template coders should be able to change the presentation without help 
from a Perl coder.  What if they decide they don't want to loop through 
the links, but rather just print a message saying there are some?  What 
if they only want to show the first two?  What if they want to display a 
list with commas between the elements where they didn't use a separator 
before?  A mature templating system can handle all of these issues 
without changing the main Perl module code.

> Because IMO, the main reason for using templates is to seperate code
> from markup.

It sounds to me like you want one of the HTML attribute ones, like Petal 
or HTML::Seamstress.  What was wrong with those?

> There is one module, CGI::FastTemplate, that does seperate code from
> markup completly. But the way different templates are strung together
> seems rather comlicated to me.

It's not complicated, but it is somewhat confusing the first time you 
look at it.  I don't find your approach much easier though.

>   # generate a new template handler
>   my $th=Template::YetAnother->new
>                 ({
>                   namespace=>'acme',
>                   template_dir=>'/projects/acme/templates/',
>                  });
>  
>   # build up a data structure
>   $data={
>          title=>$th->title('this is the title'),
>          breadcrumb=>[
>                       $th->link({url=>'/index.html',text=>'Home'}),
>                       $th->separator_breadcrumb,
>                       $th->link({url=>'/acme/index.html',text=>'Acme'}),
>                       $th->separator_breadcrumb,
>                       $th->link({url=>'/acme/bleach.html',text=>'Bleach'}),
>                      ],
>          content=>[
>                    $th->element({heading=>'SYNOPSIS',value=>'blabla'}),
>                    $th->element({heading=>'DESCRIPTION',value=>'foo bar'}),
>                   ],
>          lastmod=>scalar localtime,
>  
>         };
> 
>    # fill template & print
>    print $th->fill({data=>$data});

Isn't your fill method just doing a sort of multi-level join here?  All 
of the data is passed in already, so there is no reason to delay 
evaluation of the templates.

More importantly, your use of AUTOLOAD to treat method calls as file 
names looks neat, but is not a good approach.  It's limiting (all 
templates in one directory!) and has potential security issues with 
namespace clashes.  These are all just the same method call with a 
single argument changed, so why not write them that way?

Here's the same thing written with Template Toolkit.  You could use 
HTML::Template, CGI::FastTemplate, Text::Template, etc. here with minor 
changes:

my $th = Template->new({INCLUDE_PATH => '/projects/acme/templates/');
$th->process->('title.tmpl', {title => 'this is the title'});
$th->process->('link.tmpl', {url => '/index.html', text => 'Home'});
$th->process->('separator.tmpl');
$th->process->('link.tmpl', {url => '/index.html', text => 'Home'});
$th->process->('separator.tmpl');
$th->process->('link.tmpl', {url => '/acme/index.html', text => 'Acme'});
$th->process->('separator.tmpl');

... And so on.

Text::Template allows Perl code to be put in the template, and 
HTML::Template/TT allow loops and conditionals, but no one is going to 
force you to use them.  They are flexible modules.  I think you should 
look at them more closely before you go off on your own.

- Perrin


Re: RFC: Template::YetAnother

Posted by Thomas Klausner <do...@zsi.at>.
Hi!

On Fri, Nov 15, 2002 at 07:05:49PM -0800, Josh Chamas wrote:

> Don't do it.  If you want a stripped down version of
> ..
> evolution of these things.  The perl template user community is
> already fractured enough,

I expected that canonical answer to all Template Proposals :-)

> better to hop onto a project, hack it to your
> satisfaction ( with the blessing of the maintainers ) to get
> the configs/subclasses you need for your requirements.

I will take a closer look if my ideas could be implemented by
subclassing/extending Template Toolkit or CGI::FastTemplate.

But do you think that the concept sounds reasonable in the first
place?

-- 
#!/usr/bin/perl                                http://domm.zsi.at
for(ref(bless[],just'another'perl'hacker)){s-:+-$"-g&&print$_.$/}

Re: RFC: Template::YetAnother

Posted by Josh Chamas <jo...@chamas.com>.
Thomas Klausner wrote:
> Hi!
> 
> Yes, this is a RFC for Yet Another Templating System. I know that
> there are a lot of those around. But this own might be
> different/interesting (at least I think/hope so...)
> 
> I also posted this on perlmonks:
> http://www.perlmonks.org/index.pl?node_id=213300
> 
> So, here it is, in POD format:
> 

Don't do it.  If you want a stripped down version of
an existing templatting language, then that's great.
Work with the authors of exising frameworks like Template Toolkit
or HTML::Template that *were designed* as pure template frameworks
( as opposed to the app frameworks like Embperl, ASP, AxKit, Mason)

I'm sure you can get the authors to accept patches to disable features
that you don't want, you might even be able to create a subclass
of such a template module that sets these for the end user,
so if they just "use Template:JustVarsPlease", and Template::JustVarsPlease
is a subclass of Template or HTML::Template, then you have
your solution.

Those template solutions that are too much for you got that way
because their users wanted & needed more.  That is the natural
evolution of these things.  The perl template user community is
already fractured enough, better to hop onto a project, hack it to your
satisfaction ( with the blessing of the maintainers ) to get
the configs/subclasses you need for your requirements.

That's what I would do anyway. :)

Regards,

Josh
________________________________________________________________
Josh Chamas, Founder                   phone:925-552-0128
Chamas Enterprises Inc.                http://www.chamas.com
NodeWorks Link Checking                http://www.nodeworks.com


Re: RFC: Template::YetAnother

Posted by Perrin Harkins <pe...@elem.com>.
Thomas Klausner wrote:
> Hi!
> 
> On Sat, Nov 16, 2002 at 10:43:44AM +0100, Marcin Kasperski wrote:
> 
> 
>>One note: while talking about templating systems for generating HTML,
>>it can be inspiring to take a look at the Zope Page Templates (yes, I
>>know, this is the python/zope world, not perl...). They found the nice
>>idea of injecting loops/ifs etc into HTML in the way which does not
>>spoil HTML markup. 
> 
> 
> There is a simmilar Perl implementation of this called HTML_Tree
> http://homepage.mac.com/pauljlucas/software/html_tree/
> 
> A module based on HTML_Tree is on CPAN under the name of
> HTML-Seamstress:
> http://search.cpan.org/author/TBONE/HTML-Seamstress-1.17/Seamstress.pm

There is also an implementation that copies the Zope style pretty 
closely called Petal:
http://search.cpan.org/author/JHIVER/Petal-0.77/lib/Petal.pm

- Perrin


Re: RFC: Template::YetAnother

Posted by Thomas Klausner <do...@zsi.at>.
Hi!

On Sat, Nov 16, 2002 at 10:43:44AM +0100, Marcin Kasperski wrote:

> One note: while talking about templating systems for generating HTML,
> it can be inspiring to take a look at the Zope Page Templates (yes, I
> know, this is the python/zope world, not perl...). They found the nice
> idea of injecting loops/ifs etc into HTML in the way which does not
> spoil HTML markup. 

There is a simmilar Perl implementation of this called HTML_Tree
http://homepage.mac.com/pauljlucas/software/html_tree/

A module based on HTML_Tree is on CPAN under the name of
HTML-Seamstress:
http://search.cpan.org/author/TBONE/HTML-Seamstress-1.17/Seamstress.pm



-- 
#!/usr/bin/perl                                http://domm.zsi.at
for(ref(bless[],just'another'perl'hacker)){s-:+-$"-g&&print$_.$/}

Re: RFC: Template::YetAnother

Posted by Marcin Kasperski <Ma...@acn.waw.pl>.
Thomas Klausner <do...@zsi.at> writes:

> Yes, this is a RFC for Yet Another Templating System. I know that
> there are a lot of those around. But this own might be
> different/interesting (at least I think/hope so...)
> (...)

One note: while talking about templating systems for generating HTML,
it can be inspiring to take a look at the Zope Page Templates (yes, I
know, this is the python/zope world, not perl...). They found the nice
idea of injecting loops/ifs etc into HTML in the way which does not
spoil HTML markup. 

Their introduction is on
  http://www.zope.org/Documentation/Articles/ZPT1
and reference is in 
  http://www.zope.org/Documentation/Books/ZopeBook/current/AppendixC.stx

Here are some examples (copied from the introduction)

   <title tal:content="here/title">Page Title</title>

   The URL is <span tal:replace="request/URL">URL</span>.

   <table border="1" width="100%">
        <tr>
          <th>#</th><th>Id</th><th>Meta-Type</th><th>Title</th>
        </tr>
        <tr tal:repeat="item container/objectValues">
          <td tal:content="repeat/item/number">#</td>
          <td tal:content="item/id">Id</td>
          <td tal:content="item/meta_type">Meta-Type</td>
          <td tal:content="item/title">Title</td>
        </tr>
   </table>


-- 
( Marcin Kasperski   | Osoba jest omegalizacją ewolucji uniwersalnej na      )
( http://www.mk.w.pl | określonym odcinku etapowym (Teilhard de Chardin)     )
(----------------------------------------------------------------------------)
( Wygeneruj dokumentację: http://www.mk.w.pl/narzedzia/narzedzia_gendoc      )