Brad Baxter
2009-12-10 18:29:03 UTC
Hi all,
The other day, one of my templates started doing something mysterious.
I couldn't figure it out for a good while. After a night's sleep, the
explanation occurred to me, and I thought I'd share it, in case it
helps someone else.
Below is some code that demonstrates what was mysterious.
The first subroutine, var_test() shows that you can set a TMPL_VAR from
outside a TMPL_LOOP, as long as you include global_vars=>1. This works
even if the TMPL_VAR does not appear elsewhere in the template outside
the loop.
The second subroutine, loop_test1() shows that you can also set a
TMPL_LOOP from outside another TMPL_LOOP, as long as you again include
global_vars=>1, but--and here's the mystery--only as long as the one
TMPL_LOOP *also* appears in the template somewhere *outside* the other
loop.
The third subroutine, loop_test2() is just like loop_test1(), except
that the trees TMPL_LOOP *only* appears *inside* the forest loop.
Because of this (apparently), the values in the trees loop are not set
by H::T->param().
So the mystery was that my TMPL_VAR (global) settings were showing up
inside my loop (even when they didn't appear anywhere else in the
template), but my TMPL_LOOP ("global") settings where disappearing from
inside my loop when I removed them from elsewhere in the template.
Very much head scratching occurred.
This is an obscure problem, and reorganizing the template and the code
was dead simple. But I just had to track down the explanation so I
would know what to avoid next time.
The use case where this came up was a spreadsheet report with subtotals
and grand totals. I was using the labels from the grand totals loop
for each of the sub-groups. When I wanted to use the same code to
generate a report for just one sub-group, without grand totals, I
removed the grand totals loop from the bottom of the template, and all
of a sudden the labels for the one sub-group just disappeared.
Regards,
Brad
#!/usr/local/bin/perl
use strict;
use warnings;
use HTML::Template;
var_test(); # [maple trees, rotten logs]
loop_test1(); # [maple trees, rotten logs] {maple trees}
loop_test2(); # [ trees, rotten logs]
#---------------------------------------------------------------------
# want to use short-hand tags TV => TMPL_VAR, TL => TMPL_LOOP
sub filter {
for( ${$_[0]} ) {
s| <TV (.*?)> |<TMPL_VAR $1>|gx;
s| <TL (.*?)> |<TMPL_LOOP $1>|gx;
s| </TL> |</TMPL_LOOP>|gx;
}
}
#---------------------------------------------------------------------
sub var_test {
my $text =
"[<TL forest><TV trees> trees, <TV logs> logs</TL>]\n";
my $sref = \$text;
my $tmpl = HTML::Template->new(
scalarref => $sref,
global_vars => 1,
die_on_bad_params => 0, # not strictly needed here
filter => \&filter,
);
# trees var is set "outside" of forest, so needs global_vars=>1
$tmpl->param( trees => 'maple' );
$tmpl->param( forest => [{ logs => 'rotten' }] );
print $tmpl->output;
}
#---------------------------------------------------------------------
sub loop_test1 {
my $text = join '' =>
"[<TL forest><TL trees><TV type></TL> trees, <TV logs> logs</TL>] ",
"{<TL trees><TV type></TL> trees}\n";
my $sref = \$text;
my $tmpl = HTML::Template->new(
scalarref => $sref,
global_vars => 1,
die_on_bad_params => 0, # not strictly needed here
filter => \&filter,
);
# trees loop is set "outside" of forest, so needs global_vars=>1
$tmpl->param( trees => [{ type => 'maple' }] );
$tmpl->param( forest => [{ logs => 'rotten' }] );
print $tmpl->output;
}
#---------------------------------------------------------------------
sub loop_test2 {
my $text =
"[<TL forest><TL trees><TV type></TL> trees, <TV logs> logs</TL>]\n";
my $sref = \$text;
my $tmpl = HTML::Template->new(
scalarref => $sref,
global_vars => 1,
die_on_bad_params => 0, # definitely needed here
filter => \&filter,
);
# trees loop is set "outside" of forest, so needs global_vars=>1
# but also needs die_on_bad_param=>0 to avoid error
$tmpl->param( trees => [{ type => 'maple' }] );
$tmpl->param( forest => [{ logs => 'rotten' }] );
print $tmpl->output;
}
__END__
The other day, one of my templates started doing something mysterious.
I couldn't figure it out for a good while. After a night's sleep, the
explanation occurred to me, and I thought I'd share it, in case it
helps someone else.
Below is some code that demonstrates what was mysterious.
The first subroutine, var_test() shows that you can set a TMPL_VAR from
outside a TMPL_LOOP, as long as you include global_vars=>1. This works
even if the TMPL_VAR does not appear elsewhere in the template outside
the loop.
The second subroutine, loop_test1() shows that you can also set a
TMPL_LOOP from outside another TMPL_LOOP, as long as you again include
global_vars=>1, but--and here's the mystery--only as long as the one
TMPL_LOOP *also* appears in the template somewhere *outside* the other
loop.
The third subroutine, loop_test2() is just like loop_test1(), except
that the trees TMPL_LOOP *only* appears *inside* the forest loop.
Because of this (apparently), the values in the trees loop are not set
by H::T->param().
So the mystery was that my TMPL_VAR (global) settings were showing up
inside my loop (even when they didn't appear anywhere else in the
template), but my TMPL_LOOP ("global") settings where disappearing from
inside my loop when I removed them from elsewhere in the template.
Very much head scratching occurred.
This is an obscure problem, and reorganizing the template and the code
was dead simple. But I just had to track down the explanation so I
would know what to avoid next time.
The use case where this came up was a spreadsheet report with subtotals
and grand totals. I was using the labels from the grand totals loop
for each of the sub-groups. When I wanted to use the same code to
generate a report for just one sub-group, without grand totals, I
removed the grand totals loop from the bottom of the template, and all
of a sudden the labels for the one sub-group just disappeared.
Regards,
Brad
#!/usr/local/bin/perl
use strict;
use warnings;
use HTML::Template;
var_test(); # [maple trees, rotten logs]
loop_test1(); # [maple trees, rotten logs] {maple trees}
loop_test2(); # [ trees, rotten logs]
#---------------------------------------------------------------------
# want to use short-hand tags TV => TMPL_VAR, TL => TMPL_LOOP
sub filter {
for( ${$_[0]} ) {
s| <TV (.*?)> |<TMPL_VAR $1>|gx;
s| <TL (.*?)> |<TMPL_LOOP $1>|gx;
s| </TL> |</TMPL_LOOP>|gx;
}
}
#---------------------------------------------------------------------
sub var_test {
my $text =
"[<TL forest><TV trees> trees, <TV logs> logs</TL>]\n";
my $sref = \$text;
my $tmpl = HTML::Template->new(
scalarref => $sref,
global_vars => 1,
die_on_bad_params => 0, # not strictly needed here
filter => \&filter,
);
# trees var is set "outside" of forest, so needs global_vars=>1
$tmpl->param( trees => 'maple' );
$tmpl->param( forest => [{ logs => 'rotten' }] );
print $tmpl->output;
}
#---------------------------------------------------------------------
sub loop_test1 {
my $text = join '' =>
"[<TL forest><TL trees><TV type></TL> trees, <TV logs> logs</TL>] ",
"{<TL trees><TV type></TL> trees}\n";
my $sref = \$text;
my $tmpl = HTML::Template->new(
scalarref => $sref,
global_vars => 1,
die_on_bad_params => 0, # not strictly needed here
filter => \&filter,
);
# trees loop is set "outside" of forest, so needs global_vars=>1
$tmpl->param( trees => [{ type => 'maple' }] );
$tmpl->param( forest => [{ logs => 'rotten' }] );
print $tmpl->output;
}
#---------------------------------------------------------------------
sub loop_test2 {
my $text =
"[<TL forest><TL trees><TV type></TL> trees, <TV logs> logs</TL>]\n";
my $sref = \$text;
my $tmpl = HTML::Template->new(
scalarref => $sref,
global_vars => 1,
die_on_bad_params => 0, # definitely needed here
filter => \&filter,
);
# trees loop is set "outside" of forest, so needs global_vars=>1
# but also needs die_on_bad_param=>0 to avoid error
$tmpl->param( trees => [{ type => 'maple' }] );
$tmpl->param( forest => [{ logs => 'rotten' }] );
print $tmpl->output;
}
__END__