• このエントリーをはてなブックマークに追加

はじめに

MouseX::Types + Test::Deep による型定義を,ちょこっとだけラクに書くための一手法,というかメモ.

MouseX::Types + Test::Deep による型定義のサンプル

hirobanex さんのこちらのエントリ:

における「MouseX::Types::Mouseの思い出」という節で触れられている方法を用いると,複雑めなデータ構造なんかをひとつの「型」としてまとめられて,テストの際のデータの比較なんかをスマートに行えます.

こんな感じで型を定義して:

package My::Types;
use strict;
use My::OtherTypes map { ($_, "is_$_") } qw/HogeFuga/;
use MouseX::Types::Mouse map { ($_, "is_$_") } qw/Str Int HashRef/;
use Test::Deep;
 
subtype Foobar,
    as HashRef,
    where {
        eq_deeply($_, {
            str  => code(\&is_Str),
            int  => code(\&is_Int),
            list => array_each({
                a => code(\&is_HashRef),
                b => code(\&is_Str),
            }),
            hogefuga => code(\&is_HogeFuga),
        });
    };
 
1;

こんな感じでテスト:

use strict;
use My::Types qw/is_Foobar/;
use Test::More;
 
my %data = (...);
ok is_Foobar(\%data), '\%data is-a Foobar';
 
done_testing;

D. R. Y.

ただ,先の型定義のコード,code(\&is_{type}) みたいなのが頻出して,あまりよろしくない感じです.

そこで,「この値はこの型ですよー」と宣言する感じの次のような関数を定義してみます:

sub is { code( \&{"is_@{[ (split /::/, $_[0])[-1] ]}"} ) }

この関数を使うと,先のコードはこんな感じになります:

package My::Types;
use strict;
use My::OtherTypes map { ($_, "is_$_") } qw/HogeFuga/;
use MouseX::Types::Mouse map { ($_, "is_$_") } qw/Str Int HashRef/;
use Test::Deep;
 
sub is { code( \&{"is_@{[ (split /::/, $_[0])[-1] ]}"} ) }
 
subtype Foobar,
    as HashRef,
    where {
        eq_deeply($_, {
            str  => is(Str),
            int  => is(Int),
            list => array_each({
                a => is(HashRef),
                b => is(Str),
            }),
            hogefuga => is(HogeFuga),
        });
    };
 
1;

ちょっとすっきりしたかも.

split /::/, ... とかしている理由

use MouseX::Types::Mouse qw/Int/;
use My::OtherTypes qw/HogeFuga/;
 
print scalar(Int);       # 'Int'
print scalar(HogeFuga);  # 'My::OtherTypes::HogeFuga'

これが理由です.

なので,MouseX::Types で独自定義した別のクラスを利用しないのであれば,先の is 関数は次のようで無問題です:

sub is { code( \&{"is_$_[0]"} ) }

おわりに

以上,MouseX::Types + Test::Deep による型定義に頻出するだろう記述を単純にすることで,少しだけラクするためのメモでした.