Mojoliciousによる開発の勘所
ここからはMojoliciousを利用したWebアプリケーション開発におけるポイントを解説します。
解説する内容を反映したサンプルコードmojo_
)
MojoliciousとMojolicious::Lite
(2)myapp.
や掲示板アプリケーションは、Mojolicious::Lite
を利用したアプリケーションです。ルーティング、
ですが、Mojolicious::Lite
では荷が重くなってきます。このような場合には通常のMojoliciousアプリケーションとして開発を行うことをお勧めします。
Mojoliciousアプリケーションはmojogenerate
で生成できますが、lite_
ではなくapp
となります。
$ mojo generate app
コマンドが完了すると、my_
ディレクトリ配下に図5のディレクトリ構造が生成されます。各コンポーネントごとにファイルが分割されるので、

プロトタイピングでMojolicious::Liteアプリケーションを作成し、
モデルの実装
前述したとおり、
前節の掲示板アプリケーションの場合は特にほかのインタフェース
リスト9は、Teng
を利用したモデルの実装例です。
package MyApp::Model;
use Mojo::Base -strict;
use Carp qw/carp croak/;
use Mojo::Loader qw/find_modules load_class/;
use Teng::Schema::Loader;
load_class $_ for find_modules __PACKAGE__;
my $INIT_ARGS;
sub init {
my ($class, %args) = (shift, @_);
if ($INIT_ARGS) {
carp "already initialized";
}
else {
$INIT_ARGS = \%args;
}
return $INIT_ARGS;
}
sub db {
croak "You should init first" unless $INIT_ARGS;
state $db = Teng::Schema::Loader->load(
namespace => 'MyApp::DB',
%$INIT_ARGS,
);
}
1;
このモデルを各インタフェースで直接扱ってもよいのですが、
package MyApp::Model::User;
use Mojo::Base -strict;
sub table_name {'user'}
sub all {
my $class = shift;
MyApp::Model->db->search(
table_name,
{deleted => 0},
{order_by => 'name'},
);
}
...
このように実装することでコントローラからアプリケーションロジックを分離し、
MyApp::Model->init(connect_info => ...);
my $users = MyApp::Model::User->all();
パラメータのバリデーション
MojoliciousではパラメータのバリデーションにMojolicious::Validator::Validation
を利用できます。
コントローラのvalidation
メソッドでインスタンスを取得でき、required
やoptional
などの各種制約を指定します。
my $v = $c->validation;
$v->required('name')->like(qr/^[a-z]+$/);
$v->optional('group')->in(qw/admin guest/);
return $c->render if $v->has_error;
テンプレートではMojolicious::Plugin::TagHelpers
の各種フィールドを使用することで、field-with-error
クラスの付与を自動的に行ってくれます。
<%# TagHelpers の使用 %>
%= label_for name => 'Name (required, only a-z)'
%= text_field 'name'
<!-- エラー時に生成されるHTML -->
<label class="field-with-error" ...>...</label>
<input class="field-with-error" value="INVALID NAME" ...>
また、Mojolicious::Plugin::TagHelpers
のcsrf_
と組み合わせることでCSRF
<%# フォーム画面のテンプレートにcsrf_field を追加 %>
%= csrf_field
# コントローラでCSRFトークンをチェック
return $self->reply->not_found
if $v->csrf_protect->has_error;
ユーザ定義の制約はMojolicious::Validator
のadd_
メソッドで追加します。筆者はモデルでもMojolicious::Validator
を利用するため、
package MyApp::Model;
...
use MyApp::Validator;
...
sub validator {
state $validator = MyApp::Validator->new;
}
package MyApp::Validator;
use Mojo::Base 'Mojolicious::Validator';
...
sub new {
my ($class, @args) = @_;
my $self = $class->SUPER::new(@args);
# ユーザ定義の制約を追加
$self->add_check(not_blank => sub { $_[2] =~ /^\s*$/ })
->add_check(not_like => sub { $_[2] =~ $_[3] });
return $self;
}
...
Mojoliciousアプリケーションではvalidator
メソッドでモデルのValidatorインスタンスを登録します。こうすることでモデルとコントローラで共通の制約を扱えます。
use Mojo::Base 'Mojolicious';
use MyApp::Model;
...
sub startup {
my $self = shift;
...
# Validator を登録
$self->validator(MyApp::Model->validator);
...
アプリケーションのテスト
Mojoliciousアプリケーションは標準でサーバを通さずにリクエストを送るget
コマンドが利用できます。開発中の簡単な動作確認に重宝します。
$ ./script/my_app get /users 'ul.user-list > li' all
$ ./script/my_app get /users.json /users
本格的なテストにはMojo::Test
というテスト用のモジュールを利用します。レスポンスヘッダの内容やCSSセレクタ、Mojo::Test
を利用したテストコードの一部です。
use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
...
my $t = Test::Mojo->new('MyApp');
...
subtest 'create_user' => sub {
...
my $url = $t->app->url_for('create_user');
$t->get_ok($url)
->status_is(200)
->element_exists('input[name=csrf_token]');
my $csrf_token = $t->tx->res
->dom->at('input[name=csrf_token]')
->attr('value');
subtest 'post with valid params' => sub {
my $name = 'newuser';
$t->post_ok($url, form => {
csrf_token => $csrf_token,
name => $newuser,
})
->status_is(302)
->header_is(
Location
=> $t->app->url_for(read_user => {name => $name});
);
};
...
};
テストの実行にはMojoliciousアプリケーションのtest
コマンドを使います。Test::Harness
に付属するprove
コマンドでも実行できます。
$ ./script/my_app test
$ prove -lr t
アプリケーションのデプロイ
Mojoliciousアプリケーションはさまざまな方法でデプロイできます。代表的なデプロイ方法を解説します。
ビルトインWebサーバ
Mojoliciousアプリケーションは標準でHTTPとWebSocketをサポートしたWebサーバ機能が組み込まれます。
daemon
コマンドではシングルプロセスのWebサーバ、prefork
コマンドではプリフォーク型のWebサーバでアプリケーションを実行できます。開発用途やアプリケーション配布後の実行コマンドとして利用します。
$ ./script/my_app daemon
$ ./script/my_app prefork
Morbo、Hypnotoad
Mojoliciousをインストールすると、mojo
コマンドのほかにmorbo
とhypnotoad
コマンドもインストールされます。
morbo
はアプリケーションのdaemon
コマンドと同じくシングルプロセスのアプリケーションサーバですが、
$ morbo script/my_app
hypnotoad
はアプリケーションのprefork
コマンドと同じくプリフォーク型のアプリケーションサーバです。ホットデプロイをサポートし、
$ hypnotoad script/my_app
コマンドの再実行だけでホットデプロイ可能
$ hypnotoad script/my_app
Starting hot deployment for Hypnotoad server 31481.
ビルトインWebサーバやmorbo
がコマンドラインオプションで動作を設定するのに対して、hypnotoad
ではMojoliciousアプリケーションのconfig
で動作を設定します。Mojolicious::Plugin::Config
やMojolicuous::Plugin::JSONConfig
でも同様に設定できます。
use Mojolicious::Lite;
app->config(hypnotoad => ['http://*:80']);
...
app->start;
PSGI、CGI
Mojolicious はPSGIplackup
などのPSGIをサポートしたコマンドにスクリプトのパスを渡すだけで、
$ plackup -S Starman ./script/my_app
またCGIにも対応しており、
ScriptAlias / /path/to/my_app/script/my_app/
まとめ
駆け足になりましたが、
Mojoliciousは、
さて、