Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

I am about to write my very own templating module..

by Aristotle (Chancellor)
on Sep 26, 2001 at 05:04 UTC ( [id://114714]=perlquestion: print w/replies, xml ) Need Help??

Aristotle has asked for the wisdom of the Perl Monks concerning the following question:

.. and I know this is a no-no here - much less as the first thing I post on PM. :)

However, there is a twist to this one.

Prelude

You see, I have evaluated all templating modules I could find, and I have seen all manner of interesting, cool, plain or otherwise approaches, but none of them satisfies me. It is amazing that will so many packages for doing templates out there, they all seem to fall into either of two extremes, with noone ever having walked the middle road.

The plot

They are either pure interpolators or (more or less) fullblown interpreters. Pure interpolators are modules like CGI::FastTemplate which will intentionally do absolutely nothing other than substitute variable names for values. Interpreters are modules like HTML::Template, TemplateToolkit2, HTML::Mason, AxKit and You::Name::It which will let you create templates that look more like some code source than HTML.

Mounting tension

Now I have a problem with both. Interpolators lead to a proliferation of microscopic tiny snippet templatelets whenever you try to produce tables, esp if there are different types of rows of which one gets chosen depending on some of the variables, or tables are nested, or (the Camel help me) a mix of both. Interpreters do solve this problem - but intersparsed with commands in arbitrary places and using visually complex constructs for the simple task of variable interpolation, their templates become a chore to read, much less write, not to mention that beyond a certain degree of complexity and flexibility (such as may be tempting to use with TemplateToolkit2) they go way above webdesigners' heads, putting the programmer back in the position of layout maintainer.

Climax

So what I am almost about to write is something that lies inbetween: CGI::FastTemplate-like syntax for interpolation, and SSI-like syntax for higher level constructs. On CPAN, there is nothing that meets these criteria. The Big Question is - does anyone know of an already ongoing effort for this kind of module?

Aftermath

If not, then here's a sneak peek at my overall idea: there will only be a single highlevel construct, a loop command that doubles as a conditional construct. Nested data structure interpolation will be supported by a dot operator ( "$[foo.bar]" = $templatevars->{foo}->{bar} ), with variables usable as part of variable names ( "$[foo.$bar]" = $templatevars->{foo}->{$templatevars->{bar}} - needs some thought about how exactly to disambiguate "$[foo.$bar.$baz]"). Comments?

  • Comment on I am about to write my very own templating module..

Replies are listed 'Best First'.
Re: I am about to write my very own templating module..
by perrin (Chancellor) on Sep 26, 2001 at 06:10 UTC
    NOOOOOOO!!! I go out for one drink and this is what happens?!

    First of all, have you read the article I wrote on templating systems? If not, it might give you some perspective, even though it doesn't go into great depth on each system.

    Second, I think you are being unfair to the available options, especially HTML::Template and Template Toolkit. You're critizing them for having syntax that's too complex. How do you intend to make templates that have looping and complex variables but are simpler than this?

    [% FOREACH foo %] ...some HTML... [% bar.baz %] ...some HTML... [% END %]
    If you're worried about all the other things you could do in Template Toolkit, either don't use them or use HTML::Template which intentionally limits the instruction set even further.

    The most important thing is your motivation for doing this. If you're doing it to try writing a parser and to have some fun hacking tricky regex code, then by all means go ahead. On the other hand, if you're doing this for a purely practical purpose or to contribute to CPAN, please take another look at the available tools.

      "... HTML::Template... which will let you create templates that look more like some code source than HTML." If you're saying this then I really don't think you properly evaluated this module.

      HTML::Template consists of only a few control tags (loop, if, else, unless, include). There aren't any "commands in arbitrary places" as inline perl is not allowed. It has a very simplistic control structure; to illustrate this, let's look at a sample template.

      Sample Template

      <table> <tr> <th colspan=2>%bar%</th> </tr> <loop foo> <tr> <td>Type</td> <td> <if qux> %baz% <else> %baaz% </if> </td> </tr> <loop foobar> <tr> <td colspan=2>%barfoo%</td> </tr> </loop> </loop> </table>
      Those of you familiar with HTML::Template (but not filters) will note my template doesn't look like the examples in the POD. We'll get to that in a moment.

      The above contains variable interpolation, an if-then-else statement and a nested loop structure that combine to create a table. The previous sentence is more complex than the template. And this is a good thing.

      Even to an incredibly dense web designer (oops, that's redundant ;) this template shouldn't take too long to explain using a good example. To make things even easier on them and, in so doing, ourselves -- we can add the tags to their WYSIWYG tools. It took me 30min and a visit to Macromedia's online documentation to add 'drag and drop' tags and their accompanying editor widgets to Dreamweaver.

      Template Syntax

      Now I like this syntax, but maybe you don't. I don't like the default module syntax, maybe you do. That's okay. HTML::Template allows us all to have our own way through pre-processing filters. By passing the module a filter (sub ref or array of subs refs) you can regexp or otherwise process the template before it gets passed to HTML::Template proper. In this way you can do all sorts of fun things like change the syntax to whatever you desire. Performance is not an issue in this case unless you write horrible horrible regexps.

      Perl Code

      ... # invoke our template my $t = HTML::Template->new(filename => 'foo.html', filter => \&filter +); # prepare an array of hashes for a loop my @foobar; push @foobar, { barfoo => $_ } for (1..4); # one way to set an if statements control param $t->param( qux => 1 ); # fill in our params $t->param( foo => 'Camels of the World', bar => [ { baz => 'Bactarian', baaz => 'Dromedary', foobar => \@foobar } ] ); # output it print $t->output;

      Look Ma! No HTML! None. At all. Anywhere. Wonderful no?

      Now it looks a little messy when hard coding the variables, but when you consider that you can associate the param() method with CGI's (or any other than functions like it) and that you can fetchrow_hashref (or create your own) from a DBI query...

      My example is rather trivial, but you might be suprised at the power such a simple templating system has. If you subscribe to HTML and Perl in seperation, as you seem to do, then this module is the epitome of that philosophy.

      Conclusion

      Before you go off and write another new system be sure to take a long hard look at the current systems. You'll probably find one that's close enough to your philosphy that you can either adapt it through something like filters, or maybe even take an active hand and contribute to it.

      Caveats

      I'm writing this at 3:30am. ;)

      I'm biased. I ascribe heavily to the philosophy behind HTML::Template and TT2. TT2 often gets a lot of evangalism though, so I chose to focus on HTML::Template. If I had to choose one, I think TT2 would win out; as "you get enough rope to hang yourself and half of the town with you" (mirod) whereas HTML::Template can keep you a bit to tight a leash at times. As always YMMV and CTRTFTJ (Choose the Right Tool for the Job).

      I focused mainly on philosophy and syntax. There are lots of documents dealing with speed and fine tuning when you get to that point. Often people concentrate too much on that factor when your, my, and the graphic artist's workflow matter much more (in most cases)

      See Also

        (A nod to thpfft for his reply: thanks for your points, it's good to hear about experience from like-minded people.)

        I did actually have a long look at HTML::Template. I knew that its syntax does not inline Perl and it in general is simplistic; I had even written a few templates using it as I pointed out. The default syntax is just too bulky and not visible enough in a mess of surrounding HTML.

        (This is in reply to thpfft as well:) As I kept saying - I was almost about to start on my own templating solution. I couldn't believe noone would ever have felt the same need as I for a powerful but minimalistic language. (See my initial post, ".. it's amazing ..") My look at HTML::Template wasn't enough to discover filters as a way of making it do what I want although I remember seeing passing mention of that in the docs.

        I posted here out of desperation - I didn't want to start from scratch and knew I shouldn't, but I couldn't see any way of avoiding it. Thanks for the replies everyone, your help is greatly appreciated :) Another look at HTML::Template it is. Don't worry, I'm not going to pollute CPAN with yet another templating module anytime soon :) (Or any of my code in fact. ;) Though come to think of YATM would be a good name. Hehe.)

        Sidenote: performance is not much of an issue in my case since the script is going to spit out static files. In effect it's a 'make' to be invoked via browser; I wish I could use WML, but alas, I don't even have a limited telnet login on the server, and I can't run it locally because I'm not the only one working on the site.

      First of all, thanks for that article; it was a big shortcut in evaluating at least some of the available modules. Yes, I did read it - I am subscribed to the Perl.com Newsletter so I think I got to see it very soon. :)

      HTML::Template and TT2 made it through my preevaluation. The final decision was taken mainly with respect to the fact that the script I'm writing is intended to work on a system with a barebone Perl5 and no way to install modules - as is the case with many cheap web hosts. HTML::Template only consists of a sinlge .pm - TT2 is no competition in this case.

      Well then. I embarked in working with HTML::Template. But the syntax is not well readable - <img src="<tmpl_var imgfile>"> is bulky, the tag doesn't visually stand out against a page full of template HTML, and using the brackets within a tag irks me. There is no special case to deal with newlines around the <tmpl_*> tags, so you can't put them on a line of their own if you want the output to look neat and tidy. And <tmpl_unless> can get very tiring to write dozen of times.

      The templating module I want would offer powerful interpolation syntax but not a bit more power in highlevel constructs than necessary. It seems there currently is no way to get one without the other. HTML::Template actually is the opposite of what I need, it offers relatively powerful highlevel constructs but only a very limiting interpolation syntax unless you enable globals in which case you end up with even harder to read templates.

      I should mention that the processing model I want to implement is (by the terminology of that article) a very strict pipeline architecture that allows logic in templates by cleanly separating it back into the code:

      1. fetch data, build list
      2. run filters over list to provide the template logic; variables get changed, added, deleted etc per record here
      3. interpolate data into template
      All non-trivial modules I have seen try to integrate step 2 and 3 inside the template.

      Now, you could say TT2 allows me to do it my way even if it offers more power than I want. But besides being awkward to use when you cannot install modules, to me it feels like "the tail wagging with the dog" as they say in Germany. Total overkill. I do not need a module that comes in 20-odd files incl XS, compiles my template to interpolate it, and can make coffee and bake cake to boot.

      I was seriously about to go with CGI::FastTemplate - small, simple, clean concept written in a no-installation-required single Perl-only module. Except I had written something very similar myself for a previous (unspeakbly ugly) script and have come to despise the templatelet mess it reinforces. My only extra requirement beyond interpolation is just enough intelligence in the module so I don't end up with dozens of 30 byte template files.

      Between my first reply and this one, you should have a very clear idea of what I want :) Say I wanted to modify an existing module rather than writing one from scratch. Where would you propose that I start?

        I still don't understand the "this module is too powerful for what I want" argument. Isn't Perl itself too much power for most of the tasks we use it for?

        That said you should really have a look at Text::Template. Granted its name does not include HTML, but it works really well for it anyway.

        There was an interesting discussion about it on the TT mailing list, look for the "Pros and Cons vs Text::Template" thread. TT2 advocates had the same basic argument you have: they want a "crippled" templating language. Caching seems to me a better reason to choose TT2, but as they say YMMV...

        One of the main issues with homegrown templating systems compared to popular packages available on CPAN such as TT2 or HTML::Template is the performance: most writers of homegrown systems focus on the syntax, not on performance: parsing speed, compiling to Perl, caching, all these are hard to get right. A homegrown system with a simple parser written in Perl using regexes might do a fine job but will offer poor performance under heavy traffic.
        HTML::Template and TT2 made it through my preevaluation. The final decision was taken mainly with respect to the fact that the script I'm writing is intended to work on a system with a barebone Perl5 and no way to install modules - as is the case with many cheap web hosts. HTML::Template only consists of a sinlge .pm - TT2 is no competition in this case.

        Hmmm... if you can install one module, why can't you install 20? I would expect this to be an all or nothing situation. If you can drop one module in your local directory, you can drop all of TT there.

        I embarked in working with HTML::Template. But the syntax is not well readable

        I'm not a fan of that syntax either. I definitely prefer the aesthetics of the TT syntax.

        There is no special case to deal with newlines around the <tmpl_*> tags, so you can't put them on a line of their own if you want the output to look neat and tidy.

        TT handles that, as do some of the others.

        The templating module I want would offer powerful interpolation syntax but not a bit more power in highlevel constructs than necessary.

        But no one is twisting your arm and telling you to use the full syntax of TT! Just use the part you want. If you find out you need more of it later on, it will be there for you.

        1) fetch data, build list
        2) run filters over list to provide the template logic; variables get changed, added, deleted etc per record here
        3) interpolate data into template
        All non-trivial modules I have seen try to integrate step 2 and 3 inside the template.

        Hmmm. I don't think I understand these steps. The pipeline model is all about doing all of your data munging before running the template. The template only has display logic in it, and doesn't modify any data.

        Now, you could say TT2 allows me to do it my way even if it offers more power than I want. But besides being awkward to use when you cannot install modules, to me it feels like "the tail wagging with the dog" as they say in Germany. Total overkill. I do not need a module that comes in 20-odd files incl XS, compiles my template to interpolate it, and can make coffee and bake cake to boot.

        The XS is optional, of course. TT is fairly big. However, size is only a problem when it does not fall within your required limits for disk space, RAM, speed, and ease of use. I don't use half of the capabilities that TT provides, but I love the fact that when a designer comes to me and says "I want to display these search results in two columns" I can say "Here's the TT table plugin" and not have to write it myself. I've had experiences like that over and over with TT.

        Back when I was a Perl newbie, I did write my own templating system. I actually wrote a couple of them. (In my defense, TT didn't exist then.) I started out really simple, but I ended up with a mess because people kept requesting features like columnizing lists, or slightly fancier boolean conditionals, or date formatting, etc. That's why I get so suspicious when people complain that the existing modules have too many features.

        I was seriously about to go with CGI::FastTemplate - small, simple, clean concept written in a no-installation-required single Perl-only module. Except I had written something very similar myself for a previous (unspeakbly ugly) script and have come to despise the templatelet mess it reinforces.

        Glad to know I'm not the only one who feels that way.

        Say I wanted to modify an existing module rather than writing one from scratch. Where would you propose that I start?

        The closest to what you are suggesting is Text::Templar, but I don't know how easy it would be to extend.

        the tag doesn't visually stand out against a page full of template HTML

        I write my html in lowercase and my TMPL code in upper case. Stands out a mile, e.g.
        <h3><TMPL_VAR HEADER></h3> <h4>please log in:</h4> <form action="input.pl" method="POST" name="mainform"> <input type=hidden name="Action" value=" <TMPL_VAR FUNCTION>"> user name: <input type=text width=30 name="UserName" value="<TMPL_ +VAR USER_NAME>"><BR> pass word: <input type=password width=30 name="PassWord" value="<T +MPL_VAR PASS_WORD>"><br> <TMPL_IF LOG_IN> <h4>if you don't have a user name, then you'll need to <a href="in +put.pl?Action=StartRegister">register</a><br>and if you've forgotten +your user name or pass word, <a href="mailto:<TMPL_VAR EMAIL>">email +me</a></h4> </TMPL_IF> <TMPL_IF REGISTER> security code: <input type=text width=30 name="SecCode" value="<TM +PL_VAR SEC_CODE>"><BR> if you don't know the security code, chances are you didn't get an + email from me inviting you to be here... if you'd like one, please < +a href="mailto: <TMPL_VAR EMAIL>">email me</a> </TMPL_IF>
        Having said that, I have some sympathy with your aesthetic reservations about >>! But seriously, I'd like to add my voice to those that say HTML::Template pretty much fits the spec you laid out. Certainly as I've found more and more imaginative ways of configuring the param() method, I've found there's no structure I want to use that it can't handle with ease. And the bonus is, it lets me do the complicated stuff in regular perl, not some strange module dialect.

        § George Sherston
Re: I am about to write my very own templating module..
by mirod (Canon) on Sep 26, 2001 at 10:40 UTC

    <aol mode="on">me too!</aol>

    The fact that TT2 or HTML::Template support elaborate constructs doesn't mean that you have to use them. You can perfectly decide not to use anything else than simple variable interpolation and loops. This is really the core of the Perl spirit by the way: you get enough rope to hang yourself and half of the town with you, but you can decide not to use it. Perl let you decide how to use the language, advanced templating systems let you decide how much you want to put in the template.

    I personally use Text::Template, and I mostly use interpolation and loops like foreach (@item) { $OUT .= "<td>$_</td>"; }.

    That said, if you want to improve a module so looping over complex data structures becomes easier, by all means go for it!

    Here is an example of a function for Text::Template, that will return a list as an HTML line. You could write a bunch of those functions, then have them loaded from a separate file through prepend or always_prepend.

    #!/bin/perl -w use strict; use Text::Template; @T::list= qw( foo bar baz); my $template = new Text::Template (TYPE => 'FILEHANDLE', SOURCE => \*D +ATA ); print $template->fill_in( PACKAGE => 'T'); __DATA__ {sub table_line { $OUT .= "<tr>"; foreach (@_) { $OUT .= "<td>$_</td>"; } $OUT .= "</tr>"; } } simple list:{table_line( @list);}

    This is just a (probably lame!) example, but I just want to stress that writing Yet Another Module might look glamourous, but if you want your code to be really useful (and used), modifying an existing module might be a better idea.

      I have a great deal of sympathy with Screemer on this one. Both Perrin and Mirod have valid points, but conceptually there are problems with all the current templating methods.

      I use to work for a company that helped to invent DOM and XML. We built quite a clever HTML/XML templating engine built around XPath/XPointer and DOM. Problem was the code was spagetti, and we built much of it before we submitted proposals to the W3C, so it was a bit pre-standards.

      The key thing about the templating method, and the thing I still like about it most, was that the source and template were both XML. Basically the engine traversed the template XML DOM tree, and when it found an instruction, it pulled content via XPath from the content document, or via another script.

      The beauty of this approach is that the template and source are well-formed XML/HTML that can be worked on with any respectable HTML/XML tool. The templating engine just did templating and not much else, and "smarts" are independent from both the templating engine and the souce/template files.

      I am not able to use this tool in my current role as it was built in a proprietary BASIC like language, in a server plug-in to servers other than Apache.

      I know I'm a heretic for doing this, but I built my own templating engine in Perl, using CGI and Perl. It's 600 lines of Perl, including comments and POD. It works real well. It uses as many modules as I could find to keep the bits I wrote as short and simple as possible. At the moment I'm trying to tease it to work on mod_Perl, where it will be fast enough to use in a small multi-user environment.

      I contacted various people, and got good feedback from several template authors, but their templating methods just were not what I was looing for, though TT2 came closest.

      Here is how it works.......

      • Request comes into Apache
      • Apache starts the script
      • The engine checks to see if there is a finished page in the cache, and serves that if there is one
      • The engine loads the template, and scans it
      • When it finds an instruction, if goes off and pulls that content in
      • Finally it caches the page, and sends the finishined page to the browser

      The engine understands requests to get files via HTTP, FTP, or from the local file system. When it has got a file, it can use HTTP::TreeBuilder, XML::TreeBuilder or XML::XPath to extract a given fragment.

      So if you want to call a dynamic menu for example, you simply place a call in the template to a script (any language) that makes a dynamic navigation fragment, then you parse or grab verbaitim the bit you want.

      At the moment I pass all pages through HTML-Tidy to clean them up, but this is easy to configure if you want to change it.

      To publish the finished pages, I simply use GNU WGet to suck all the pages of the server, and then FTP them to a production server for final delivery.

      This solution is not fast, it's not supposed to be, but it's faster than I thought, and is getting faster. I probably could implement it in TT2, but as mod_Perl is flakey on NT/Apache yet (Re: Re: Re: (jeffa) Re: Good place to learn Apache::PerlRun), this isn't an option for me.

      I built a little ego site using this technology, and it performed perfectly. The site is a bit lame, so apologies to monks who look at it...

      I know I have gone against the advice of more learned monks... but sometimes you have to try something to learn, even if it turns out to be a mistake!

        I use to work for a company that helped to invent DOM and XML.

        So that was your fault? :)

        The key thing about the templating method, and the thing I still like about it most, was that the source and template were both XML.

        Sounds a lot like the things that AxKit can do to me, or possibly Apache::ASP with XMLSubs.

        The beauty of this approach is that the template and source are well-formed XML/HTML that can be worked on with any respectable HTML/XML tool. If that's important to you, you could check out HTML_Tree as well.

        Request comes into Apache
        Apache starts the script
        The engine checks to see if there is a finished page in the cache, and serves that if there is one
        The engine loads the template, and scans it
        When it finds an instruction, if goes off and pulls that content in
        Finally it caches the page, and sends the finishined page to the browser

        Strictly speaking, caching pages is more of a web framework function than a templating tool one. Personally, I like to use mod_proxy for this and take some load off mod_perl. However, AxKit does have this built in.

Re: I am about to write my very own templating module..
by thpfft (Chaplain) on Sep 26, 2001 at 16:20 UTC

    way to get perrin's attention!

    My situation is very close to yours: a closet full of more or less terrible templating solutions, a strong preference for pipelining and simplicity, and a bias towards readable templates.

    I also share your concern about vast machines and tiny tasks, but I've become one of the TT faithful nevertheless. Not because I need everything it does, but because it does everything I need. More than that: it does a great deal that I might need one day. I've benefited from that headroom several times already.

    Just one example: I recently shifted a normal sort of "use DBI use Template" application over to a more elegant OO implementation using Class::DBI. Quite unexpectedly, it turned out that where I'd previously been pre-stuffing hashrefs, i could now use TT's marvellous 'lazy' method calls to do exactly the amount of database work that was required to populate a given template and no more. I didn't have to write any code for that purpose - just delete most of what i had - and i didn't even have to change the templates much.

    (actually, if you haven't already I'd urge you to check it out: it allows you to write a more or less pipelined system yet put data retrieval and computation completely at the discretion of the template)

    I suppose the point is that not only do lots of people share your present needs, they share the future needs that you don't know about yet, and they've already done the work for you...

    Which isn't to say that TT is the right solution for you. it's not an easy one to install without root. But I really don't think you should be starting from scratch here.

      I just felt I had to reply to reach closure with the first question and also first post to the Monastery I ever wrote: I ended up going the exact same route. I now swear by Template Toolkit. In fact I find it hard to stick to pipelining sometimes - but I'm getting better at drawing the line between what is application logic and should be calculated in the script and what is display logic and should be done in the template. It's a great tool, albeit not the easiest one to get started with (both in terms of installation as well as usage). At this point I can no longer imagine inhabiting a host which doesn't and doesn't want to offer it.

      Makeshifts last the longest.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://114714]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (3)
As of 2024-04-26 00:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found