Perlでコマンドラインモジュールを書くときのクラス設計
コマンドラインから引数が渡すようなモジュールでも以下のような要件があったりする。
- コマンドラインからだけじゃなくて、コード内で直接オブジェクトをnewしたい場合もある
- 複数のコマンドラインツールを組み合わせて使うような場合に
@ARGV
を何度か渡すケースがある
growthforecast.pl
みたいなサーバースクリプトだとよくあると思っていて、スクリプト単体で引数渡して起動できるけど、自分でGrowthForecast::Web->new
したくなることもある。これが前者。
growthforecast.pl
にPlackとGrowthForecastのオプションをコマンドラインから同時に渡したいみたいなのがある。これが後者。
GrowthForecastはあくまで例えです。念のため。
それに対しては以下のようにすればよいのかなーとなった。
parse_options
というクラスメソッドで@ARGV
の分解を行う- 1の結果を普通に
new
に食わせる - ついでに、1,2を同時にやる
new_with_options
を生やしておくと便利そう
コード例としては以下。
use MyApp;
my ($opt, $rest_argv) = MyApp->parse_options(@ARGV);
my $app = MyApp->new($opt);
...
or
use MyApp;
my $app = MyApp->new_with_options(@ARGV);
$rest_argv
には、@ARGVの残りが入ってくる。それを他のparse_options
に食わせたりとかできるという寸法。
parse_options
は例えば以下の様な感じ。
use Getopt::Long ();
use Pod::Usage;
use Hash::Rename qw/hash_rename/;
sub parse_options {
my ($class, @argv) = @_;
my $p = Getopt::Long::Parser->new(
config => [qw/posix_default no_ignore_case auto_help bundling pass_through/]
);
$p->getoptionsfromarray(\@argv, \%opt, qw/
hoge=s
fuga=s
dry-run
/) or pod2usage;
hash_rename %opt, code => sub {tr/-/_/};
(\%opt, \@argv);
}
Getopt::Long::Parserを使うとconfigを局所化できるし、getoptionsfromarray
使うといちいち@ARGV
をローカライズとかしなくていいので、この使い方が最近のオレオレGetopt::Longプラクティス。
乱暴だけどHash::Renameでハイフン区切りのキーを一括でsnake_caseに変換とかしてみた。Hash::Rename便利だった。
色々つらつらと考えた末、こうするのがいいんじゃないかなーと思った。いかがでしょうか?