愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

Sub::Spyとか作った、けどナントカの夜は明けない

TestのmockやらVerificationやらの話を聴いて、そういやサブルーチンリファレンスを一回くるんで引数/返り値/投げられた例外を記録してくれるサブルーチンリファレンスもどきを、Sinon.JS (http://sinonjs.org/) のspyみたいな感じで実装できないかなあ、と思って。

作った。

http://github.com/zentooo/p5-sub-spy

Perlだと使いどころが微妙だけど、サブルーチンリファレンスを渡す関数のテストやら、非同期にサブルーチンリファレンスが呼ばれるような場合のテストやらにちょっと便利なような気もする。


こういう感じで使う。

    use Sub::Spy;

    my $subref1 = sub { return shift * 2; };
    my $spy1 = Sub::Spy->new($subref1);

    $spy1->(1);
    $spy1->(2);

    print $spy1->called; # 1 ( true )
    print $spy1->called_twice; # 1 ( true )

    print $spy1->args->[0]->[0]; # 1
    print $spy1->return_values->[0]; # 2

    my $call1 = $spy1->get_call(0);
    print $call1->return_value; # 2

    my $call2 = $spy2->get_call(1);
    print $call2->return_value; # 4


    my $subref2 = sub { die "die!"; };
    my $spy2 = Sub::Spy->new($subref2);

    $spy2->();

    print $spy2->args->[0]->[0]; # 1
    print $spy2->exceptions->[0]; # "die" in ...
    print $spy2->threw; # 1 (true)


callableなobjectは、&{}をoverloadすることで出来るっぽい。

http://d.hatena.ne.jp/tokuhirom/20090714/1247539000


でもこれだけで「僕はサブルーチンリファレンスです!」って誤魔化し切るのには無理があって、サブルーチンリファレンスを受け取る側のコードで大抵しているだろう ref $spy eq "CODE" みたいなチェックは見事に通らない。

CORE::GLOBAL::refを書き換えるのも何だかイヤだし、どうしたもんやら。



10/15追記

じーえふえっくすさんに、

http://d.hatena.ne.jp/gfx/20111015/1318640600

というアドバイスをもらった。InsideOut法とか何かの本で読んで完全に忘れていた。

autobox使うのはなんかアレだし、特にオブジェクト指向インタフェースに何が何でもというこだわりがあるわけでもないので、InsideOut法で書き換えるか、な。