YukiWikiMini-1.0.2 PSGI 版をアップデート

id:hyuki さん作の YukiWikiMini-1.0.2 を、二年前に PSGI 版へ翻訳したものを、アップデートしました。

https://gist.github.com/tociyuki/1405146

変更点:

  1. アプリケーションはシングルトンなのでインスタンスを作らなくてもかまいませんが、インスタンス変数を使うためにインスタンスを作るようにしました。
  2. データベースに DB_File の DB_HASH を使います。 TokyoCabinet は止め。
  3. ページ書き換え後にリダイレクトするようにしました。
  4. マークアップHTML5 にしました。ヘッディングのレイアウトをテーブルではなくスタイルシートでおこなうように変えました。
  5. テンプレート置換ルーチンを Liquid 風のものに書き換えました。
  6. テンプレートをコードから分離しました。データ・セクションに置こうかとも考えたのですが、今のところ、ヒアドキュメントにしています。
  7. print_header、print_footer を止めて、response にまとめました。

テンプレート・エンジンは、Lamawiki のものを大幅に簡略化しています。変数はスタッシュのトップレベルから読むだけで、リファレンス・チェーンをたどりません。また、FOR 展開では暗黙のアンダースコア変数を使うことで、短くしています。IF 展開はなく、 FOR 展開を使って IF の真似をさせるようにしています。

sub subst {
    my($self, $t, $e) = @_;
    $t =~ s{\{\{(?:FOR([.]\w)\s(\w+)\}\}\n?(.*?)\{\{ENDFOR\1|(\w+)(?:[ ](\w+))?)\}\}\n?}{
        $1 ? $self->_subst_for($e->{$2}, $3, $e) : escape($e->{$4}, $5 || 'HTMLALL')
    }egmsxo;
    return $t;
}

sub _subst_for {
    my($self, $x, $t, $e) = @_;
    $x = ! defined $x ? [] : ref $x eq 'ARRAY' ? $x : [$x];
    return join q(), map { local $e->{'_'} = $_; $self->subst($t, $e) } @{$x};
}

これを使って、例えば index のリストを次のテンプレートで展開しています。

<ul>
{{FOR.1 list}}<li><a href="{{thisurl}}?{{_ URIALL}}">{{_}}</a></li>
{{ENDFOR.1}}
</ul>

オリジナルの ykwkmini.cgi は 217 行なのに対して、この PSGI 版は 233 行です。テンプレート関連でやや膨らんでいるものの、以前の版よりもオリジナルの雰囲気に近いものになったと考えています。

注意点として、行数を抑えるために、エスケープ処理を担当している escape 関数で HTMLALL と RAW しか実装していません。 URIALL の指定をした場合でも HTMLALL のエスケープしかしないので、 WikiName に 7ビット ASCII のアルファベット以外を許すように拡張するときは、 escape 関数の中に URIALL のエスケープ処理を追加する必要が生じます。