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

While developing a moderately complex system, I came up with two techniques that can ease the pain of using HTML::Template to build pages. These techniques probably aren't original, but I didn't see them in the POD or in a quick search through the PM archives.

Wrap all calls in Eval

Since HTML::Template likes to report errors to STDERR via die(), you'll find yourself making frequent visits to your error logs. Doing something like the following will direct those errors to your browser.

my $template; eval { $template = new HTML::Template(filename => 'foo.tmpl'); }; print $@ if $@; eval { $template->param(param1 => $somevalue); $template->param(param2 => $someothervalue); }; print $@ if $@; eval { print $template->output(); };
The first eval catches typos in the template. The second eval catches both mismatches in parameter names (e.g., "payrate" vs. "pay_rate"). I forget now what the third eval catches, but it did catch something odd for me once, so I recommend it. Doing this cut my debug cycle in half.

Turning Templates into Error Logs

Instead of (or in addition to) dumping diagnostic information into a separate logfile, it's possible to turn a template into an error log, via the following technique:

In the template, switch between the error log and "normal" by embedding the following:

<TMPL_IF debug> <ul> <TMPL_LOOP debuglog> <li><TMPL_VAR item ESCAPE=HTML> </TMPL_LOOP> </ul> <TMPL_ELSE> ... the non-debug page ... </TMPL_IF>
Use this by doing
my @debuglog = (); if ( ... ) { push @debuglog, {item => "Some message"}; } ... $template->parameter(debug => 0 != @debuglog); $template->parameter(debuglog => \@debuglog);
For convenience you can do something like
sub debuglog { push @debuglog, {item => $_} for ( @_ ); }
so that you can write   debuglog("Some message") if ( ... ); The beauty of this approach is that you get a debug log if there are problems, or a normal page if there aren't. Debug cycles can be wickedly quick.

I adapted this scheme to report "normal" errors, and use it to give users a punch list of things to fix, like "Please provide a valid credit card number".

Replies are listed 'Best First'.
Re: Two tips for developing with HTML::Template
by mojotoad (Monsignor) on Jan 07, 2003 at 20:40 UTC
    I forget now what the third eval catches, but it did catch something odd for me once, so I recommend it.

    Easy there, Cowboy. You're tripping over yourself in your haste to start a cargo cult!

    How about an update detailing what, exactly, this last eval catches?

    Matt

    Update: My use of the loaded term "cargo cult" is not aimed at our esteemable dws; rather, I was using it as a humorous way to describe the hordes of followers that might use that third eval without every troubling themselves over why they were using it.

      How about an update detailing what, exactly, this last eval catches?

      If you trace out what HTML::Template::output() does, you'll see that there are a lot of die()s sprinkled about. The eval will catch any one of them.

      There's nothing cargo-cultish about catching and redirecting errors. The technique is independent of any one particular error.

        There's nothing cargo-cultish about catching and redirecting errors. The technique is independent of any one particular error.

        I see your point, but I suppose it depends on the level of analysis and the type of error. "cargo cult programming", as I understand it, is when a programmer repeatedly uses a chunk of code or a technique without ever really understanding why they are doing so.

        So on one level "catching errors" might suffice as an explanation for a programmer doing what you suggest; on a deeper level, however, that explanation might not be satisfactory if the programmer has no idea where the errors originate or why. (my broad definition would even include tried and true techniques such as using stict without ever understanding the utility or purpose of its usage.)

        Anyway -- my first comment was not meant so much as critcism as it was a colorful way to express my curiousity -- when you stated that it "caught something odd once" I was interested in hearing more information about the rare beast in question.

        Matt

        P.S. If my notion of "cargo cult" is incorrect, then please feel free to ignore my cargo cultish use of the phrase "cargo cult." ;)

Re: Two tips for developing with HTML::Template
by seattlejohn (Deacon) on Jan 08, 2003 at 03:08 UTC
    Rather than changing your coding habits to wrap all the template-related calls in eval, wouldn't it be simpler to use CGI::Carp qw(fatalsToBrowser) at the beginning of your script, or perhaps set up a $SIG{__DIE__} handler? Or is there something unusual about the way that HTML::Template dies that makes this inappropriate?

            $perlmonks{seattlejohn} = 'John Clyman';

      Wouldn't it be simpler to use CGI::Carp qw(fatalsToBrowser) at the beginning of your script, or perhaps set up a $SIG{__DIE__} handler?

      Both of those are reasonable approaches, though I found that using eval gave me a bit more control. With an eval, you can make a case-by-case decision to continue or really die. Some HTML::Template errors, particularly complaints about parameters, are safe to continue past when you're debugging.

        Ah, I see... though I would probably be inclined to zero HTML::Template's die_on_bad_params if I just wanted to continue past non-fatal errors while debugging.

        I guess as a matter of principle I tend to prefer handling the die-now-or-let-it-slip logic on a more global level rather than sprinkling the checking throughout the code. Then I can toggle it on or off globally, define different handlers for debug vs. production behavior in one place, and not have to worry about remembering anything extra when I'm coding the core page-generation logic.

                $perlmonks{seattlejohn} = 'John Clyman';

Re: Two tips for developing with HTML::Template
by bart (Canon) on Jan 08, 2003 at 09:23 UTC
    Are these three independent snippets, or does your main script actually look like that? Because the problem I'm having with it, is that here, you appear to continue onwards with the next step, even though execution of the script should have stopped here. It doesn't make any sense to fill in template variables, if loading the template file failed. I think having one huge eval block, containing all of the code, instead of three separate ones, would make more sense.

    And perhaps you should have combined these techniques, pushing $@ onto @debuglog instead of printing it out.

      Are these three independent snippets, or does your main script actually look like that?

      They're independent snippets, culled from a much larger script (and template), with non-essential parts stripped away for purposes of illustrating the two techniques.

      Perhaps you should have combined these techniques, pushing $@ onto @debuglog instead of printing it out.

      The problem with this is twofold: first, once an exception gets caught, there's no guarantee that a template will produce output. Second, the two snippets are useful at non-overlapping phases of development. Using eval helps diagnose and repair template problems; the debug log trick helps diagnose application-level errors once templates are working. Until the former problems are handled, I don't worry about the latter ones.

        That reply doesn't make me happy. Why are you filling bits and pieces into the template here and there and everywhere, instead of accumulating the relevant data in one place during a first, application logic only phase, then passing that to the templating engine in a second, display logic only phase? Your tips themselves advertise intertwining the two, so I feel fairly secure in assuming that you are doing this throughout the rest of your code. I don't like that practice at all. Even in commandline scripts I try to keep all my prints together in one place as much as possible, not sprinkle them throughout the code.**

        ** Heredocs and (ab)using @{[ foo() ]} serves nicely as a poor man's templating engine.

        Makeshifts last the longest.