$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

Mojolicious + Xslate + SkinnyでMVC開発環境

Perlには非常に多くのフレームワークがあります。非常に大きいものにはCatalystやJifty、小さいものにはMojolicious、CGI::Application、HTTP::Engine、Dancerなどがあります。今Perlではフルスタックのフレームワークを使うよりも、小さいものを組み合わせてWebアプリケーションをつくっていくほうが流行っていると思うので、「小さいものを組み合わせ」ながらMVCで開発できるような環境を考えてみました。

 今回はWAFとしてMojoliciousを、テンプレートエンジンとしてText::Xslateを、O/RマッパーとしてDBIx::Skinnyを用いて、MVCでWebアプリケーションを作る方法について順を追って解説します。


それぞれの簡単な説明

Mojoliciousとは

SimpleなMVCのWebフレームワーク。軽いプロジェクトならMojolicious::Liteを、少し重いならMojoliciousを、などと使い分けることが出来る。今回はディスパッチ部分、繋ぎこみ部分、コントローラにMojoliciousを利用する。

Text::Xslateとは

id:gfxさんが作成した国産テンプレートエンジン。動作が非常に速いことが特徴。Template::Toolkitのようなシンタックスも用意されている。今回はViewとして使います。

DBIx::Skinnyとは

id:nekokakさんが作成した国産O/Rマッパー。DBIを薄くラップした軽量ORM。ruleベースのinflate/deflateなどができる。今回はModelとして使います。

Mojoliciousの雛形を作成する

雛形作成コマンド

 まずはMojoliciousの雛形を作成します。これはgeneratorが用意されているので次のようなコマンドを入力することで作成することが出来ます。

% mojo generate app MojoMvcTutorial
構成

次のような構成が作成されます。デフォルトではMojoMvcTutorial.pmがルーティングや設定のため、Example.pmがコントローラで、templates以下がViewを置けるようになっています。

mojo_mvc_tutorial
├── lib
│   ├── MojoMvcTutorial
│   │   └── Example.pm
│   └── MojoMvcTutorial.pm
├── log
├── public
│   └── index.html
├── script
│   └── mojo_mvc_tutorial
├── t
│   └── basic.t
└── templates
    ├── example
    │   └── welcome.html.ep
    ├── exception.html.ep
    ├── layouts
    │   └── default.html.ep
    └── not_found.html.ep
アプリケーション起動

Plack対応しているので以下のコマンドで起動できます。

plackup script/mojo_mvc_tutorial

http://localhost:5000/にアクセスすると次のように表示されます。

ルーティングと設定

デフォルトだとlib/MojoMvcTutorial.pmがアプリケーションのコアオブジェクトとなっていてルーティングや設定などをここで行います。初期状態だと以下のようになっていて、URLをcontroller, action, idにマップし、root(/のこと)ではexample#welcomeにマッピングするという設定がなされているだけです。
lib/MojoMvcTutorial.pm

package MojoMvcTutorial;

use strict;
use warnings;

use base 'Mojolicious';

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

    # Routes
    my $r = $self->routes;

    # Default route
    $r->route('/:controller/:action/:id')->to('example#welcome', id => 1);
}

1;
Controller

lib/MojoMvcTutorial/Example.pmがサンプルで作成されたコントローラです。アクションを関数として定義していくという一般的なコントローラです。renderメソッドにhashを渡すことでテンプレートにデータを渡せます。
lib/MojoMvcTutorial/Example.pm

package MojoMvcTutorial::Example;

use strict;
use warnings;

use base 'Mojolicious::Controller';

# This action will render a template
sub welcome {
    my $self = shift;

    # Render template "example/welcome.html.ep" with message
    $self->render(message => 'Welcome to the Mojolicious Web Framework!');
}

1;
View

templates以下にViewを配置できるようになっています。デフォルトではPerlのコードを埋め込むというep形式でViewが書かれていて、コントローラアクションに対応する形でファイルが配置されます。

% layout 'default';
<h2><%= $message %></h2>
This page was generated from the template
"templates/example/welcome.html.ep" and the layout
"templates/layouts/default.html.ep",
<a href="<%== url_for %>">click here</a>
to reload the page or
<a href="/index.html">here</a>
to move forward to a static page.

ViewとしてXslateを導入

Mojoliciousのテンプレートエンジンの切り替えは非常に簡単で、Text::Xslateの場合、pluginが用意されています。MojoMvcTutorial.pmのstartupの中に以下を追加するだけで、Xslateが使えるようになります。

$self->plugin('xslate_renderer');

あとはtemplateをXslate用に書き換えるだけです。
templates/example/welcome.html.tx

<h2><: $message :></h2>
xslate render this

Controllerの名前空間変更

Mojoliciousの場合、Controllerはlib/MojoMvcTutorial/以下に全て配置されます。Modelやその他のオブジェクト層を作るときにこのままだと非常に見にくいため、名前空間を区切った方がいいと思うので、lib/MojoMvcTutorial/Controller/以下に配置できるようにしたいと思います。これもMojoliciousが機能を提供していて、MojoMvcTutorial.pmのstartupに以下を追加するだけです。

$r->namespace('MojoMvcTutorial::Controller');

あとはExample.pmをControllerディレクトリに入れて、package名も変えておきます。


ModelとしてDBIx::Skinnyを導入

 次はModelとしてSkinnyを導入します。

DB設定

lib/MojoMvcTutorial/Model.pmにDBの設定を記述します。
lib/MojoMvcTutorial/Model.pm

package MojoMvcTutorial::Model;
use strict;
use warnings;

use DBIx::Skinny connect_info => +{
    dsn => 'dbi:mysql:mojo_mvc_tutorial',
    username => 'root',
    password => '',
};

1;
Schema設定

次にlib/MojoMvcTutorial/Model/Schema.pmにスキーマの設定を記述します。今回はサンプルとして、エントリーをポストしてそれを見れるだけの簡単なものをつくってみたので、以下のようなスキーマにしておきました。DBIx::Skinnyは名前ベースのruleを作成できるので_atが付いていたら、Datetimeとして使うように設定してみました。

package MojoMvcTutorial::Model::Schema;
use strict;
use warnings;

use DBIx::Skinny::Schema;
use DateTime;
use DateTime::Format::Strptime;
use DateTime::Format::MySQL;
use DateTime::TimeZone;


my $timezone = DateTime::TimeZone->new(name => 'Asia/Tokyo');
install_inflate_rule '^.+_at$' => callback {
    inflate {
        my $value = shift;
        my $dt = DateTime::Format::Strptime->new(
            pattern   => '%Y-%m-%d %H:%M:%S',
            time_zone => $timezone,
        )->parse_datetime($value);
        return DateTime->from_object( object => $dt );
    };
    deflate {
        my $value = shift;
        return DateTime::Format::MySQL->format_datetime($value);
    };
};

install_utf8_columns qw/title body/;

install_table 'entry' => schema {
    pk 'id';
    columns qw/
                  id title body created_at updated_at
              /;
};

1;

__DATA__;
DROP TABLE if EXISTS `entry`;
CREATE TABLE `entry` (
       `id` int(11) unsigned auto_increment NOT NULL,
       `title` tinytext,
       `body`  text,
       `created_at` timestamp NOT NULL,
       `updated_at` timestamp NOT NULL,
       PRIMARY KEY (`id`)
);
Controllerから簡単に使えるように

ここまでの設定で

use MojoMvcTutorial::Model;
MojoMvcTutorial::Model->search(...)

などとすればどこでもModelが使えるようになっているのですが、ControllerからModelを簡単に呼び出せるようにアクセサを提供しておきます。これはあってもなくてもいいと思います。
MojoMvcTutorial.pmに以下を追記。

use MojoMvcTutorial::Model;
__PACKAGE__->attr(model => sub { MojoMvcTutorial::Model->new });

こうすることでコントローラ内から以下のように使うことが出来ます。

sub example {
    my $c = shift;
    $c->app->model->...;
}

最終的な構成

 最終的に以下のような構成になりました。この構成だと、Model, Controller, Viewがきっちり分かれていて、非常に見やすく書けます。小さいものを組み合わせて作っているため、ModelやViewの置き換えも簡単にできます。また、他にオブジェクト層やアプリケーション層を作りたい時も、lib/MojoMvcTutorial/以下に適当に区切って作り、Controllerなどから呼び出せばよく、柔軟に対応できます。

mojo_mvc_tutorial
├── lib
│   ├── MojoMvcTutorial
│   │   ├── Controller
│   │   │   └── Example.pm
│   │   ├── Model
│   │   │   └── Schema.pm
│   │   └── Model.pm
│   └── MojoMvcTutorial.pm
├── log
├── public
│   └── index.html
├── script
│   └── mojo_mvc_tutorial
├── t
│   └── basic.t
├── templates
│   ├── example
│   │   └── welcome.html.tx
│   ├── exception.html.ep
│   ├── layouts
│   │   └── default.html.ep
│   └── not_found.html.ep
└── tmp

まとめ

 今回はMojolicious + Xslate + SkinnyでMVC開発を行うために自分がやってみた方法を実際に構成しながら説明しました。https://github.com/shiba-yu36/p5-mojo-mvc-tutorialにこの構成を使って適当にSampleを作ってみた例を置いておきましたので、興味があればそちらも見てもらえればと思います。