App::RadというCLIがだいぶ良い

App::Rad というコマンドラインツールがあるのですが、
手軽にサブコマンド作れてだいぶウマーな感じなのに
日本語情報が全くないので紹介してみます。



基本系

まずは「rad.pl」に「bucho」というサブコマンドを作ってみたいと思います。

use App::Rad;
App::Rad->run;

sub bucho {
     return "Hello Bucho!";
}

たったこれだけです。まずは実行してみましょう。

$ perl rad.pl
Usage: rad.pl command [arguments]

Available Commands:
    bucho    
    help      show syntax and available commands

いい感じにヘルプが出ました。


定義したサブルーチン「bucho」が勝手にサブコマンドとして認識されていますね。
buchoコマンドを実行してみましょう。

$ perl rad.pl bucho
Hello Bucho!

実行されました。簡単!

help

buchoコマンドもヘルプに出したいですよね。
こんな感じで書いてみましょう。

use App::Rad;
App::Rad->run;

sub bucho :Help(give a nice compliment) {
     return "Hello Bucho!";
}

変な構文ですね。ヘルプを実行してみましょう。

$ perl rad.pl
Usage: rad.pl command [arguments]
Available Commands:
    bucho     give a nice compliment
    help      show syntax and available commands

ヘルプがついた!

setup

setupコマンドはスクリプトの初期化処理のようなものです。
いくつか例を挙げてみましょう。
以下は先ほどのbuchoコマンドを定義したものです。

sub setup {
    my $c = shift;
    $c->register_commands( {
        bucho => 'give a nice compliment!',
    });
}

もちろん、ここに書いておけば:Helpみたいな変な構文もいりません。


また、サブルーチン全てがコマンドとして登録したくないこともあるでしょう。
その時にはこのように明示的に指定するのが良いでしょう。


逆に、コマンドとして認識させない、という設定もあります。
以下は「_」で始まるサブルーチンをコマンドとして登録させません。

sub setup {
    my $c = shift;
    $c->register_commands( { -ignore_prefix => '_' } );
}


ただし、コマンド管理については
最初に紹介した「使うコマンドを登録する」方式がオススメです。

なぜならuseしたときにExportされるサブルーチンも自動で登録されてしまうため、
除外方式だとカバーしきれなくなってくるからです。*1


ちなみにコマンドを削除する「unregister_command 」
というサブルーチンもあったりします。


引数/バリデーション

引数を扱うにはoptionsを使います*2

$ perl rad.pl create --id=aaa
...
sub create {
    my $c = shift;
    print $c->options->{id}, "\n";  # aaa
}


バリデーションを行いたい場合、getoptを使う方法と
自前でチェックする方法が考えられます。
お好みでどうぞ。

# getopt
sub create {
    my $c = shift;
    $c->getopt( 'id=s', 'version=i' )
            or $c->execute('usage') or return undef;
}

# Data::Validator
sub create {
    my $c = shift;
    my $args = eval {
        state $rule = Data::Validator->new(
            account_id => { isa => 'Str'},
        );
        $rule->validate($c->options);
    };
    if($@) {
        $c->execute('usage') or return undef;
    }
}

共有データ

stashを使うと変数を共有できます。

use Redis::hiredis;

sub setup {
    my $c = shift;
    my $redis = Redis::hiredis->new();
    $redis->connect('127.0.0.1', 6379);
    # stashに入れる
    $c->stash->{redis} = $redis;
}

sub create {
    my $c = shift;
    …
    # stashから取り出す
    $c->stash->{redis}->command('set foo bar');
}

他の便利メソッド

pre_processやpost_processもとても便利ですが
デフォルトの動作が変わってしまう可能性があるので
気をつけて使う必要があるでしょう。*3

example

DynamoDBを操作する簡易CRUDコマンドを作ってみます。

# 例1:createサブコマンド実行
perl dynamodb_crud.pl create --id=1 --name=tori


簡易デプロイツールのテンプレのようなもの。

# 例1:deployサブコマンドをdevelopmentモードで実行
perl deploy.pl deploy --env=development

その他注意点

デフォルトではサブコマンドの戻り値が標準出力されるため、*4
何も出力したくない場合はいちいち
return undef;
しないといけません。

最後に

今までコマンドラインツールといえばGetopt::Longでしたが、
簡単なスクリプトならお手軽にかけますし
Getopt::Longの代わりとしてもアリなのではないでしょうか。
defaultメソッドに処理を書けばサブコマンドなしでも動きますし。*5

use App::Rad;
App::Rad->run();

sub default { rand(10) }

紹介しきれなかった機能もたくさんあるので
ドキュメントをご参照ください。
http://search.cpan.org/~garu/App-Rad-1.04/lib/App/Rad.pm

*1:Data::DumperのDumperコマンドなど

*2:これだけではありません。詳しくはヘルプを

*3:デフォルトの動作はドキュメントに載っているコードです

*4:post_processをオーバーライドすれば変えれます

*5:これは、defaultの本来の使い方ではないような気もしますが…