MojaMojaを使ってみてsinatraライクな micro web application framwork に興味が湧いたので、 本家sinatraやMojolicious::Lite、Schenker、Dancerあたりを参考にして 自分なりの micro WAF を言わば「再開発」してみた。 あくまでオレオレ。名前はアニメ化物語がインスパイアで「Hitagi」と言います。
Hitagi - Shall we talk about stars and micro web application frameworks.
日本人の方が(メインの)作者のCPANモジュールを組み合わせてsinatraっぽくかつ簡単な モデルまでサポートさせているのが特徴です。 以下のようにモジュールを使っています。おかげでHitagiのコード量は200行弱に収まりました。
- Plack::Request/Response - リクエスト/レスポンス生成
- Text::MicroTemplate + Data::Section::Simple - ビュー
- Router::Simple - ディスパッチャ
- DBIx::Skinny - モデル(DB)
簡単な使い方、SYNOPSISがこちら。モデルが戦場ヶ原ひたぎなので最後に star; と書きます。
use Hitagi;
get '/' => sub { render( 'index', { message => 'Hi' }) };
star;
__DATA__
@@ index
<h1>message : <?= $message ?></h1>
これをmyapp.plとして保存して perl コマンドで実行すればデフォルト5000番ポートでサーバが立ち上がります。
$ perl myapp.pl
getメソッドでディパッチとそれに対するのコントローラの定義をします。 「__DATA__」以下のデータセクションにText::MicroTemplateの書式でテンプレートを書きます。 renderメソッドがそのテンプレートの処理をしてくれます。 第一引数にテンプレートの名前、第二引数にテンプレートに渡したい値やサブルーチンをハッシュリファレンスの 形式で渡してあげます。するとハッシュのキーの名前を変数名としてそのキーに対する値やサブルーチンをテンプレートの中で扱うことができるようになります。(Text::MicroTemplate::Extendedからパクりました^^)
テンプレートの扱いをちょっと便利にしていて、 「layout」を使いまわすことが可能です。 こう書くと index テンプレートを render すると layout テンプレートでラップしてくれます。
$ perl myapp.pl
use Hitagi;
...;
__DATA__
@@ index
<h1>welcome</h1>
@@ layout
<html>
</head><title>title</title></head>
<body>
<div id="container">
<?= content ?>
</div>
<address>This content is made by Hitagi</address>
</body>
</html>
静的ファイルのサーブもstaticというディレクトリに置けばよいとデフォルトで利用可能で、 またモデルとしてDBもサポートしています。 詳しくはREADMEやPODを参照してもらいたいのですが、 手っ取り早くわかるために tokuhirom の OreOre-NoPasteの仕様を真似て似た様なものを作ってみたのでそのコードを掲載します。
use Hitagi;
use Data::UUID;
my $uuid_gen = Data::UUID->new;
set db => {
connect_info => [ 'dbi:SQLite:','', '' ],
schema => qq{
install_table entry => schema {
pk 'id';
columns qw/id body/;
};
}
};
db->do(q{CREATE TABLE entry ( id varchar, body text )});
get '/' => 'index';
post '/post' => sub {
my $req = shift;
my $body = $req->param('body') or redirect( $req->base );
my $uuid = $uuid_gen->create_str;
db->insert(
entry => {
id => $uuid,
body => $body,
}
);
return redirect( $req->base . "entry/$uuid" );
};
get '/entry/{entry_id}' => sub {
my ( $req, $args ) = @_;
my $entry_id = $args->{entry_id};
my $entry = db->single( entry => { id => $entry_id, } );
return res(404,[],'Not Found')->finalize unless $entry;
render( 'entry', { body => $entry->body } );
};
star;
__DATA__
@@ layout
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>nopaste</title>
<link rel="stylesheet" type="text/css" href="<?= $base ?>static/screen.css" />
</head>
<body>
<div class="container">
<h1><a href="<?= $base ?>">Yet Another nopaste</a></h1>
<?= content ?>
</div>
</body>
</html>
@@ index
<form action="<?= $base ?>post" method="post">
<p><textarea name="body" cols="60" rows="10"></textarea></p>
<p><input type="submit" value="no paste" /><p>
</form>
@@ entry
<pre>
<?= $body ?>
</pre>
こうすれば nopaste.pl と static/screen.css だけあればとりあえずは 動くものができちゃいます。 一応デプロイのことも考えていて、nopaste.pl を nopaste.cgi にするように 拡張子によってCGIとして動作させることができます。 また、もちろんPlackベースなのでPSGIアプリとしても利用可能で、 今のところ $ENV{PLACK_ENV} の値をみて star メソッドがアプリケーションハンドラを返すかどうかの 分岐をしています。
一発ネタの内部的には簡単なアプリを今までわざわざCatalystで作ってたりしましたが、 今度からHitagiベースで開発していこうかと思います。 個人的な要望含め今後改善していくので、まぁ似た様なものありますが、気になった方は試してみてください。 ということでEnjoy! ダウンロード&githubレポジトリは以下から!