Hatena::Groupangelos

Angelos in Action RSSフィード

Fork me on GitHub
 | 

2009-10-01

waf with plack

02:45 | waf with plack - Angelos in Action を含むブックマーク はてなブックマーク - waf with plack - Angelos in Action waf with plack - Angelos in Action のブックマークコメント

middlewareまわりもAPIがでそろいつつあるので、Plack周りが一段落したら、以下の形にかきかえようかなぁと。

やってることは殆ど同じなんだけど、こうしたほうがわかりやすいかもなぁと。

package Angelos::Plack::Application;
use base Plack::Middleware;

call(env) 
   req = Angelos::Request->new(env);
   res = Angelos::Dispatcher->handler_request(req)
   res->finalize

merbをみて少し思ったり

TODO

02:30 | TODO - Angelos in Action を含むブックマーク はてなブックマーク - TODO - Angelos in Action TODO - Angelos in Action のブックマークコメント

  • Plack::Responseのpatch

Plack::BuilderのDSL部分とAPI部分(middleware組み立て部分)を分離したい!

02:12 | Plack::BuilderのDSL部分とAPI部分(middleware組み立て部分)を分離したい! - Angelos in Action を含むブックマーク はてなブックマーク - Plack::BuilderのDSL部分とAPI部分(middleware組み立て部分)を分離したい! - Angelos in Action Plack::BuilderのDSL部分とAPI部分(middleware組み立て部分)を分離したい! - Angelos in Action のブックマークコメント

Plack::Builderで、条件に応じてMiddlewareを読み込みたいなぁと思ったんですが、今のAPIだとPlack::Middlewareの以下のコードでuseするので、条件に応じてMiddlewareをロードするというのが少しやりにくいなぁと思ってました(これが一つのPlackX::Engineを書く動機の一つでした)

use Plack::Middleware qw(Static);

そこでDSLAPI部分(middleware組み立て部分)を分離してみました。

例えば、以下のようにPlack::BuilderAPI(Plack::Builder) と DSL(Plack::Builder::Declare)に分離してあると、 WAF開発者はPlack::Builder(API)を使って、middlewareを組み立てていけばいいし、利用者はDSLで簡易にかけるので便利かなぁと。

WAF開発者という立場で考えると、Plack::Builder(API)のほうだけが必要で、DSLはあまり必要ではないです。一方、アプリケーション開発者の視点で考えると、簡易にかつ明示的にかけるほうがよく、DSLがあったほうがいいです。

ただ、DSLだと、WAF側のconfig、例えばYAMLなどに書いてある場合、順にmiddlewareをロードしてwrapするコードが書けない(?)んですが、Plack::Builderであれば、add_middlewareで順に追加していって、最後にto_appすればいいだけなので、組み立てやすいかなぁと。その点で、WAF開発者にはありがたいですね。

- middlewares
  - Static
  - AccessLog::Timed

DSL部分とAPI部分は、分離されている方が開発者に取っては往々に使いやすい事が多いので、分離しておいたほうがいいかな。なので、DSLからAPIに対して以上するような形で、実装しておくと誰もが幸せになれるんじゃないかと思いました。



以下コードです

package Plack::Builder;
use strict;
use warnings;
use base qw/Class::Accessor::Fast/;
__PACKAGE__->mk_accessors(qw/middlewares/);

sub new {
    my ($class, $middlewares) = @_; 
    $middlewares ||= []; 
    my $self = bless { middlewares => $middlewares }, $class;
    return $self;
}

sub add_middleware {
    my ( $self, $middleware_name, $middleware ) = @_; 
    eval "use $middleware_name";
    die $@ if $@; 

    push @{$self->{middlewares}}, $middleware;
}

sub to_app {
    my ($self, $app) = @_; 
    for my $mw (reverse @{$self->{middlewares}}) {
        $app = $mw->($app);
    }   
    $app;
}

1;
package Plack::Builder::Declare;
use strict;
use Plack::Builder;
 
sub import {
    my $caller = caller;
 
    no strict 'refs';
    no warnings 'redefine';
 
    *{ $caller . '::builder' } = \&builder;
    *{ $caller . '::enable' }  = \&enable;
}
 
sub builder(&) {
    my $block = shift;
 
    my $builder = Plack::Builder->new;
    no warnings 'redefine';
 
    local *enable = enable_middleware($builder);
 
    my $app = $block->();
    $builder->to_app($app);
}
 
sub enable_middleware {
    my $builder = shift;
 
    return sub {
        my ($middleware, @args) = @_; 
        $builder->add_middleware( $middleware, sub { $middleware->wrap(@args, $_[0]); }); 
    };  
}
 
1;
use Plack::Builder::Declare;

my $app = sub {
    return [ 200, [ "Content-Type" => "text/plain", "Content-Length" => 11 ], [ "Hello World" ] ];
};

builder {
    enable 'Plack::Middleware::Static';
    $app;
};

こうなっているとBuilder部分のコードを共有しつつ、DSLでもAPIでもconditionalなmiddlewareの読み込みをサポートできるんじゃないかなぁと。

# 個人的な思いとして、PlackそのものがWAF開発者含めて幅広いユーザーに使われるものであってほしいと思っているので、APIについては使ってみてfeedbackをしようかなと思ってます。

 |