http://www.perlmonks.org?node_id=635789

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

I'm gutting and rewriting a website I wrote about 7 years ago that used static CGI.pm calls to output HTML, and it got quite lengthy, quite fast (try putting a long FAQ into CGI.pm's $cgi->p($cgi->a({-href=>' ...'})); syntax all over the place, every sub gets to be hundreds of lines long, ugh).

So I started rerolling it into HTML::Template, and I'm wondering what the best approach is for the workflow. What I have looks like this:

$cgi = CGI->new(); $action = $cgi->param('a'); my %dispatch = ( home => sub { \&home(); }, donate => sub { \&donate(); }, news => sub { \&news(); }, faq => sub { \&faq(); }, dl => sub { \&download(); }, bugs => sub { \&bugs(); }, # ... and so on ); if (defined $action) { $action =~ s/[^A-Za-z0-9 ]*/home/g; } &begin_page(); $action = HTML::Entities::encode($action); $action = $cgi->param('a') && $dispatch{$cgi->param('a')} || $dispatch{'home'}; $action->(); &end_page(); sub home { my $template = HTML::Template->new( die_on_bad_params => '0', filename => 'home.tmpl'); my $content = $template->output; print $content; }

In this context, every sub prints some HTML which is pulled from a series of templates.

Here's where it gets tricky:

Since I can't put a template inside a template (or at least, when I try to do that, HTML::Template just prints the name of the second template into the body of the first template, instead of the second template's contents), I am using File::Slurp to slurp some static HTML from files into scalars, and pushing that into my template, like this:

my $template = HTML::Template->new( die_on_bad_params => '0', filename => 'news.tmpl'); my $latest_news = read_file('latest_news'); $template->param(LATEST_NEWS => $latest_news); my $content = $template->output; print $content;

Inside the template called 'news.tmpl', I have:

<TMPL_VAR NAME=LATEST_NEWS>

Basically I have "static" HTML which I want to just read in and print out, but inside the middle of that static HTML file, I need to insert dynamic bits of HTML (for example, query the database for latest news, wrap some HTML around the query results and stuff that in the middle of the template).

This gets a bit redundant and confusing fast, since each sub from my dispatch, is basically going to be doing the same thing:

sub home { my $template = HTML::Template->new( die_on_bad_params => '0', filename => 'home.tmpl'); } sub donate { my $template = HTML::Template->new( die_on_bad_params => '0', filename => 'donate.tmpl'); } sub news { my $template = HTML::Template->new( die_on_bad_params => '0', filename => 'news.tmpl'); } sub faq { my $template = HTML::Template->new( die_on_bad_params => '0', filename => 'faq.tmpl'); }

...and so on.

What I'm wondering, is... there MUST be a better, cleaner way to do this, while reducing the amount of "duplicity" here in each sub.

Ideas?

Replies are listed 'Best First'.
Re: Help with Template Workflow
by moritz (Cardinal) on Aug 29, 2007 at 13:48 UTC
    Since I can't put a template inside a template

    But you can, with <TMPL_INCLUDE>.

    As for the template names, you can build a hash:

    my %templates = ( faq => 'faq.tmpl', home => 'home.tmpl', new => 'news.tmpl', ... );

    And instead of calling a function for each page, just read the corresponding template.

Re: Help with Template Workflow
by naikonta (Curate) on Aug 29, 2007 at 13:52 UTC
    Having a dispatch table to handle multiple operations is the basic strategy. But I think you want,
    my %dispatch = ( home => \&home, donate => \&donate, news => \&news, faq => \&faq, .... );
    instead of
    my %dispatch = ( home => sub { \&home(); }, donate => sub { \&donate(); }, news => sub { \&news(); }, faq => sub { \&faq(); }, .... );
    So you can say, for example,
    my $default_action = 'home'; my $action = $cgi->param('a') || ''; # is $action recognized? my $executor = $dispatch{$action} || $dispatch{$default_action}; &$executor();
    When you specify \&some_func(), you actually mean to call some_func() and make a reference of whatever it returns. And that reference is what you get when you dispatch, for example, $dispatch{home}->(). Some thing I'm sure not what you want.
    Since I can't put a template inside a template
    What's your exact needs regarding this? If you have another html file, wheter is static or another template, you can use the <tmpl_include> command. This way, you need to process the placeholders in the included template file at the same level of the main template file. See the manpage for details.
    ... some html.... <tmpl_include name="other-template.html"> <tmpl_include name="other-static.html">
    If what you need is some kind of multi processing template then you need to work this out yourself. One way is by having a number of template objects according to the number of included templates.
    <!-- news.html --> ...some placeholders here... <!-- end of news.html --> <!-- faq.html --> ... placeholders for FAQ ... <!-- end of faq.html --> <!-- main.html --> <html> .... Today: <tmpl_var name="today"> <p><tmpl_var name="news"> <p><tmpl_var name="faq"> ... </html> <!-- end of main.html -->
    Now, you have to process the news.html and faq.html separately, getting their outputs and putting it in their respective placeholders when processing main.html. Something like (imaginatively),
    # assuming load_template() is defined somewhere # to return a template object my $faq = load_template('faq.html'); $faq->param(PARAMS_FOR_FAQ_PLACEHOLDERS); my $news = load_template('news.html'); $news->param(PARAMS_FOR_NEWS_PLACEHOLDERS); my $template = load_template('main.html'); $template->param( today => scalar(localtime), news => $news->output, faq => $faq->output, ); print $template->output;
    Having said all of that, I really like to suggest you to try CGI::Application. It would make your life easier :-) It shifts the dispatch table to some higher extend.

    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

Re: Help with Template Workflow
by f00li5h (Chaplain) on Aug 29, 2007 at 14:11 UTC

    Hopefully I'm not sounding like a you-should-use-qmail type guy...

    Template::Toolkit will allow you to use a template variable as the name of the thing to include like so

    I'd be tempted to suggest that your content should occur in the data you pass into the template (%tt_variables), rather than including your content as a template file on the disk...

    @_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;

Re: Help with Template Workflow
by rdfield (Priest) on Aug 29, 2007 at 13:39 UTC
    I don't know HTML::Template, but that kind of thing is child's play in HTML::Mason.

    rdfield