It seems, there's a bug in TT which causes that an array of single object item 
could be handled differently than an array of more object items. This already 
was discussed many times, but I didn't found any satisfying solution, yet.

 

Problem description:

This could happen when a method returns an array of objects rather than a 
reference to it. If such returned array has just one item then TT assumes it 
meant a single item (rather than an array of 1 item). This is standard (and 
documented) behavior. Problem could occur if you try to call some list VMethod 
(sort, first, ...) on such array of one item. It seems it has been partially 
solved in some previous TT version (2.20?) in such way that if a list VMethod 
is called on a scalar value then the value is promoted to a list in advance of 
the list VMethod call.

 

But, if the only item of returned array is an object it seems that it is not 
promoted to a single item array as it would be expected. Instead, TT tries to 
find the VMethod among the object's methods. If it finds there a method with 
such name, then TT calls it (this is still ok and understandable). In opposite, 
if it doesn't find it there, then TT decides to treat the object as a hash (and 
that's the problem). Thus, the called list VMethod becomes hash VMethod and its 
expected function is done on the object's attributes. (Note: As my object is a 
blessed hash). Instead, I would expect TT to promote the object (as well as 
other scalars) to a list and then call a VMethod on it (of course, in case that 
such method couldn't be found among the object's methods).

 

Questions:

1) Did I understand TT behavior correctly?

2) What is the reason for such behavior? Isn't there any way in Perl to 
determine whether item an object or a hash ref is?

3) Is this already solved in some TT version or is it planned to be solved in 
some further version (3.0, ...)?

4) Is there any way how this could be worked around?

 

Hints:

1) I couldn't change methods to return array refs instead of arrays as I 
already use them in lot of other modules.

2) One possible workaround I found is to determine whether the method is called 
from perl or from TT and return array or array ref respectively. This solution 
is based on 
http://www.mail-archive.com/[email protected]/msg08524.html. But 
this solution, considering e.g. object inheritance, is vulnerable.

3) I would prefer a solution on TT side, not to modify object methods as they 
are "third party" objects/modules.

4) Objects inside the returned array are blessed hashes.

5) I have TT version 2.20.

6) Sample code to reproduce the problem follows:

 

 

------------ CODE ---------------------

 

use strict;

use warnings;

 

use Template;

 

package MyObj;

 

sub new {

      my $type      = shift;

      my %params    = @_;

      my $self      = {};

 

      $self->{name} = $params{name};

      $self->{foo}  = undef;

 

      bless $self, $type;

}

 

sub get_name {

      my $self = shift;

      return $self->{name};

}

 

#sub sort {

#     just to demonstrate that TT goes here, if the object has a method

#     named like some VMethod

#     if you uncomment this stupid work around TT will work fine

#

#     my $self = shift;

#     return $self;

#}

 

package main;

 

sub get_MyObj_list1 {

      my @array = ( MyObj->new( name => 'a' ) );

      

#     demonstration of another vulnerable workaround from Hint 2)

#     return caller =~ m/Template::/ ? [...@array] : @array;

}

 

sub get_MyObj_list2 {

      my @array = ( MyObj->new( name => 'b' ), MyObj->new( name => 'a' ) );

      

#     demonstration of another vulnerable workaround from Hint 2)      

#     return caller =~ m/Template::/ ? [...@array] : @array;  

}

 

my $template = Template->new();

$template->process(\*DATA, { get_MyObj_list1 => \&get_MyObj_list1,

                               get_MyObj_list2 => \&get_MyObj_list2 }

                               ) || print $template->error();

 

__DATA__

1 ARRAY DESCRIPTION                 TT ASSUMES IT MEANT      ARRAY CONTENT

2 -----------------                 -------------------      -------------

3 Unsorted array of 1 MyObj item  : [% get_MyObj_list1 %] -> [% FOREACH item IN 
get_MyObj_list1 %] [% item %] = [% item.name %] [% END %]

4 Unsorted array of 2 MyObj items : [% get_MyObj_list2 %] -> [% FOREACH item IN 
get_MyObj_list2 %] [% item %] = [% item.name %] [% END %]

5

6 Sorted array of 1 MyObj item    : [% get_MyObj_list1 %] -> [% FOREACH item IN 
get_MyObj_list1.sort('name') %] [% item %] = [% item.name %] [% END %]

7 Sorted array of 2 MyObj items   : [% get_MyObj_list2 %] -> [% FOREACH item IN 
get_MyObj_list2.sort('get_name') %] [% item %] = [% item.name %] [% END %]

 

------------ END OF CODE ---------------------

 

 

------------ OUTPUT -------------------

 

1 ARRAY DESCRIPTION                 TT ASSUMES IT MEANT       ARRAY CONTENT

2 -----------------                 -------------------       -------------

3 Unsorted array of 1 MyObj item  : MyObj=HASH(0x1c5fee4) ->  
MyObj=HASH(0x1c5bcfc) = a 

4 Unsorted array of 2 MyObj items : ARRAY(0x1c5bacc)      ->  
MyObj=HASH(0x1c5bc6c) = b  MyObj=HASH(0x1c5bd0c) = a 

5

6 Sorted array of 1 MyObj item    : MyObj=HASH(0x1c57bf4) ->  foo =   name =  

7 Sorted array of 2 MyObj items   : ARRAY(0x1c5bcdc)      ->  
MyObj=HASH(0x1c57b84) = a  MyObj=HASH(0x1c5bacc) = b

 

------------ END OF OUTPUT ---------------------

 

 

Note:

As this could be dangerous and not easily debugged, I would suppose to 
emphasize strongly that TT is happy with array refs but not with arrays and 
discuss problem with single object lists more visible than as a last chapter of 
VMethod documentation. (see 
http://template-toolkit.org/docs/manual/VMethods.html#section_Automagic_Promotion_of_Scalar_to_List_for_Virtual_Methods).
 Btw, the chapter name doesn't make me think of it as a danger.

 

 

Any suggestions very appreciated. Thanks a lot in advance!

Kind regards,

Petr DanihlĂ­k

 

_______________________________________________
templates mailing list
[email protected]
http://mail.template-toolkit.org/mailman/listinfo/templates

Reply via email to