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

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

Long story. My team has a group of custom libraries for inserting values into an HTML template within perl. Problem is these libraries were written way back when perl4 was da bomb.

When I try and use the -w option in a cgi that uses these libraries, I run into all sorts of problems. So I went looking for CPAN modules that I could replace bits of our library with. For this particular thing, the Template Toolkit seems to perform quite well, with one exception:

The old libraries could populate template variables with the output of functions which were run when the template parser reached the variable.

So far, I have no problem with setting up the perl Template object and changing tags to fit the old html templates and doing simple replacements, but when I try and use a function, the function runs at the very beginning of the script and I see the output at the top of the page instead of at the proper point in the template. I've included a small example below:

First the template file:

<html> <head><title>{#page_title#}</title></head> <body> Here's a list of {#elements#} numbers:<br> <ul> {#show_elements#} </ul> </body> </html>

And the corresponding cgi:

#!/usr/bin/perl -w use strict; use CGI qw/:standard/; use Template; sub show_elements ($) { my $num = $_[0]; return unless( $num > 0 ); print qq(<li>$_</li>\n) for( 1 .. $num ); ''; } my $elements = 6; my $cgi = CGI->new; my $tt = Template->new( { START_TAG => quotemeta( '{#' ), END_TAG => quotemeta( '#}' ), } ); my $data = { page_title => 'Elbie Intl Numbers Inc.', elements => $elements, show_elements => \&show_elements( $elements ), }; print $cgi->header(); $tt->process( 'test.html', $data ) || die $tt->error;

So what I get is an enumerated list above the HTML headers, and SCALAR(0x82921d4) or similar where the list should go.

Granted, given enough time, I would just rewrite all the called subroutines, but for now I just need to drop something in place of my existing template functions.

Any help would be greatly appreciated.

elbieelbieelbie

Replies are listed 'Best First'.
Re: Template Toolkit, and delaying the execution of a function
by dondelelcaro (Monk) on Aug 18, 2001 at 04:26 UTC
    You might have already realized this, but the problem is in the print qq(). Template Toolkit parses the template and runs called subroutines before the data is output to STDOUT, so you will get output from those functions that are called before the data is acutally displayed.

    The solution is to adjust the functions that are being called to return the values that they would have printed instead... ala:
    $stuff_to_print .= qq(<li>$_</li>\n) for( 1 .. $num ); return $stuff_to_print;
    or similar. Unfortunatly that involves a rewrite of the code...

    You might be able to get it to work by selecting Template::Perl::PERLOUT as the standard output in the function... there is some indication that that might work as well.. but I'm not an expert in TT by any means.
      Yup, and that's exactly the problem. What I'm hoping is that there is some way to delay the execution of the subroutine, or to encapsulate the subroutine in another function that redirects the output to a variable.

      I can't seem to find the module Template::Perl anywhere though. :(

      elbieelbieelbie

        It would be cleaner to change you code to return values, but TT does provide a directive that automatically ties STDOUT for you. Try this:

        {#PERL#} $stash->get('show_elements'); {#END#}

        Or just call &show_elements in some other way from within the PERL block.

Re: Template Toolkit, and delaying the execution of a function
by echo (Pilgrim) on Aug 18, 2001 at 15:02 UTC
    I see no reason in your code to delay execution of show_elements. You can just change that sub to return a list of stuff, instead of having it output HTML items directly (which is the whole point of using templates in the fist place):
    sub show_elements ($)
    {
        my $num = $_[0];
        return [] unless( $num > 0 );
        return [ 1..$num ];
    }
    ...
    my $data = {
                 show_elements => show_elements( $elements ),
               };
    
    then in the template:
    <ul>
    {# FOREACH e = show_elements #}
      <li>{# e #}</li>
    {# END #}
    </ul>
    

    If you really need to delay the sub call, have a look at the TT docs which explain it quite adequately.

      You're not the first person to suggest changing the subroutine. I'd like to clarify that that is exactly the thing which I was hoping to avoid.

      The example I gave above was a rather simplified example. Some of the subroutines are rather long and complex, and with the current allotment of time, it's not feasible.

      Any new stuff we will write will certainly take advantage of all the fine features that the TT has to offer, but for the existing stuff, I need something quick and dirty.

      dondelecaro suggested IO::Scalar which seems promising, and I like perrin's suggestion as well. I'll try out both of those.

      elbieelbieelbie