Tatsumaki ライクな micro WAF "Lanky" を作ってみた

Twiggy を使う場合は Tatsumaki があって簡単に書けるけれども Starman / Starlet 使うときにも似たようにかけたらいいなぁ、ということで似たように書けるものを作ってみました。
モデルは実装してないので(どの ORM を使うかは TIMTOWTDI でどれ採用すべきか迷ったので)正確には WAF ではないと思うのだけれど、とりあえず Tatsumaki と似たことはできるようにしたつもりです。なにか意見いただけると嬉しいです

https://github.com/punytan/Lanky

Tatsumaki のように全部ひとつのファイルに突っ込むことも出来るし、別々のファイルに分割して書けるようにもしてあります。

t/02_app をみてもらえるとすぐわかると思うんですが、下記のように書けるようになっております。

ひとつのファイルで書く場合

errordoc, htdocs, template ディレクトリを準備して、

$ tree 
.
|-- errordoc
|   `-- 404
|-- htdocs
|   |-- favicon.ico
|   |-- foo.htlm
|   `-- static
|       `-- index.html
|-- template
|   `-- foo.xt
`-- united.t
united.t

こんな風に書いたり、

use strict;

package RootHandler;
use parent 'Lanky::Handler';
use Data::Dumper;

sub get {
    my ($self) = @_;
    my $body = $self->render('foo.xt', {foo => 'testing'});

    my $res = $self->request->new_response(200);
    $res->content_type('text/html');
    $res->body($body);
    $res->finalize;
}

1;

package LoginHandler;
use parent 'Lanky::Handler';
use Data::Dumper;

sub get {
    my ($self) = @_;
    my $body = $self->render('foo.xt', {foo => 'testing'});

    my $res = $self->request->new_response(200);
    $res->content_type('text/html');
    $res->body($body);
    $res->finalize;
}

1;

package main;
use Test::More;
use Plack::Test;
use HTTP::Request::Common;

use File::Spec;
use File::Basename;

use Lanky;
use Plack::Builder;

my $basedir = File::Basename::dirname(__FILE__);

my $lanky = Lanky->new(
    application => [
        '/' => 'RootHandler',
        '/login' => 'LoginHandler',
    ],
    template => [
        path => ["$basedir/template"],
        cache => 1,
        cache_dir => File::Spec->tmpdir,
    ],
    errordoc_path => "$basedir/errordoc",
    render_encoding => 'utf8',
);

my $app = builder {
    enable "Plack::Middleware::Static",
        path => sub { s{^/(?:(favicon\.ico)|static/)}{$1||''}e },
        root => "$basedir/htdocs/";
    $lanky->to_app;
};

test_psgi $app, sub {
    my $cb = shift;
    my $res = $cb->(GET "/");
    is $res->content, "testingいろはにほへと\n";

    $res = $cb->(GET "/login");
    is $res->content, "testingいろはにほへと\n";

    $res = $cb->(GET "/xxx");
    is $res->content, "みつからないよ!\n";
    is $res->code, 404;
};

done_testing;

別々のファイルに書く場合

lib ディレクトリを作って @INC に ./lib を突っ込んで、

$ tree 
.
|-- errordoc
|   `-- 404
|-- htdocs
|   |-- favicon.ico
|   |-- foo.htlm
|   `-- static
|       `-- index.html
|-- lib
|   `-- MyApp
|       `-- C
|           |-- Login.pm
|           `-- Root.pm
|-- template
|   `-- foo.xt
`-- divided.t
divided.t

こんな風に書いて、 MyApp::C::Root で上と同じように use parent 'Lanky::Handler'; してやれば、 $self から諸々呼べるようになっています

use strict;
use Test::More;
use Plack::Test;
use HTTP::Request::Common;

use File::Spec;
use File::Basename;
use lib File::Basename::dirname(__FILE__) . "/lib";

use Lanky;
use Plack::Builder;

my $basedir = File::Basename::dirname(__FILE__);

my $lanky = Lanky->new(
    application => [
        '/' => 'MyApp::C::Root',
        '/login' => 'MyApp::C::Login',
    ],
    template => [
        path => ["$basedir/template"],
        cache => 1,
        cache_dir => File::Spec->tmpdir,
    ],
    errordoc_path => "$basedir/errordoc",
    render_encoding => 'utf8',
);

my $app = builder {
    enable "Plack::Middleware::Static",
        path => sub { s{^/(?:(favicon\.ico)|static/)}{$1||''}e },
        root => "$basedir/htdocs/";
    $lanky->to_app;
};

test_psgi $app, sub {
    my $cb = shift;
    my $res = $cb->(GET "/");
    is $res->content, "testingいろはにほへと\n";

    $res = $cb->(GET "/login");
    is $res->content, "testingいろはにほへと\n";

    $res = $cb->(GET "/xxx");
    is $res->content, "みつからないよ!\n";
    is $res->code, 404;
};

done_testing;