Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Q / A and HTML::Template techniques...

by maverick (Curate)
on Dec 20, 2001 at 05:28 UTC ( #133360=perlmeditation: print w/ replies, xml ) Need Help??

Greetings fellow monks,

At this node vladb and I got into a somewhat off topic conversation on the difficulties and techniques involved in using a template system like HTML::Template. Since this topic may be helpful to some, and may be missed because of the node depth, I've decided to post this as a meditation on the subject.

What follows is a description / case study of the technique I and several of my friends / coworkers have applied with great success. Hopefully answering vladb's questions along the way.

vladb: Did you have any difficulty integrating the templates?
We never had to go back and add templates to an existing site. These were all new creations and designed from the group up to be templated. That made the task much easier. If I had to add templating to an existing site, I would make a careful examination of the code. There are two ways you could do this. Extract the HTML, making the common parts their own templates on a script-by-script basis. Or a ground up rewrite using the existing code as a prototype if the code is extremely poor.

vladb: How did you overcome them?
If this is your first attempt at using a templating mechanism, you're naturally going to end up with two distinct layers. One for the templates and one for all of your code. I would advise taking this one step further and making 3 layers. A layer for the Presentation (the HTML), one for Logic (your code to generate the parameters for the template), and a 'glue' layer to tie them together.

The glue layer is used to abstract away all of the commonality of each page that you must generate. Most likely the flow for every single page goes like this.

  • Parse the CGI parameters.
  • Connect to the database (if you're using a database).
  • Attach to the user's session (maybe you're using cookies for persistence).
  • Open the template
  • ###Do some page specific stuff###
  • Pack the template parameters
  • Send the resulting HTML to the user
The only things that are different for any given request are which template to open, and the page specific stuff. So what I ended up developing was a mod_perl handler that did all of this common dirty work, and called another module (based on the requested URI) to do the page specific stuff. This left the other developers to only worry the logic for a given page.

This means that every page module conforms to an API that I defined for interacting with the mod_perl handler. A module to handle a page ended up looking like the following:

package foo::bar; use strict; sub handle { my $p = shift; my $parameters = $p->{'parameters'}; # here are the paramet +ers for either POST or GET my $session = $p->{'session'}; # persistent session i +nformation via [kobe://Apache::Session] my $dbh = $p->{'dbh'}; # database handle via +[kobe://Apache::DBI] # do some stuff # do some more stuff # what is returned from this is a hash reference that is the p +arameters to HTML::Template my $return; $return->{'some_template_var'} = $some_var; return $return; } 1;
Under this type of setup, the task of creation a new page from scratch has been greatly simplified. This API (including a read through of the HTML::Template docs) could be taught to a development team in an hour or two. At this point, we can now assign the coding of sections of the site to a given developer. Part of his task is to create a bare bones template that will correctly display the data from his module. Let's say we have a "register user" page. The template as designed by the coder probably looks like:
<html> <body> <form action="/yadda/yadda"> username: <input type="text" name="username"> <TMPL_IF NAME="missing_username">This is required</TMPL_IF> <br> password: <input type="password" name="password"> <TMPL_IF NAME="password_mismatch">passwords do not match</TMPL +_IF> <TMPL_IF NAME="missing_password">This is required</TMPL_IF> <br> confirm password: <input type="password" name="password_c"> <TMPL_IF NAME="missing_password_c">This is required</TMPL_IF> <br> <input type=submit value="Go"> </body> </html>
No glitz and very, very little formatting. Just enough for the developer to test the code, and just enough for the UI designer to know what template constructs he's dealing with.

Look at it from this standpoint. Unless your UI person is very sharp, handing them a list of TMPL_VAR names and TMPL_LOOP names isn't going to do any good at all. They're not going to understand it most likely, and they won't be fast enough working on it for the developers. They'll spend their time making each page look perfect (as is their job) and the developers wouldn't have ANYTHING to test their code against. Let the developers handle the "just make it work" aspect and let the UI person handle "make it look pretty".

The typical flow for including a new module into the site went like

  • perl modules is written for a particular page
  • 'functional' template is written by the developer
  • debug cycle for the developer
  • add the page list of page for the UI person to pretty up

One of which has undergone a complete UI redesign by a different designer than the original one.

vladb: I'm really interested in knowing how you structured your display data so that it could be included within a template in any way the UI guy wanted to? Say, I had seen some complex templates with quite a bit of templating code.. how much of that did you have in your site?
The thing you have to be careful of are the template loops. Everything can be moved around as long as it stays with the same TMPL_LOOP that it was originally designed for. If you have a TMPL_LOOP that contains a TMP_IF or another TMPL_LOOP, they have to maintain the same structure. This is another subtle added benefit of the developer producing a 'functional' template for the UI person. The general structure of the 'functional' template is most likely not going to be changed by the UI person. In all but the rarest cases, his/her updates to the templates don't require any alteration of the developer's code. HTML::Template has really shown itself to be that flexible.

This technique has worked for 5 public sites, 2 of which contain around 300 pages (95%) dynamic content. The most complex of those pages never got beyond things like.

<TMPL_LOOP NAME="Loop1"> <TMPL_VAR NAME="var1"> <TMPL_LOOP NAME="Loop2"> <TMPL_VAR NAME="var2"> <TMPL_IF NAME="if1"> <TMPL_VAR NAME="var3"> </TMP_IF> </TMPL_LOOP> </TMPL_LOOP>

I hope this has imparted some wisdom to my fellow monks.

I'll see y'all next week. I'm going to the 'land of no Internet' to be with my family for Christmas.

/\/\averick
perl -l -e "eval pack('h*','072796e6470272f2c5f2c5166756279636b672');"

Comment on Q / A and HTML::Template techniques...
Select or Download Code
Re: Q / A and HTML::Template techniques...
by vladb (Vicar) on Dec 20, 2001 at 07:53 UTC
    Thanks Maverick, it is becoming more clear to me now ;). To prove the point, here's an excerpt from an app I'm now building:

    File: main.cgi
    #!/usr/local/bin/perl -w use strict; use HTML::Template; ################################### ## includes ################################### use XCGI; require "config.pl"; ################################### ## MAIN ################################### my $cgi = new XCGI; # my version of CGI (with a few subs added/redefin +ed). print $cgi->header(); my $template = new HTML::Template(filename => "$CONFIG::template_root/ +main.tmpl", die_on_bad_params => 0); $template->param( select_box_datasource => $cgi->select_list(-name=>'s_dns', -values=>[keys(%CONFIG::ds_info)], -labels=>{sh=>\%CONFIG::ds_info, so +urce_key=>'info'}, -default=>['webdb'], -size=>1), ); print $template->output();

    File: templates/main.tmpl
    <html> <title>Automation Tool</title> <body> <form> <table border=1 > <tr> <td> Select Datasource: <TMPL_VAR NAME="select_box_datasource"> <br> </td> </table> </form> </body> </html>
    Note that select_box_datasource variable will hold a piece of custom built html similar to this:
    <SELECT NAME="s_dns" SIZE=1> <OPTION VALUE="foo">Production <OPTION SELECTED VALUE="foobar">Development <OPTION VALUE="bar">Development 1 </SELECT>

    the select_list() subroutine is implemented (overwritten) inside XCGI module which serves as my (custom) extension to standard CGI. Although, CGI has a similar subroutine, it didn't fit the needs of my application (as seen from the code...).


    "There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (9)
As of 2014-08-21 20:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (143 votes), past polls