ziguzagu.org

useのLISTにインポートするシンボル以外を渡す

Test::Moreではuseするときにテスト回数やら、スキップの設定なんかを渡せたりします。

use Test::More tests => 5;
use Test::More skip_all => 'skip any conditions';

とか。

このいわゆるLIST部分をどうやって内部ではさばいてるんだろう?と思って調べたのですが、もちろんuseに特別な仕組みがあるわけでもなく、importでがんばってLISTを分解してimportのシンボル以外はplan()の引数に渡す、という感じで実装されてました。

以下はTest::Moreの親クラスTest::Builder::Moduleの実装。

sub import {
    my($class) = shift;

    my $test = $class->builder;

    my $caller = caller;

    $test->exported_to($caller);

    $class->import_extra(\@_);
    my(@imports) = $class->_strip_imports(\@_);

    $test->plan(@_);

    $class->$_export_to_level(1, $class, @imports);
}

sub _strip_imports {
    my $class = shift;
    my $list  = shift;

    my @imports = ();
    my @other   = ();
    my $idx = 0;
    while( $idx <= $#{$list} ) {
        my $item = $list->[$idx];

        if( defined $item and $item eq 'import' ) {
            push @imports, @{$list->[$idx+1]};
            $idx++;
        }
        else {
            push @other, $item;
        }

        $idx++;
    }

    @$list = @other;

    return @imports;
}

+strip_imports()でuseのLISTを@importsと@otherに分離。 +@importsを戻り値に、@otherをLISTに置き換える。 +@importsをexport_to_level(の代替バージョン)でインポートする +@_をplan()に渡す。

そんな感じ。実際にはplan()はuse後に呼び出すことも出来る、にも関わらずわざわざimportにこんな実装を入れているのは…、何でだろう?やっぱりわかりやすいから?

コレが必要だったり便利なケースって他に…。

…。 …。 …。

出てこないので、あとで(いつか)考える。

LIST部分の書式はLISTであること以外に制約もないですし、デファクトな書式ってのがあるのかどうかわからないので、使う機会があったらとりあえずオレオレ実装で、、、よいのだろうか…。