【Moose】アトリビュートの設定を拡張する


MooseX::Getoptを使ってCUIアプリを作るとき、ロールに含まれるアトリビュートがコマンドラインオプションと化してしまうので困っていた。例えばこんな感じ。

ソースその1

#!/usr/bin/perl
{
    package MyApp::Role;
    use Moose::Role;
    has attr1 => ( is => "ro", default => 5 );
    1;
}

{
    package MyApp::CUI;
    use Moose;
    with qw!
        MyApp::Role
        MooseX::Getopt
    !;
    has opt1 => ( is => "ro", required => 1 );

    sub run { my $self = shift;
        print "attr1 => " . $self->attr1 . "\n";
        print "opt1  => " . $self->opt1 . "\n";
    }

    __PACKAGE__->meta->make_immutable;
}

MyApp::CUI->new_with_options->run;

実行結果

$ perl test.pl
Required option missing: opt1
usage: test.pl [long options...]
        --opt1
        --attr1

オプションにしたいのはopt1だけで、attr1はユーザーに見せたくない。一応、こういう時のために使うメタクラスが用意されている1

ソースその2

#!/usr/bin/perl
{
    package MyApp::Role;
    use Moose::Role;
    with "MooseX::Getopt";
    has attr1 => ( metaclass => "NoGetopt", is => "ro", default => 5 );
    1;
}

{
    package MyApp::CUI;
    use Moose;
    with qw!
        MyApp::Role
        MooseX::Getopt
    !;
    has opt1 => ( is => "ro", required => 1 );

    sub run { my $self = shift;
        print "attr1 => " . $self->attr1 . "\n";
        print "opt1  => " . $self->opt1 . "\n";
    }

    __PACKAGE__->meta->make_immutable;
}

MyApp::CUI->new_with_options->run;

実行結果

$ perl test.pl
Required option missing: opt1
usage: test.pl [long options...]
        --opt1

attr1は見えなくなった。でも、MyApp::RoleをCUIアプリ以外から利用する時を考えたらどうだろう? 必要もないのにMooseX::Getoptをwithしているのは無駄だ。attr1アトリビュートはCUIアプリで使うときだけ「NoGetopt」になってくれればいいのだ。

アトリビュートに「+」する

で、やっと本題。これをスマートに解決してくれる方法があった。あったというか、単に僕が勉強不足だっただけなのだが。

has "+$attribute_name" => %option;

継承したスーパークラスや消費したロールに存在するアトリビュートの設定を拡張します。
http://perl-mongers.org/2010/02/the-fastest-way-to-mastering-moose-and-mouse.html

ソースその3

#!/usr/bin/perl
{
    package MyApp::Role;
    use Moose::Role;
    has attr1 => ( is => "ro", default => 5 );
    1;
}

{
    package MyApp::CUI;
    use Moose;
    with qw!
        MyApp::Role
        MooseX::Getopt
    !;
    has opt1 => ( is => "ro", required => 1 );
    has "+attr1" => ( metaclass => "NoGetopt" );

    sub run { my $self = shift;
        print "attr1 => " . $self->attr1 . "\n";
        print "opt1  => " . $self->opt1 . "\n";
    }

    __PACKAGE__->meta->make_immutable;
}

MyApp::CUI->new_with_options->run;

実行結果

$ perl test.pl --attr1 a
Unknown option: attr1
usage: test.pl [long options...]
        --opt1

このように、MyApp::Roleを利用するアプリケーション(MyApp::CUI)の中で、コマンドラインオプションにしたくないアトリビュートを指定している。こっちの方がずっとスマートだね。


  1. 他に、アトリビュートの名称に「_」を付けるという手もある。今回の例で言えば、「_attr1」というアトリビュートなら、コマンドラインオプションにならない。 

コメントを残す