Posted at

MojoliciousでCLI

More than 5 years have passed since last update.

こんにちは。@hayajoと申します。今月はPHPで開発していますが進捗ダメです。


さて、ウェブアプリケーションといってもウェブインターフェースだけではなく、外部データのインポートやエクスポート、データ集計のためのバッチ処理など、コマンドラインインターフェース(CLI)として実装するものも少なくありません。

今回はこのようなコマンド群をMojolicous(::Lite)アプリケーション実行コマンドのサブコマンドとして実装する方法をご紹介します。


ネームスペースの追加する

まずはアプリケーションのcommandsに新しくネームスペース(MyApp::CLI)を追加します。


Mojolicious::Liteアプリの場合


myapp.pl

#!/usr/bin/env perl

use Mojolicious::Lite;

# 追加ここから --
use FindBin;
BEGIN { unshift @INC, "$FindBin::Bin/lib" }

my $commands = app->commands;
# コマンドのネームスペースとしてMyApp::CLIを追加
push @{ $commands->namespaces }, 'MyApp::CLI';
# -- 追加ここまで

# Documentation browser under "/perldoc"
plugin 'PODRenderer';
...



Mojoliciousアプリの場合


lib/MyApp.pm

package MyApp;

use Mojo::Base 'Mojolicious';

# 追加ここから --
has commands => sub {
my $commands = shift->SUPER::commands;
# コマンドのネームスペースとしてMyApp::CLIを追加
push @{ $commands->namespaces }, 'MyApp::CLI';
return $commands;
};
# -- 追加ここまで

# This method will run once at server start
sub startup {
my $self = shift;
...



サブコマンドを作成する

サブコマンドはMojolicious::Commandを継承して作成します。

例ではnyanサブコマンドを作成しています。


lib/MyApp/CLI/nyan.pm

package MyApp::CLI::nyan;

# サブコマンドはMojolicious::Commandを継承して作ります
use Mojo::Base qw/Mojolicious::Command/;

use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);

# コマンド一覧に表示される解説
has description => "say 'nayn'.\n";

# ヘルプメッセージ
has usage => <<EOF;
usage: $0 hoge

These options are available:
-d --debug debug mode
EOF

# コマンド実態
sub run {
# コマンドライン引数は@argsに格納されます
my ($self, @args) = @_;

# コマンドライン引数をパースします
GetOptionsFromArray(\@args, 'd|debug' => \(my $debug))
or die $self->usage;

if ($debug) {
# $self->appでMojolicious(::Lite)インスタンスが取得できます。
say "MOJO_HOME: " . $self->app->home;
say "MOJO_MODE: " . $self->app->mode;
say "---";
}

say 'nayn';
};

1;


下記のオプションはMojolicious側で処理されるので、サブコマンド側でオプションを受け取る場合は注意してください。


  • -h, --help

  • -m, --mode

  • --home


サブコマンドを実行する

nyanサブコマンドが認識されているか確認します。(例はMojolicious::Liteアプリ)

$ ./myapp.pl

usage: ./myapp.pl COMMAND [OPTIONS]

Tip: CGI and PSGI environments can be automatically detected very often and
work without commands.

These commands are currently available:
cgi Start application with CGI.
cpanify Upload distribution to CPAN.
daemon Start application with HTTP and WebSocket server.
eval Run code against application.
generate Generate files and directories from templates.
get Perform HTTP request.
inflate Inflate embedded files to real files.
prefork Start application with preforking HTTP and WebSocket server.
psgi Start application with PSGI.
routes Show available routes.
test Run unit tests.
version Show versions of installed modules.
nyan say 'nayn'.

These options are available for all commands:
-h, --help Get more information on a specific command.
--home <path> Path to your applications home directory, defaults to
the value of MOJO_HOME or auto detection.
-m, --mode <name> Operating mode for your application, defaults to the
value of MOJO_MODE/PLACK_ENV or "development".

See './myapp.pl help COMMAND' for more information on a specific command.

nyanサブコマンドが追加されていますね。実行してみましょう。

$ ./myapp.pl nyan --help

usage: ./myapp.pl hoge

These options are available:
-d --debug debug mode
$ ./myapp.pl nyan
nayn
$ ./myapp.pl nyan --debug
MOJO_HOME: /Users/hayajo/work/my_app
MOJO_MODE: development
---
nayn

サブコマンドとして実行できていますね。


CLIだけの場合

CLIだけが必要であれば下記のようなスクリプトを用意します。

#!/usr/bin/env perl

use strict;
use warnings;

use FindBin;
BEGIN {
# パスは環境にあわせて変更してください
unshift @INC, "$FindBin::Bin/lib";
$ENV{MOJO_HOME} = $FindBin::Bin
unless $ENV{MOJO_HOME};
}

require Mojolicious::Commands;
my $cmd = Mojolicious::Commands->new;
$cmd->namespaces([qw/MyApp::CLI/]); # Mojolicious::Command::* は利用しない

$cmd->run(@ARGV);

あとは前述のようにサブコマンドを用意しください。この場合の$self->appは素のMojolicious::Liteインスタンスになります。


ちなみに...

あまりお勧めはしませんが、ネームスペースが気にならないのであればサブコマンドをMojolicious::Command::nyanとして実装してしまうことも可能です。


まとめ

ウェブアプリケーションの脇を固めるコマンド群は単独のコマンドとして作っても問題無いと思いますが、このようにしてサブコマンドとして実装すると、コマンドが散らばらずにとてもスッキリします。

また、設計によってはウェブアプリケーションとコマンドの処理や設定の共通化もできるので、コードもスッキリすると思われます。

Mojolicious::Commands自体はCLIのフレームワーク的に利用するこができます。Mojoliciousの便利なモジュールと組み合わせたポータブルなコマンドを作成することも可能です。

Mojolicious、なかなか奥が深いですね。


@yusukebeさん、声をかけていただきありがとうございました。

参加するか迷っていたところだったので、ちょうど良いタイミングでした。


今年の娘ちゃん(小4)のクリスマスプレゼントはiPod Touchです。私が同じ年齢のときはゾイドのアイアンコングを貰ったよなうな気が…