Tatsumakiのdemoプログラム解析

demo

Tatsumakiはmiyagawaさんが作ったPlackベースのフレームワークです.eg/chat配下のサンプルがwebベースでチャットシステムを作る時の参考になるので解析してみました.

使い方

まずapp.psgiを調べてみます.

$ plackup -s AnyEvent

で実行すると5000番ポートでTatsumakiサーバが起動します.まず,アクセスするパスですが,mainパッケージをみてみると,

package main;
use File::Basename;

my $chat_re = '[\w\.\-]+';
my $app = Tatsumaki::Application->new([
    "/chat/($chat_re)/poll" => 'ChatPollHandler',
    "/chat/($chat_re)/mxhrpoll" => 'ChatMultipartPollHandler',
    "/chat/($chat_re)/post" => 'ChatPostHandler',
    "/chat/($chat_re)" => 'ChatRoomHandler',
]);

となっているのでローカルサーバにアクセスするのであれば,チャットルーム名をsampleとした場合には

http://localhost:5000/chat/sample

とアクセスすることでチャット画面が起動します."Your email(for Gravatar)"のテキストエリアにGravatar用のメールアドレスを記入すると,"Something to say"に記入したメッセージ送信時に,そのヘッダにGravatarに登録したアイコンを表示します.

次にtemplates/chat.htmlを見てみます.

% my $channel = $_[0]->{handler}->args->[0];
% my $mxhr = $_[0]->{handler}->request->param('mxhr');
<html>
<head>
<title>Tatsumaki Chat demo</title>
<script src="/static/jquery-1.3.2.min.js"></script>
% if ($mxhr) {
<script src="/static/DUI.js"></script>
<script src="/static/Stream.js"></script>
% } else {
<script src="/static/jquery.ev.js"></script>
% }

requestのパラメータに"mxhr"という文字列が入っているとincludeするjavascriptライブラリが変わって挙動が変化するようになっています.具体的には,

http://localhost:5000/chat/sample?mxhr=1

とすると挙動が変わります.mxhrをつけずに起動した場合にはjquery.ev.jsが働き"long poll"型のアーキテクチャとして動きます.一方mxhrをつけて起動した場合にはDUI.jsが働いてmultipart/mixed型のアーキテクチャとして動きます.さらに,"oembed.js"をインクルードしているので"Something to say"のテキストエリアに記入した,サードパーティサイトのURLで現されたコンテンツを埋め込むことができます.例えば

http://www.flickr.com/photos/bulknews/2694608273/

と記入すると,miyagawaさんとLarry Wallが写ってる写真を取込みます.

構成

使い方の項でも見たように,アクセスパスと呼び出すクラスの関係は以下のようになっています.

my $app = Tatsumaki::Application->new([
    "/chat/($chat_re)/poll" => 'ChatPollHandler',
    "/chat/($chat_re)/mxhrpoll" => 'ChatMultipartPollHandler',
    "/chat/($chat_re)/post" => 'ChatPostHandler',
    "/chat/($chat_re)" => 'ChatRoomHandler',
]);

まず,テンプレートを使ってhtmlファイルを書き出す場合のサンプルはChatRoomHandlerになります.このパッケージをみると,

sub get {
    my($self, $channel) = @_;
    $self->render('chat.html');
}

となっていて,テンプレートファイルを読み込んでレンダリングする場合には,

$self->render('テンプレートファイル');

とすることがわかります.テンプレートの描画にはText::MicroTemplate::Fileを使っていて,

    my $mt = Text::MicroTemplate::File->new(
        include_path => [ 'templates' ],
        use_cache => 0,
        tag_start => '<%',
        tag_end   => '%>',
        line_start => '%',
    );

とカスタマイズしています.先頭に"%"を入れるとperlスクリプトがそのまま書けるわけです.

次に,メッセージをpostするサンプルはChatPostHandlerで,

sub post {
    my($self, $channel) = @_;
    ...
    $self->write({ success => 1 });
}

jsonでのデータをクライアントに返します.さらに,longpoll型のアーキテクチャ用のサンプルはChatPollHandlerになります.

sub get {
    my($self, $channel) = @_;
    ...
    $mq->poll_once($client_id, sub { $self->on_new_event(@_) });
}

sub on_new_event {
    my($self, @events) = @_;
    $self->write(\@events);
    $self->finish;
}

writeしてからfinishする必要があると.最後にmultipart/mixed型のサンプルはChatMultipartPollHandlerになります.

sub get {
    my($self, $channel) = @_;

    my $client_id = $self->request->param('client_id') || rand(1);

    $self->multipart_xhr_push(1);

    my $mq = Tatsumaki::MessageQueue->instance($channel);
    $mq->poll($client_id, sub {
        my @events = @_;
        for my $event (@events) {
            $self->stream_write($event);
        }
    });
}

この場合はstream_writeを使っています.finishを呼び出す必要はありません.

おわりに

なんか中途半端な感もありますが,とりあえずこんなところで.