時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

Atelierという俺俺WAFを書いています

なんでこんなものを書いてるんですか

最近はPSGI/Plackが普及してきてかなりWAFが書きやすくなってきたようですね!(僕がWAF触り始めたのは最近ですが。。。)
僕は最近はid:tokuhiromさんのAmon2がいい感じだなーと思って個人的に使っています。


ただ、社内で使うときに、社内での独自拡張が結構あったりとか、
プロジェクトごとの独自拡張が結構あったりとかして、
置き換えるのは結構骨が折れたりします。


例えば、Sledgeを使ったプロジェクトで以下のようなコードがあった場合、
Amon2で同じようなことをするためにはtriggerのあたりをどうするかで、結構骨が折れます。
比較的Sledge likeなid:nekokakさん作のKamuiを使うという手もありますが、
KamuiのControllerのメソッドのprefixはdo_となっていて、これは、
Kamui::Web::Handlerのdispatchメソッドにハードコードされているので、
これはオーバーライドしたりしない限り変えることはできないので、
これもやはり骨が折れます。

package Proj::Pages::Hoge;
use strict;
use warnings;

__PACKAGE__->add_trigger(# Proj::Pages::Hoge以下のすべてのだけ有効にしたい
    'BEFORE_DISPATCH' => sub {
        my $self = shift;

        # code
    },
);

sub dispatch_hoge {
    my $self = shift;

    # code
}

# ...


これら諸々の問題を解決するために、
・Sledgeから移行しやすいWAFがほしい!
って思ったりしたので僕が開発を始めたのがAtelierというWAFです。

っていうのは建前で、俺俺WAF作ってみたかったって思ったのが一番大きな理由だったりします。*1

Atelierってなんぞ?

拙作のSledge likeなインターフェースのPSGIの仕様に沿って作られているWeb Application Frameworkです。アトリエって読みます。
標準ではDispatcherとPages(Controller)しか提供しません。
Atelierは最小限の処理でアプリケーションを動かすために、最小限の機能しか備えていません。
それ以外の必要な機能はすべてPluginから利用するスタイルです。
ViewやTriggerすらもPluginで実装されています。
最小構成でのサンプルコードは以下のようになります。

# app.psgi
use Atelier;

Atelier->create_app(
    app => 'YourApp', # pass your app namespace
);

# YourApp::Dispatcher
package YourApp::Dispatcher;
use Atelier::Dispatcher::RouterSimple;

get '/' => +{
    pages    => 'Root',
    dispatch => 'index',
};
1;

# YourApp::Pages
package YourApp::Pages;
use parent qw/Atelier::Pages/;
1;

# YourApp::Pages::Root
package YourApp::Pages::Root;
use parent qw/YourApp::Pages/;

sub dispatch_index { # "/" or "/index" path route to this dispatch.
    my $self = shift;

    return [
        200,
        [ 'Cotent-Type' => 'text/plain' ],
        [ 'Hello,world!' ],
    ];
}
1;


自由にHTTPレスポンスを作りたい極めて特殊なケースでは、上の例のようにPSGIのレスポンスを直接返す事で問題を解決出来ます。
またPages以下以外から直接例外的にレスポンスを返したい場合はAtelier::Plugin::Exception;のresponseというsuger関数で同様のことができます。(こちらは即時にレスポンスを返します。)

たとえばAtelier::Plugin::Renderer::JSONをPagesでuseして、
出力するデータを$self->stash、(もしくは__PACKAGE__->stash)に格納してあげればそれだけでデータをJSONにしてHTTPのレスポンスとして返すことができます。


Atelier::Plugin::Renderer::TiffanyをPagesでuseしてオプションを与え、
$self->templateに使用するテンプレートへのパスを与え、
出力するデータを$self->stash、(もしくは__PACKAGE__->stash)に格納してあげればそれだけでデータを任意のテンプレートエンジンで処理してHTTPのレスポンスとして返すことができます。


まだまだドキュメントを書けていないので詳しくはソースを読んでみてください。

なんでこんな低機能にしたの?

柔軟性をもたせるためです。


各プロジェクトをs/Slege/OtherWAF/gすることを考えたときに、
・Sledgeで使っていたプラグインはなるべくそのまま使いたい。
・動かなくなったら嫌なのであんまり書き換えたくない。
といった考えがありました。


なので、Sledgeと殆ど同じルールで書けるWAFが欲しかったのです。
なので、本当に最低限の機能だけ実装し、あとはPlugin等で
新たにメソッドをはやしたり、オーバーライドしたりすることで、
Sledgeに近づけていけるようにする。という事が出来るように意識して作りました。


なのでPagesは全体的にオーバーライドしやすくなっていると思います。
目的に応じてオーバーライドして使ってください。

Atelierについて

リポジトリはgithubで公開しています。
https://github.com/karupanerura/p5-Atelier

悪いところは山ほどあると思うので、ぜひ教えてください!
そしてもし良かったらpull reqください><

*1:ぶっちゃけ代替となりうるWAFは既に弊社内にあって実績もあったりするので