Awesome Template Engine / Framework ぽいの

今日の日記はネタなのでマジレス禁止 (;-P

Awesome Template Engine とか Awesome Framework*1 とか読むと PHP ってのはテンプレートエンジンを内包してるのが強みだなぁと思うわけです。Code Igniter のドキュメントにもそんな感じのことが書いてあったような。

ERB なんかも PHP ぽい構文で書けますよね。コーダーのためのプロトタイピング環境としてうらやましい。

という声も聞こえてきますが,遊びで書いてみました。

package ATE;

use strict;
use v5.8;
use Carp;

my %cache;
our $stash = {};

sub include {
    my $file = shift;
    
    $cache{$file} = _compile_file($file)
        if ! exists $cache{$file};
    
    return &{ $cache{$file} }(@_);
}

sub fetch {
    my $buffer = '';
    open my $handle, '>', \$buffer
        or die $!;
    {
        local *STDOUT = $handle;
        include(@_);
    }
    close $handle;
    
    return $buffer;
}

sub escape_html {
    local $_ = shift;
    s'&'&'go;   s'"'"'go;
    s'<'&lt;'go;    s'>'&gt;'go;
    return $_;
}

sub _compile_file {
    my ($file) = @_;
    
    open my $handle, '<', $file
        or croak "$!: '$file'";
    my $src = _compile_text( do { local $/; <$handle> } );
    close $handle;
    
    my $code = eval "sub { $src }";
    die if $@;
    
    return $code;
}

sub _compile_text {
    my ($text) = @_;
    
    my @tokens = split(m/ <\? ( .*? ) \?> /xmso, $text);
    my $i = 0;
    for (@tokens) {
        if ($i ++ % 2 == 0) {           # Literal
            s'^\n+''o;                  #   trim heading \n
            s'~'\~'go;                  #   escape quote chars
            $_ = "print q~$_~;\n" if $_;
        }
        else {                          # Code
            if (s'^=''o) {              #   <?= CODE ?>    Syntax
                $_ = (s'^raw\s+''o) ? "print do { $_ };\n"
                   :                  "print escape_html(do { $_ });\n"
                   ;
            }
            else {                      #   <? CODE ?>     Syntax
                s'^perl\s+''io;         #   <?perl CODE ?> Syntax
                # AS-IS
            }
        }
    }
    
    return join('', @tokens);
}

1;

ぜんぜん短くねぇ〜&スマートじゃねぇ……

こいつで

% perl -MATE -e "print ATE::fetch('index.html')"

と起動することにすると,あとは index.html をいじるだけでロジックとビューをどちらも触ることができます。分業ハンタイ!

たとえば,

<?perl
#   index.html

    use DBIx::Simple;
    use DBIx::Simple::OO;

    my $db = DBIx::Simple->connect('dbi:SQLite:sample.db');
    my @authors = $db->select(
        'authors', '*',
        { id => { '>', 2 } },
        [ 'id DESC' ],
    )->objects;

?>
<html>
<? include('header.html', 'hello'); ?>
    <body>
        <table>
<?perl foreach my $author (@authors) { ?>
            <tr>
                <td><?= $author->id ?></td>
                <td><?= $author->name ?></td>
            </tr>
<?perl } ?>
        </table>
    </body>
</html>

みたく書けます。include されてる header.html は,

<?perl
    my $title = shift;
?>
<head>
    <title><?= $title ?></title>
</head>

こんな感じで,引数もとれます(グローバル変数 $stash を使ってもいいです)。

あとは,

  • セキュリティ甘甘
  • utf8 フラグ考慮してないー

なところを直して

  • DETACH 例外イベント送出&トラップによる forward() の実装
  • CGI.pm によるクエリの取得
  • クエリや PATH_INFO ベースのルーティング
  • CGI::Session あたりにセッションをおまかせ

を実装すれば,ようやく Awesome Framework においつけるかな……

*1:このリンク先だと結構長くなってますけど,最初のバージョンは1行だったりします