NAME
Plack::Builder - OO and DSL to enable Plack Middlewares
SYNOPSIS
# in .psgi
use Plack::Builder;
my $app = sub { ... };
builder {
enable "Deflater";
enable "Session", store => "File";
enable "Debug", panels => [ qw(DBITrace Memory Timer) ];
enable "+My::Plack::Middleware";
$app;
};
# use URLMap
builder {
mount "/foo" => builder {
enable "Foo";
$app;
};
mount "/bar" => $app2;
mount "http://example.com/" => builder { $app3 };
};
# using OO interface
my $builder = Plack::Builder->new;
$builder->add_middleware('Foo', opt => 1);
$builder->add_middleware('Bar');
$builder->wrap($app);
DESCRIPTION
Plack::Builder gives you a quick domain specific language (DSL) to wrap your application with Plack::Middleware subclasses. The middleware you're trying to use should use Plack::Middleware as a base class to use this DSL, inspired by Rack::Builder.
Whenever you call enable
on any middleware, the middleware app is pushed to the stack inside the builder, and then reversed when it actually creates a wrapped application handler. "Plack::Middleware::"
is added as a prefix by default. So:
builder {
enable "Foo";
enable "Bar", opt => "val";
$app;
};
is syntactically equal to:
$app = Plack::Middleware::Bar->wrap($app, opt => "val");
$app = Plack::Middleware::Foo->wrap($app);
In other words, you're supposed to enable
middleware from outer to inner.
INLINE MIDDLEWARE
Plack::Builder allows you to code middleware inline using a nested code reference.
If the first argument to enable
is a code reference, it will be passed an $app
and should return another code reference which is a PSGI application that consumes $env
at runtime. So:
builder {
enable sub {
my $app = shift;
sub {
my $env = shift;
# do preprocessing
my $res = $app->($env);
# do postprocessing
return $res;
};
};
$app;
};
is equal to:
my $mw = sub {
my $app = shift;
sub { my $env = shift; $app->($env) };
};
$app = $mw->($app);
URLMap support
Plack::Builder has a native support for Plack::App::URLMap via the mount
method.
use Plack::Builder;
my $app = builder {
mount "/foo" => $app1;
mount "/bar" => builder {
enable "Foo";
$app2;
};
};
See Plack::App::URLMap's map
method to see what they mean. With builder
you can't use map
as a DSL, for the obvious reason :)
NOTE: Once you use mount
in your builder code, you have to use mount
for all the paths, including the root path (/
). You can't have the default app in the last line of builder
like:
my $app = sub {
my $env = shift;
...
};
builder {
mount "/foo" => sub { ... };
$app; # THIS DOESN'T WORK
};
You'll get warnings saying that your mount configuration will be ignored. Instead you should use mount "/" => ...
in the last line to set the default fallback app.
builder {
mount "/foo" => sub { ... };
mount "/" => $app;
}
Note that the builder
DSL returns a whole new PSGI application, which means
builder { ... }
should normally the last statement of a.psgi
file, because the return value ofbuilder
is the application that is actually executed.You can nest your
builder
blocks, mixed withmount
statements (see "URLMap support" above):builder { mount "/foo" => builder { mount "/bar" => $app; } }
will locate the
$app
under/foo/bar
, since the innerbuilder
block puts it under/bar
and it results in a new PSGI application which is located under/foo
because of the outerbuilder
block.
CONDITIONAL MIDDLEWARE SUPPORT
You can use enable_if
to conditionally enable middleware based on the runtime environment.
builder {
enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' } 'StackTrace', force => 1;
$app;
};
See Plack::Middleware::Conditional for details.
OBJECT ORIENTED INTERFACE
Object oriented interface supports the same functionality with the DSL version in a clearer interface, probably with more typing required.
# With mount
my $builder = Plack::Builder->new;
$builder->add_middleware('Foo', opt => 1);
$builder->mount('/foo' => $foo_app);
$builder->mount('/' => $root_app);
$builder->to_app;
# Nested builders. Equivalent to:
# builder {
# mount '/foo' => builder {
# enable 'Foo';
# $app;
# };
# mount '/' => $app2;
# };
my $builder_out = Plack::Builder->new;
my $builder_in = Plack::Builder->new;
$builder_in->add_middleware('Foo');
$builder_out->mount("/foo" => $builder_in->wrap($app));
$builder_out->mount("/" => $app2);
$builder_out->to_app;
# conditional. You can also directly use Plack::Middleware::Conditional
my $builder = Plack::Builder->new;
$builder->add_middleware_if(sub { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' }, 'StackTrace');
$builder->wrap($app);
SEE ALSO
Plack::Middleware Plack::App::URLMap Plack::Middleware::Conditional