1

I'm trying to use perl Text::Template for short templates and so far failed to get it to iterate over an array.

Here is a short test program I wrote to demonstrate what I'm trying to do:

#!/usr/bin/perl
use Text::Template;
my $template = Text::Template->new(TYPE => 'STRING', SOURCE => <<'__EOT__');
array[0]: { $array[0] }
{ foreach my $i (@array) { }
 {$i}
}
__EOT__
print $template->fill_in(HASH => { array => [qw(item1 item2)]});

According to the Text::Template manual I expected this to print: array[0]: item1 item1 item2 But instead it prints array[0]: item1

(i.e. the output of the first line outside the loop and an empty line).

I couldn't find anywhere on the web any example of someone actually using a loop inside a template, though the documentation says it should "just work".

What am I missing?

apaderno
  • 28,547
  • 16
  • 75
  • 90
Capt. Crunch
  • 4,490
  • 6
  • 32
  • 38

2 Answers2

1
my $template = Text::Template->new(TYPE => 'STRING', SOURCE => <<'__EOT__',  DELIMITERS => [qw(<% %>)],);

Pick different delimiters. The documentation advises you to do so several times for various reasons, mostly for being easier to work with because Perl code also uses {} braces. It also says:

Because the parsing of templates is simplified by the absence of backslash escapes, using alternative DELIMITERS may speed up the parsing process by 20-25%. This shows that my original choice of { and } was very bad.

Just {$i} does not work here because it is in void context. The documentation says:

The result of the last statement executed will be evaluted in scalar context; the result of this statement is a string, which is interpolated into the template in place of the program fragment itself.

Rewrite it with the $OUT variable:

<% foreach my $i (@array) {
    $OUT .= $i
} %>

The documentation says:

Anything you append to this variable will appear in the output of the template. Also, if you use $OUT in a program fragment, the normal behavior, of replacing the fragment with its return value, is disabled; instead the fragment is replaced with the value of $OUT.

<% $OUT .= $_ for @array %>

Same result, but shorter.

daxim
  • 39,270
  • 4
  • 65
  • 132
  • 1
    Thanks to both of you for the answers. I'm already aware of $OUT and the option for different delimiters but the problem is that this isn't just a single "$i" that I want to repeat in the loop. I actually want to interpolate it inside about 5 lines of text and possibly more variables. This part of the template should actually be: server {$server_ip} { address = {$server_ip} active = 1 weight = {$weight} } (it's part of [lvs.cf](http://linux.die.net/man/5/lvs.cf)). Putting all these lines in one "$OUT .=" sort of defeats the purpose of a template. (to be continued..) – Capt. Crunch May 07 '11 at 10:24
  • 1
    (..continued). When trying the different delimiters I got the following errors: array[0]: item1 Program fragment delivered error ``Missing right curly or square bracket at template line 2, at end of line syntax error at template line 2, at EOF'' Program fragment delivered error ``Unmatched right curly bracket at template line 4, at end of line syntax error at template line 4, near "; #line 4 template }"'' So it looks like it won't allow me to break the "for" body to multiple fregmants as I expected from the manual. – Capt. Crunch May 07 '11 at 10:30
0

A couple of experiments indicate that this:

{ stuff }

Is turned into (effectively) something like this pseudo-perl:

my $x = eval(stuff);
$template =~ s/{ stuff }/$x/;

So the "stuff" needs to be an expression so that it returns something to put into the template. Your "stuff" is a foreach loop which doesn't have a value so your template doesn't do anything interesting.

If you look at the tests for Text::Template (always go to the test suite for examples, the test suites for CPAN packages are invaluable for learning how things work), you'll see things like this:

{ $t = ''; foreach $n (1 .. 20) { $t .= $n . ' ' } $t }

Note the way $t is being used. That indicates that you want something more like this for your template:

array[0]: { $array[0] }
{ $t = ''; foreach my $i (@array) { $t .= "\t$i\n" } }

There's also the $OUT special variable which can take the place of $t above. The documentation for CPAN packages is generally pretty good and well worth reading, you'll miss it when you work in other languages.

mu is too short
  • 426,620
  • 70
  • 833
  • 800