DEN将棋EX_Mojolicious

前回は、最近リモートアプリで多く使用されていると思われるウェブソケットDEN将棋EXにどのように実装したかを記事にしました。今回はWeb Appplication Framework(WAF)の話です。DEN将棋EXではdaemonとして常駐するゲートウェイをPSGIとして作りましたが、クラウドサービスとしてのユーザー管理・DB連携といった部分はほとんどperlのWAFであるMojolicious::Liteを使用しています。ここでは、DEN将棋EXのコードを抜粋して紹介しながら、どのようにこれを使用したか記述したいと思います。

1.画面遷移

下図はDEN将棋EXの画面遷移図です。紺色の画面Mojolicious::Liteで作りました。緑色の画面は、静的htmlとjqueryなどで作っており、PSGIによるwebsocketサーバーと通信します。

gamen_state.PNG




2.モジュールのインストール

まずは Mojolicious::Lite

cpanm install Mojolicious::Lite

続いて Mojolicious::Plugin::PlackMiddleware

cpanm Mojolicious::Plugin::PlackMiddleware

3.概要

MojoliciousはMVC(Model-View-Controller)モデルのうち、MVを実装しているperlのWAFの一つです。MVCに対応しているcatarystを意識して色々変遷が有ったようですが、入門としてのMojolicious::Liteは、誰でも手軽に使えるMV(Model,View)に対応したWAFで大変重宝しています。

Mojolicious::Lite:本当に簡単なウェブアプリがあればいいときは

基本的なgetやpostメソッドの使い方や、htmlテンプレートの使い方、テンプレートへのperl文法の埋め込み方法などは、下記を参照してください。初心者向けの大変わかりやすい内容です。

Mojolicious入門

Controllerはユーザーが好きなように実装できるような仕組みが実装されています。DEN将棋EXではPlack MiddlewareであるPlack::Sessionを使ったり、オリジナルのモジュールメソッドをヘルパーメソッドとして定義したりしています。

4.Plack::Sessionの利用

理由は省きますがセッションについてはMojolicious標準では無くてPlack::Sessionを使用しました。
Mojolicious::Plugin::PlackMiddlewareをインストールしておいて、Mojolicious::Liteのpluginメソッドを使うと、Plack Middlewareをプラグインとして登録して使用する事ができます。下記コードがその定義部分です。(****は伏せ字)

クラスメソッドであるpluginメソッドの引数に、「plack_middleware」という文字をキーとしたセッション情報を渡します。$SesAr_refがそのセッション情報で、配列リファレンスです。配列要素として、StateとStoreの情報を定義しています。Plack::Session::State、Plack::Session::Storeについては下記を参照してください。

DEN将棋サービスについて
https://metacpan.org/pod/release/MIYAGAWA/Plack-Middleware-Session-0.30/lib/Plack/Session/State/Cookie.pm
https://metacpan.org/pod/release/MIYAGAWA/Plack-Middleware-Session-0.30/lib/Plack/Session/Store/File.pm

my $SesAr_ref = [
    'Session' => {state => Plack::Session::State::Cookie->new(
                    session_key => '****',
                    expires => 1200,
                    path => '/',
                    httponly => 1
                    )},
    'Session' => {store => Plack::Session::Store::File->new(
                    dir => $SesDir
                    )},
];
app->plugin(plack_middleware => $SesAr_ref);#Mojolicious Plugin の書式

DEN将棋EXではログインリクエスト処理(postメソッドの引数で指定するコールバック)でセッション登録用サブルーチンを呼びます。サブルーチンは、

app->start;

でアプリをスタートさせるメソッド呼び出しの後に、記述します。
このセッション登録用サブルーチンを下記に示します。ここではユーザー名とかパスワードをセッションに保存しています。また、第一引数にコントローラーオブジェクトリファレンス(getやpostメソッドで受け取る第一引数)を受け取れるようにしているのは、reqメソッドによりリクエストオブジェクトを取得して、環境変数を得るためです。このリクエスト情報から取得した環境変数を渡してPlack::Sessionオブジェクトを生成します。

sub set_plack_session{
    my($c,$un,$pas)=@_;
    my $request = $c->req;
    my $sess = Plack::Session->new( $request->env );
    $sess->set('uname',$un);
    $sess->set('pass',$pas);
    $sess->set('loginok',1);
    $sess->set('logintime',time);
    my $sfilename = $SesDir . '/' . $sess->id;
    undef $sess;
    eval{
        chmod(0666, $sfilename);
    };
    return(1);
}

下記のコードは、色々なリクエストを受信するたびに呼ばれ、リクエスト環境変数をもとにセッション判定するサブルーチンです。$ExpreSecondはあらかじめ入力してあるセッション有効期間(秒値)で、セッションから取り出したログイン時刻が有効期間内か判定しています。時刻を扱うためTime::Pieceを使用しています。判定OKならセッションのログイン時刻を更新します。実際にはDBと連携しているのでもう少し複雑なコードになっています。

sub load_plack_session{
    my($c,$un_ref,$pas_ref)=@_;
    my $request = $c->req;
    my $sess = Plack::Session->new( $request->env );
    my $t = localtime($sess->get('logintime'));
    my $timestampda = $t->year.'/'.$t->mon.'/'.$t->mday.' '.$t->hour.':'
.$t->minute.':'.$t->sec; my $t_now = localtime; my $timestampda_now = $t_now->year.'/'.$t_now->mon.'/'.$t_now->mday.' '
.$t_now->hour.':'.$t_now->minute.':'.$t_now->sec; if($sess->get('loginok') and ($sess->get('logintime') + $ExpreSecond > time)){
#有効期限チェック $$un_ref = $sess->get('uname'); $$pas_ref = $sess->get('pass'); $sess->set('logintime',time);#logintime更新
return(1); }else{ return(0); } }

5.ヘルパーメソッドの使用

getやpost処理を記述する前に、独自のヘルパーメソッドを定義しておくと、htmlテンプレート内で使用する事ができます。下記はその定義部分です。ShogiEX::DENLibは独自のライブラリーモジュールで、このモジュールで定義してあるメソッドをヘルパーメソッドとして登録しています。

my $DenLib = ShogiEX::DENLib->new();#DEN将棋EXライブラリオブジェクト
helper denlib_get_profile => sub {
    shift;
    my($d1,$d2)=@_;
    return $DenLib->get_profile($d1,$d2);
};

下記はプロフィール変更画面を提供するためのgetリクエスト受信後の処理です。getメソッドのハッシュ引数のキー「/ch_profile」がリクエストURL、バリューの部分が処理本体です。セッションOKならch_progileテンプレートをレンダリングしています。

get '/ch_profile' => sub {
#プロフィール変更画面
    my $self = shift;
    my($un,$pas);
    my $wret = '';
    if(&load_plack_session($self,\$un,\$pas)){
        if($un){
            $self->render('ch_profile','myurl' => \$MyURL ,
'uname' => \$un , 'pass' => \$pas); }else{ my $title = 'エラー'; my $h1 = '不正リクエスト'; $self->render('error','myurl' => \$MyURL,'title' => \$title,
'h1' => \$h1,'comment' => \$wret); } }else{ $self->render('login','myurl' => \$MyURL ,'comment' => \$wret); } };

下記コードは上記処理の中で、コントローラーオブジェクト(上記の$self)のrenderメソッドに渡されるhtmlテンプレートです。テンプレートの中でヘルパーメソッドを呼んでいます。スカラー変数の値はstash関数を使ってテンプレートに渡して使用し、独自のサブルーチンはヘルパーメソッドとして登録する事によりテンプレート内で使用できるという事になります。

@@ ch_profile.html.ep
<%
my $myurl_ref = stash('myurl');
my $un_ref = stash('uname');
my $ps_ref = stash('pass');
my($wret,$txt,@rec,$email,$profile);
my $un = $$un_ref;
$wret = denlib_get_profile($un,\@rec);
if($wret ne 'ok'){
    ### プロフィール取得成功時のhtml出力を記述
}else{
    ### プロフィール取得失敗時のhtml出力を記述
}
%>