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レポジトリは以下から!