拡張性等を考慮しなければ、以下のように簡単にかくことができる。
View を XSlate きめうち。ルータは given-when で、設定ファイルは .pl という構成。だいたいのウェブアプリケーションは、これぐらいの機能の WAF で十分。ルーター部分は Router::Simple とかにさしかえた方がよいかもだけど。
5年前に Python で似たような記事をみたのだが、今はみんなのがんばりによって Perl でもおなじことができるようになった。
use strict;
use warnings;
use 5.10.1;
package MyWAF;
use parent qw/Class::Data::Inheritable/;
use Path::Class;
use Text::Xslate qw/mark_raw/;
use Encode;
use Time::Piece;
__PACKAGE__->mk_classdata(qw/config/);
__PACKAGE__->mk_classdata(qw/base_dir/);
__PACKAGE__->mk_classdata(qw/response_class/);
__PACKAGE__->mk_classdata(qw/request_class/);
__PACKAGE__->mk_classdata(qw/xslate/);
sub context { die "no context is awaked" }
sub init {
my ($class) = @_;
$class->init_base_dir();
$class->init_config();
$class->init_xslate();
$class->response_class("MyWAF::Response");
$class->request_class("MyWAF::Request");
}
sub init_base_dir {
my ($class) = @_;
$class->base_dir(file(__FILE__)->dir()->parent());
}
sub init_config {
my ($class) = @_;
my $env = $ENV{PLACK_ENV} || 'development';
my $fname = $class->base_dir->file('conf', "$env.pl")->stringify;
my $config = do $fname or die "cannot load configuration file: $fname";
$class->config($config);
}
sub init_xslate {
my ($class, ) = @_;
my $xslate = Text::Xslate->new(
syntax => 'TTerse',
path => $class->base_dir->subdir('tmpl')->stringify,
# cache => 0,
function => {
},
module => [qw/URI::Escape/],
);
$class->xslate($xslate);
}
sub new {
my $class = shift;
my %args = @_==1 ? %{$_[0]} : @_;
bless { %args }, $class;
}
sub DESTROY {
my $self = shift;
if ($self->{dbh}) {
$self->{dbh}->disconnect;
}
}
sub dbh {
my $self = shift;
$self->{dbh} //= DBI->connect(@{$self->config->{DB}}) or die "DBI connection failed";
}
sub render {
my $self = shift;
my $html = $self->xslate->render(@_);
$html = Encode::encode_utf8 $html;
$self->response_class->new(200, ['Content-Length' => length($html), 'Content-Type' => 'text/html; charset=utf-8'], [$html]);
}
sub return_404 {
$self->response_class->new(404, ['Content-Type' => 'text/plain; charset=utf-8'], ['not found']);
}
sub redirect {
my ($self, $location) = @_;
my $res = $self->response_class->new();
$res->redirect($location);
$res;
}
sub show_error {
my ($self, $msg) = @_;
$self->response_class->new(500, ['Content-Type' => 'text/plain; charset=utf-8'], [$msg]);
}
sub req { $_[0]->{req} }
sub handler {
my ($class) = @_;
sub {
my $env = shift;
my $req = $class->request_class->new($env);
my $c = $class->new(req => $req);
local *MyWAF::context = sub { $c };
my $res = $class->handle_request( $c, $req );
return $res->finalize;
};
};
package MyWAF::Request;
use parent qw/Plack::Request/;
package MyWAF::Response;
use parent qw/Plack::Response/;
1;
で、使用例は以下のようになる。
# sample code here.
package MyApp;
use 5.12.1;
use parent qw/MyWAF/;
__PACKAGE__->init();
sub handle_request {
my ($class, $c, $req) = @_;
given ($req->path_info) {
when ('/') {
return $c->render('/');
}
defualt {
return $c->return_404();
}
}
}
# in your myapp.psgi
use 5.12.1;
use MyApp;
MyApp->handler;
なお、この記事は、WAF が簡単にかけるよってことぐらいがいいたいのであって、オレオレフレームワークを推奨しているというわけではない。