URLに応じたページが表示されるまで

tag

当初は3日くらいで次の人に回すつもりだったのですが、なんだかんだで機能追加してるので5日くらい書かないといけないのではないか戦々恐々としてるkanです。

昨日は http://localhost:5000/ へアクセスすると「hello」と表示されるようになったので、いよいよ本格的に作っていくことにします。まず、ページの構成をどうするか考えます。最初、こんな感じに考えました

更に、複数のtrackを扱うのでtrackの一覧を出して欲しいということになり

年/ で、その年のtrack一覧を表示するようにしました。

これを実現するには、アクセス毎に処理を振り分ける必要があります。CPANにはpath routing用のモジュールが沢山ありますが、今回はシンプルに使える Router::Simple を使うことにしました。↑のルールは以下のように書けます。

$router->connect(
    '/{year:\d{4}}/',
    { controller => 'Calendar', action => 'track_list' }
);
$router->connect(
    '/{year:\d{4}}/{name:[a-zA-Z0-9_-]+?}/',
    { controller => 'Calendar', action => 'index' }
);
$router->connect(
    '/{year:\d{4}}/{name:[a-zA-Z0-9_-]+?}/{day:\d{1,2}}',
    { controller => 'Calendar', action => 'entry' }
);

「{year:\d{4}}」のように書くと「\d{4}」という正規表現にマッチした値を「year」というキーで参照できるようになります。以下のような感じです。

my $p = $router->match($env);
warn $p->{year};

処理を振り分けたら、画面を表示する処理を作ります。HTMLを出力する場合、perlのソース中にHTMLを埋め込んでいく手もありますが、可読性が落ちて早々にメンテが困難になるケースが多いと思います(e.g. perl4時代のCGIスクリプト)。そういった理由から個人的にはなるべく最初からテンプレートをコードと分離するようにしています。

分離したテンプレートはplain textで書いて正規表現で置換、とかでも良いのですが、どうせ誰かすぐ使うだろうということで、今をときめく Text::Xslate を使ってしまうことにしました。Text::Xslate::Syntax::TTerse を使ってTT2風の構文で記述することにします。

my $tx = Text::Xslate->new(
    syntax    => 'TTerse',
    path      => ['tmpl', 'app/tmpl'],
    cache_dir => '/tmp/app-adventcalendar',
    cache     => 1,
    function  => {
        uri_for => sub {
            my($path, $args) = @_;
            my $uri = $base->clone;
            $uri->path($conf->{base_path} . $uri->path . $path);
            $uri->query_form(@$args) if $args;
            $uri;
        },
    },
);

pathに↑のように複数のディレクトリを指定しておくと、基本的に app/tmpl の下にテンプレートファイルを置くけど、tmpl/ の下に置くとそっちを使うようにする、みたいなことが出来て便利ですね。

シンプルなテンプレートを表示して満足したので明日に続きます。