Search code examples
perltemplate-toolkitrose-db-object

How can I force list context in Template Toolkit with RDBO?


I have a TT plugin that does the trivial unique ids:

sub get_unique_uid_tt {
  my ( $classname, $o ) = @_;

  my %h;

  foreach my $item ( @{$o} ) {
    unless ( exists $h{ $item->id } ) {
      $h{ $item->id } = 1;
    }
  }

  return keys %h;
}

where the template call is simply:

[% Namespace.get_unique_uid_tt( data.users ) %]

and "data" is an RDB Object, users being one of its relationships. I have verified that the ".users" returns a list in Perl directly, whether the relationship has one or many elements.

However, it appears that TT returns the element for single-element lists, while properly returning lists for multiple element.

I looked this up and found that you can force list context with ".list":

[% Namespace.get_unique_uid_tt( data.users.list ) %]

This does not work as intended for single-element lists, as a Data::Dumper revealed:

$VAR1 = [
      {
        'value' => 1,
        'key' => '__xrdbopriv_in_db'
      },
      {
        'value' => bless(
         ... snip ...
         ),
        'key' => 'db'
      },
      {
        'value' => '1',
        'key' => 'id'
      }
];

instead of the expected

 $VAR1 = [
     bless( {
             '__xrdbopriv_in_db' => 1,
             'id' => '1',
             'db' => ... snip ...
     }, 'DataClass' )
 ];

Is there any other simple way in TT to get a list of objects, even on single-element lists? (One approach is to rewrite the function, but one that does not would be preferable)


Solution

  • Found this on the TT mailing list:

    http://lists.template-toolkit.org/pipermail/templates/2009-December/011061.html

    seems like TT's ".list" has trouble converting objects to lists in general, not just RDBOs.

    The suggestion is make a vmethod:

    $Template::Stash::LIST_OPS->{ as_list } = sub {
       return ref( $_[0] ) eq 'ARRAY' ? shift : [shift];
    };
    

    I added this to my context object (same idea):

    $context->define_vmethod(
        'list',
        'as_list',
        sub {
            return ref( $_[0] ) eq 'ARRAY' ? shift : [shift];
        },
    );