views:

151

answers:

2

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)

+4  A: 

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];
    },
);
Timmy
+3  A: 

It's not quite what you're after, but could you alter the TT plugin to handle both lists and single items?

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

  my %h;

  if (ref $o eq 'ARRAY') {
    foreach my $item ( @{$o} ) {
      unless ( exists $h{ $item->id } ) {
        $h{ $item->id } = 1;
      }
    }
  }
  else {
      return ($o->id);
  }

  return keys %h;
}
mopoke