だるろぐ

だるいぶろぐです

PlackとDBIx::SkinnyとText::MicroTemplate::Fileでwiki作ったのでソースを公開してみる

2010/04/19 22:41 追記

ブコメでkamipoさんとa666666さんに「TMTで変数をエスケープさせたくない場合はencoded_string()使えばいい」と教えてもらったので修正。
ありがとうございます。


GitHub - hirafoo/piki: PlackとDBIx::SkinnyとText: :Fileで作ったwiki

psgiとかplackとかstarmanとかモダンなアレソレとかに全然ついていけてないので勉強がてら。
今まで作って公開してみたときは解説長々と書いてきたのだけど今回は略。解説じゃない事を書こう。

何でwiki

認証とCRUDが使えれば基本はいいだろうということで今まではBBS作ってたのだけど、芸が無いので。
あと、作ったもの晒すだけなら、ドメイン取って公開せんでもソースだけ晒せばいいと気付き始めた。

development用の環境しか作ってません

最近余裕が無くて週末に晒すとこまでいかんと次やれるのが来週末なので、production用の設定は略。
まぁPlack::Middleware::StackTraceとPlack::Middleware::Staticを外して、daemontools用に適当なrun書けばいい。
devだって起動はstarman使ってるので。違いはdaemonizeするかどうかくらいかと。
ただ、plackupはPLACK_ENVがdevelopmentだと静的コンテンツへのリクエストもログる。個人的にはdevだとしてもそこはログって欲しくないのだけど仕方ないので -E nolog とかで。

PlackはWAFではない

WAFと言うと怒られます。PSGIは仕様、Plackは実装。
とはいえこれを使えば簡単にWAFが作れる。

コントローラにロジック

を書くというのは愚の骨頂。自ら「私は馬鹿です」と宣言するに等しい行為。
とはいえ今回は時間も無いし大したロジックも必要無いので全部コントローラに載せた。
おかげでModel以下に置いたSchema以外のpmがただの飾りです。

Skinny

トリガ、[in|de]flateを作る仕組みが用意されてるので非常に楽。クールですね。
DBICだと色々面倒でモジュール使ってたのに。

DBIx::Skinny::Schema::Loader

を使えばスキーマ定義を書かずに済む。が、アプリ起動時のトリガの実装の都合上、スルー。
Schema.pmにdsn書いてない場合も使えるんだろうなあと思いつつ。

HTTP::Engineと似ている

PlackHTTP::Engineとよく似ているので、ディスパッチャやらハンドラやらのコア部分はこめったーと大差無い。
要はリクエスト受けてディスパッチしてハンドルしてコントローラに渡してモデル呼んでリクエスト返せばよろしい。webappの基本。

DB作る

rubyのDataMapperで作ってたのだけど、これもActiveRecordに倣ってテーブルを複数形で作る。
DBIx::Classならテーブル名は複数、プログラムで呼ぶときは単数って出来るのだけどSkinnyではちょっといじったくらいじゃ出来ないっぽかった上に時間も無いのでrailsに逃げた。
まぁ最初っからテーブル名を単数形で作れって話ではある。

ディスパッチャ

前回は HTTPx::Dispatcher を使った。今回は、それの後継の Router::Simple を使った。
んで、単純にルーティングだけ欲しいのだけど、それは書こうと思えばYAMLにでも書けるけどそれはない、じゃあconfigかというとそれもない、わざわざクラス作ってインスタンス作るのもなー、かといってour変数に持たせるのも変。もう考えるのをやめてpmに書いてクラスメソッドで取得しよう、と思うと

my $dispatch = MyApp::Web::Dispatcher->get_dispatch;

とか頭悪い命名になる。ネーミングセンスが無いので毎回こういうところで苦労している。んで前上司に相談してみたら

そんなときのnewだよ

とのお言葉を頂いた。聞いたときは確かに!と思った…が、結局インスタンスとかルーティングを持つメンバ変数とかにも名前は要るわけで。
結局こうなりました。

sub new {
    my $class = shift;

    my $router = route();
    bless { router => $router }, $class;
}

-later-
my $rule = approuter->{router}->match($env);
# approuter() は上をnewした結果を返す

routerって名前使おうと思ったら、 Router::Simple::Declare がそもそもrouterって関数をexportしてた。この辺で色々考えるのを投げた。
ネーミングセンス欲しいです。

記号

$とか;の多さを(見づらい|面倒)だと思っているのは俺のような下っ端だけかと思ったら、社内の話を聞いていたら、偉い人やhackerもそうだったようです。むしろ、偉い人やhacker曰く「ダサい」とのこと。
rubyいいよrubypythonもいいらしいです。
あとText::MicroTemplateで毎回ビュー側で

? my $params = shift;

とかしたくなかったので、TMTを読んだら、caller側の関数とかが使えそうだったので試したら使えた。
小細工した結果、

コントローラ
sub post_create_account {
    model->insert(account => {
        name     => p->{name},
        password => p->{password},
    });

    +{ template => "create_data" };
}
ビュー
? if (p->{content}) {
<?= p->{content} ?>
? }

とかに。今までpは warn Dumper のエイリアスだったのだけど、今回はppに譲る事に。
あーあと vars::declare 大好きです。

Text::MicroTemplateで自動エスケープを外したかった

wikiという特性上、htmlをアプリ側で生成してビューに渡して表示させたい。が、TMTは自動エスケープするので困る。
部分的に外そうとしたけど面倒だったので最初からTMTのコンストラクタに escape_func => undef して逃げた。
当然、全変数がエスケープされないのでよろしいわけがない。が、時間ないので終了。
escape_func がデフォルトのとundefのインスタンス1個ずつ作ればいいやも。
あとビューの中でマルチバイトな変数出そうとしたら毎回decodeする羽目になった。何かを間違えている。

DBIx::Skinnyのrowオブジェクトをnfeezeしようとすると死ぬ

my $obj = model->search("page")->next;
my $serialized = Storable::nfreeze($obj);

死にます。セッションにaccountのオブジェクト入れようとしたら気付いた。
仕方ないのでオブジェクトの値をhashrefにしたもので代用。

my $hashref = $obj->get_columns;
my $serialized = Storable::nfreeze($hashref);

WAFを作らないのか

と言われた事がある。確かに簡単に作れるし、ディスパッチャ、ハンドラまで書いたならほぼそれに近いのだけど、作ったところでオリジナルは超えないし、きっと俺すら使わないので超誰得なので作らない。

終わり

あー疲れた。物凄く急いで、plackってどうやって使うの?から始めた。というか今回使ったものは全部初めて触った。
次々出てくるモダンなのを追うのは大変だけど楽しい。知らない事、分からない事をやるのが楽しい。
とはいえこれだと提供された物を使うだけなのであんまり成長しないので、もっと色々勉強せんと。
最後に今回参考にしたところ。

http://advent.plackperl.org/
http://github.com/miyagawa/plack-dispatching-samples