NAME
Plack::Builder - OO and DSL to enable Plack Middlewares
SYNOPSIS
# in .psgi
use Plack::Builder;
my $app = sub { ... };
builder {
enable "Plack::Middleware::Foo";
enable "Plack::Middleware::Bar", opt => "val";
enable "Plack::Middleware::Baz";
$app;
};
# use URLMap
builder {
mount "/foo" => builder {
enable "Plack::Middleware::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);
$app = $builder->mount('/app' => $app);
$app = $builder->to_app($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, so:
builder {
enable "Plack::Middleware::Foo";
enable "Plack::Middleware::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 is supposed to return another code reference which is PSGI application that consumes $env
in 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 with mount
method.
use Plack::Builder;
my $app = builder {
mount "/foo" => $app1;
mount "/bar" => builder {
enable "Plack::Middleware::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 actually is executed.You can nest your
builder
block, mixed withmount
(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 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. See Plack::Middleware::Conditional for details.
SEE ALSO
Plack::Middleware Plack::App::URLMap Plack::Middleware::Conditional