TT-2.15 で list-vmethod-fallback がうまくいってなさそう

SQL::Translator がうまくインストールできないと思っていろいろ調べたら,TT-2.15 でなにやら Stash の挙動がおかしくなっているようでした。

SQL-Translator の RT
http://rt.cpan.org/Public/Bug/Display.html?id=20047
↑の TT での RT(コメントはついていない)
http://rt.cpan.org/Public/Bug/Display.html?id=20123
ちょっと違う部位だけど関連してそげな TT の RT
http://rt.cpan.org/Public/Bug/Display.html?id=19827

具体的には,「Automagic Promotion of Scalar to List for Virtual Methods」で書かれている機能がオブジェクトに対してうまく働きません。

その挙動を確かめるスクリプトを純粋化してみたのがこちら。

#!/usr/bin/perl

use strict;
use warnings;
use Template;

my $tt = Template->new();

$tt->process(\*DATA, { tokens => \&tokens })
    || die $tt->error(), "\n";

sub tokens {
    my ($type, $count) = @_;

    if ($type eq 'obj') {
        return ( ( DummyObject->new() ) x $count );
    }
    elsif ($type eq 'hash') {
        return ( ( { }                ) x $count );
    }
    else {
        return ( ( 'str'              ) x $count );
    }
}

package DummyObject;

use overload
    '""' => sub { return 'str'; },
    'eq' => sub { return 0;     },
;

sub new { bless {}, shift }

package main;

__DATA__

 1: [% tokens('str',  2).join(', ') %]
 2: [% tokens('str',  1).join(', ') %]

 3: [% tokens('hash', 2).join(', ') %]
 4: [% tokens('hash', 1).join(', ') %]

 5: [% tokens('obj',  2).join(', ') %]
 6: [% tokens('obj',  1).join(', ') %]

 7: [% FOREACH item IN tokens('obj', 1) %][% item %][% END %]

別に overload する必要もないんですが,一応してみました。で,eq も override してるのは,これを定義しておかないと Pure Perl 版 Template::Stash が line 685 の

    my $atroot  = ($root eq $self);

というところでコケてしまうんで,場当たりハックしたまでです。

結果がこちら。

 1: str, str
 2: str

 3: HASH(0x8d45e84), HASH(0x8d45e84)
 4: 

 5: obj, obj
 6: 

 7: obj

4 や 6 でも 2 と同じように単一の結果を出してほしかったのですが出力されません。FOREACH でまわした 7 ではきちんと疑似リスト化してます。

自分なりに原因を考えてみました。一例として Template::Stash の sub _dotop() で line 763 あたりから

            # failed to call object method, so try some fallbacks
            if (UNIVERSAL::isa($root, 'HASH') ) {
                if( defined($value = $root->{ $item })) {
                    return $value unless ref $value eq 'CODE';      ## RETURN
                    @result = &$value(@$args);
                }
                elsif ($value = $HASH_OPS->{ $item }) {
                    @result = &$value($root, @$args);
                }
                # さもなくばどうするの〜?
            }

このように,たとえばハッシュベースのオブジェクトで join 等のメソッドが見つからない場合,@result が undef になるんですね。このへん外枠のロジックから見てあんまりうまくないような気がします。

RT に投げるべきかなぁと思いつつ自信がない*1んで寝かせ中…

*1:仕様な気もするので…でも CDBI が引き合いにでてるしなぁ…