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

Hi, I use Class::DBI together with Template Toolkit. I have a method in one of my classes like this:
sub auxdata { my $self = shift; if (wantarray) { # returns a list-ref return [ $self->auxclass->retrieve_all() ]; } else { # returns an interator return $self->auxclass->retrieve_all(); } }
I want to be able to use both Class::DBI::Iterator and lists in TT, but I can only get lists to work:
[% FOREACH aux = object.auxdata %] <option value="[% aux.id %]">[% aux.name %]</option> [% END %]
This doesn't work:
[% WHILE ax = a.auxdata.next %] ID [% ax.id %] [% END %]
If I add print statements to my auxdata method I see it is called in list-context every time. What am I doing wrong? What is the correct way of getting this to work?

Replies are listed 'Best First'.
Re: Template Toolkit scalar vs list context
by Errto (Vicar) on Dec 11, 2004 at 03:47 UTC

    First of all, the brackets are unnecessary in list context. If you wish to return a list to pass to the FOREACH directive, then you should return the value of retrieve_all() directly as a list, instead of wrapping it in an array reference and passing that as a list of length one. Nonetheless when I did a test it seemed to work the way you have it.

    Second, the WHILE syntax is wrong. What you want is more like this:

    [% iter = a.auxdata.scalar %] [% ax = iter.next %] [% WHILE ax %] ID [% ax.id %] [% ax = iter.next %] [% END %]

    The reason is two-fold. First of all, in the Template Toolkit, an assignment is not an expression and you can't use it as a test. Second, even if you could, the way you wrote it it would fetch a fresh iterator every time it went through the loop, which would be bad. So you need to fetch the iterator once and then iterate through it with a separate variable.

    Third, the default behavior in Template Toolkit is to use list context. You need to use the Template::Stash::Context class to get around this. First you need to initialize your template properly:

    my $stash = Template::Stash::Context->new(); my $tt = Template->new({STASH => $stash });

    and then you need to call the explicit pseudomethod "scalar" in the template itself, as I showed above.

    By the way, if you really do need to support both methods, a better way than using context might be to have a parameter in your "auxdata" method.