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 preforkMorbo、Hypnotoad
Mojoliciousをインストールすると、mojoコマンドのほかにmorboとhypnotoadコマンドもインストールされます。
morboはアプリケーションのdaemonコマンドと同じくシングルプロセスのアプリケーションサーバですが、
$ morbo script/my_apphypnotoadはアプリケーションの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は、
さて、