> The problem is with TT automatically deciding what's an array and what's a
> scalar. CGI.pm returns a single array element when you have a single
> checkbox in a group checked, and two array elements when there are two
> boxes checked. TT will try to do the right thing and create a scalar in
> the first case, but a list in the second. I've no way to force a list
> context in TT for lvalue. The code will talk better than I. This code
> tries to show that Perl does the right thing, whereas TT doesn't:

Yes, this is a problem.  There are two seperate issues:

 1) TT2 always calls plugin functions in array context.
 2) Single element arrays get demoted to scalars.

Your specific problem relates to (2) more than (1).  To solve (1) it
might be nice if there was some syntax that allowed you to specify
whether plugins are called in scalar or array context.

The scalar demotion would be less of a problem if most of the scalar
and array ops correctly worked with both scalar and array arguments.
For example, I should be able to push a scalar to get a two element
array, and I should be able to join a scalar as if it was a one element
array.  (I think this would be a pretty easy addition to Stash.pm.
Also, it would be nice if the array ops worked on Iterators, but
that's another topic.)

Here is a workaround.  You can add two new ops "scalar" and "array"
that convert between scalar and array types.  "array" promotes a
scalar to a one element array, and is a no-op on an array.  Conversely,
"scalar" takes the first element of an array and is a no-op on a
scalar.

Here's your example again.  cgi.param is still always called in array
context, but the two TT2 cases are now closer analogs of the perl code
and produce the same output:

    #!/bin/perl

    use CGI;
    use Template;
    my $cgi = CGI->new('foo=5&foo=6&bar=7');

    $Template::Stash::SCALAR_OPS = {
        'array'  => sub { ref $_[0] eq "ARRAY" ? $_[0] : [$_[0]] },
        'scalar' => sub { ref $_[0] eq "ARRAY" ? $_[0][0] : $_[0] },
    };

    $Template::Stash::LIST_OPS = {
        'scalar' => sub { ref $_[0] eq "ARRAY" ? $_[0][0] : $_[0] },
        'array'  => sub { ref $_[0] eq "ARRAY" ? $_[0] : [$_[0]] },
    };

    # put everything in a scalar context
    for ($cgi->param) {
      print "$_ => ".$cgi->param($_)."\n";
    }

    # put everything in the list context
    for ($cgi->param) {
      my @values = $cgi->param($_);
      print "$_ => ".join(",",@values)."\n";
    }
    print "---------------\n";

    Template->new->process(\*DATA,{ cgi => $cgi});

    __DATA__

    [% # scalar context -%]
    [% FOR p = cgi.param -%]
    [% p %] => [% cgi.param(p).scalar %]
    [% END -%]

    [% # list context -%]
    [% FOR p = cgi.param -%]
    [% p %] => [% cgi.param(p).array.join(',') %]
    [% END -%]

and the output is:

    foo => 5
    bar => 7
    foo => 5,6
    bar => 7
    ---------------

    foo => 5
    bar => 7

    foo => 5,6
    bar => 7

Craig


Reply via email to