Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

RFC: Template::YetAnother

by domm (Chaplain)
on Nov 15, 2002 at 22:41 UTC ( #213300=perlmeditation: print w/ replies, xml ) Need Help??

NAME

Template::YetAnother

INTRODUCTION

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

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 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 "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:

  • 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.

  • 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 "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.

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:

Template::YetAnother

The name is just a placeholder right now, other ideas are:
  • Template::Dumper
  • Template::Stringify
  • Template::OO
  • Template::OO_Dumper
  • any other ideas?
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 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.

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

DESCRIPTION

new

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

fill

$th->fill($data);

Fill the template with the data in the data structure.

_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 "_gen"

SEE ALSO

Search for "template" on http://search.cpan.org, if you dare.
-- #!/usr/bin/perl for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}

Comment on RFC: Template::YetAnother
Select or Download Code
Re: RFC: Template::YetAnother
by Ovid (Cardinal) on Nov 15, 2002 at 23:00 UTC

    domm wrote: So, once again, code (even rather trivial) mixed with the markup.

    With all due respect, I think you are misunderstanding something. While minimizing the amount of code in a template is a Good Thing, the benefit of templates is not separating code from the presentation. It's decoupling the application logic and the presentation logic. Consider the following Template Toolkit snippet:

    [% IF people; FOREACH person = people %] <td>[% person.last_name %]</td> <td>[% person.first_name %]</td> <td>[% person.department %]</td> [% END; # FOREACH person; ELSE %] <td colspan="3" class="warning">No people found</td> [% END; # end if %]

    There's nothing wrong with that. We're not calculating overtime. We're not pulling department data from a database. In fact, we're doing nothing but controlling the presentation logic. By pushing the presentation logic back into the application, then the application needs to know how the data is to be presented. If later you need to change the presentation of the data, you're forced to change the application!

    Of course, you could get around this by assembling your data and then passing it off to another portion of the code that knows about the presentation, but that portion of code is often referred to as a "template" :)

    Cheers,
    Ovid

    New address of my CGI Course.
    Silence is Evil

      the benefit of templates is not separating code from the presentation. It's decoupling the application logic and the presentation logic.

      My problem with presentation logic vs application logic is where to draw the line.

      In your example, I would consider the warning about no people to be found to be part of the application logic. The template should just display this warning.

      In my proposed system, your example probably would look like this:

      # in the App: if ($sth->rows == 0) { $data->{'people'}=$th->people_none(); return; } my @people; while (my $r=$sth->fetchrow_hashref) { push(@people,$th->person($r); } $data->{'people'}=@people; return; # template people_none.tpl <td colspan=3 class="warning">No people found</td> # template person.tpl <tr><td>[% last_name %]</td> <td>[% first_name %]</td> <td>[% department %]</td></tr>

      By pushing the presentation logic back into the application, then the application needs to know how the data is to be presented. If later you need to change the presentation of the data, you're forced to change the application!

      The application doesn't need to know how the data is to be presented. It only needs to know what sort of data it is handling. The data then gets tagged (mis)using Perl's OO fetures (attributes would be another solution, I guess). The Templating System looks at those tags that describe data, chooses the appropriate template and fills in the data.

      One reason for starting to to think about my proposal was the problem of testing web applications. I asked Schwern about that on YAPC::EU 2002 and he said (more or less) one simple way to test Web Apps is to test the data each function/method returns before it gets passed to the template (So you do not have to parse the HTML in the test...)

      Until now I use a simple homegrown regex as a "templating system". It sucks. But none of the multitude of Templating Systems an CPAN really convinced me.

      But maybe I should take a much closer look at TT (which seems to be one of the better Templating Systems). I used TT a little bit when working on the mod_perl site and it definitly wasn't love at the first sight...

      Thanks for the feedback, anyway!

      -- #!/usr/bin/perl for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}
Re: RFC: Template::YetAnother
by Zaxo (Archbishop) on Nov 15, 2002 at 23:18 UTC

    I ++ed this for the work and thought you've put into aiming before shooting. I regret that I think you missed anyhow.

    Looking at your coded $data, that doesn't seem much different in concept from the html generation available from CGI.pm. You still have much of the content firmly embedded in perl.

    I don't see what's gained by (somewhat inaccurately) calling an iterative loop 'recursion' and shoveling it back into the program logic under a fig leaf of a template label. Similarly with decision structures.

    A template system should try to leave all the text in the template or in data files, with code logic filling in variables, selecting which data files to display, etc. The template is itself representative of some grammar, a little language for display. You correctly recognised that as code, but I think you missed by designing it back into the perl layer.

    After Compline,
    Zaxo

      Looking at your coded $data, that doesn't seem much different in concept from the html generation available from CGI.pm.

      Hmm. There is indeed some similarity. But there are two important differences:

      1. CGI.pm only does HTML-tags. My proposed system would handle anything you could write a template for.
      2. The HTML-generating functions of CGI.pm return the finished HTML-string. My proposed system would return an object that would stringify only whern requested to do so (e.g. by calling $th->fill)
      You correctly recognised that as code, but I think you missed by designing it back into the perl layer. See my reply to Ovids post above.

      I am still not sure if i missed with my idea or just didn't describe it properly...

      -- #!/usr/bin/perl for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}
Re: RFC: Template::YetAnother
by belg4mit (Prior) on Nov 15, 2002 at 23:45 UTC
      You need embedded Perl Code for stuff like includes. I do not like embedded code. That's why I didn't mentioned it...
      -- #!/usr/bin/perl for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}
        No, not really. You could read a seperate template or file into a variable to be susbtituted into the base template. Or, you could write a generic include subroutine. Better yet use the one provided
        *Q::include = \&Text::Template::_load_text;

        --
        I'm not belgian but I play one on TV.

Re: RFC: Template::YetAnother
by BrowserUk (Pope) on Nov 16, 2002 at 18:48 UTC

    I don't (yet) have strong feelings on the technical merits of your proposal, I vascilate with each argument that I read.

    On the subject of the name, I think you already said a good candidate should you decide to go ahead.

    Just::Templates

    Thats seems to sum up your intent exactly:)

    Actually, I can think of quite a few other modules that could do with being factored into the Just::* namespace.

    I think a set of small, (fast), clean modules that do one thing and do it exeptionally well would be a good antithisis to some of the modules I've looked at that take a single good idea and then wrap it up whith a gazillion unrequired or little used variations on the theme.


    Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
    Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
    Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
    Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

      *ugh* No... We don't need any more top-level names. Especially anything as arrogant as "Just". If domm *does* proceed there are two current naming conventions which would be more appropriate to follow, Template::Simple or Template::Lite (UPDATE: Template::Minimal even, though AFAIK CGI::Minimal is the only member of that set)

      --
      I'm not belgian but I play one on TV.

        Quite why you associate the word "just" with arrogance escapes me.


        Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
        Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
        Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
        Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

      CPAN namespaces are about categorization, and each level is supposed to be meaningful on its own, not just when combined with the other levels into a phrase. "Just" is not a very meaningful categorization on its own and will not help a person looking for a template module find one underneath it. To put it another way, the dominant characteristic of a simple template module is that it does templates, not that it is simple.

        Makes sense. And I withdraw the (originally somewhat lighthearted) suggestion.

        However, there are many modules that sit in one namespace, but could equally well sit in several namespaces. The obvious example is CGI.pm. Belg4mit has just:^) recently pointed out CGI::Minimal which I hadn't encountered and maybe Template::Minimal is a better choice.

        My thought was that I would appreciate a top level space where there were modules that did exactly one thing and nothing more. Within that they could be categorised in the normal way.

        Oh well. T'was j..er.. only a thought.



        Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
        Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
        Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
        Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

Re: RFC: Template::YetAnother
by Sifmole (Chaplain) on Nov 18, 2002 at 12:54 UTC
    There are two types of code (at least) that pollute nearly all Templating Systems

    You might want to ask yourself why this is; I find that invariably as a system I am working on grows I end up needing a LOOP or IF construct in the templates -- and I think the prevelance of these two constructs in templating engines perhaps shows that I am not alone. Basically what you are proposing is to simply drop functionality from an existing templating system. What does this gain you over simply not using those options? If the answer is nothing, then there is no real need for this "new" templating system.

    Of course developing for the learning experience is always an option, but I don't think CPAN needs YATS ( Yet Another Templating System ).

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://213300]
Approved by BrowserUk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (6)
As of 2014-10-26 03:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (149 votes), past polls