.\" -*- mode: troff; coding: utf-8 -*- .\" Automatically generated by Pod::Man 5.01 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>. .ie n \{\ . ds C` "" . ds C' "" 'br\} .el\{\ . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Plack::Builder 3pm" .TH Plack::Builder 3pm 2024-01-20 "perl v5.38.2" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH NAME Plack::Builder \- OO and DSL to enable Plack Middlewares .SH SYNOPSIS .IX Header "SYNOPSIS" .Vb 2 \& # 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(\*(AqFoo\*(Aq, opt => 1); \& $builder\->add_middleware(\*(AqBar\*(Aq); \& $builder\->wrap($app); .Ve .SH DESCRIPTION .IX Header "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. .PP Whenever you call \f(CW\*(C`enable\*(C'\fR 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. \f(CW"Plack::Middleware::"\fR is added as a prefix by default. So: .PP .Vb 5 \& builder { \& enable "Foo"; \& enable "Bar", opt => "val"; \& $app; \& }; .Ve .PP is syntactically equal to: .PP .Vb 2 \& $app = Plack::Middleware::Bar\->wrap($app, opt => "val"); \& $app = Plack::Middleware::Foo\->wrap($app); .Ve .PP In other words, you're supposed to \f(CW\*(C`enable\*(C'\fR middleware from outer to inner. .SH "INLINE MIDDLEWARE" .IX Header "INLINE MIDDLEWARE" Plack::Builder allows you to code middleware inline using a nested code reference. .PP If the first argument to \f(CW\*(C`enable\*(C'\fR is a code reference, it will be passed an \f(CW$app\fR and should return another code reference which is a PSGI application that consumes \f(CW$env\fR at runtime. So: .PP .Vb 10 \& builder { \& enable sub { \& my $app = shift; \& sub { \& my $env = shift; \& # do preprocessing \& my $res = $app\->($env); \& # do postprocessing \& return $res; \& }; \& }; \& $app; \& }; .Ve .PP is equal to: .PP .Vb 4 \& my $mw = sub { \& my $app = shift; \& sub { my $env = shift; $app\->($env) }; \& }; \& \& $app = $mw\->($app); .Ve .SH "URLMap support" .IX Header "URLMap support" Plack::Builder has a native support for Plack::App::URLMap via the \f(CW\*(C`mount\*(C'\fR method. .PP .Vb 8 \& use Plack::Builder; \& my $app = builder { \& mount "/foo" => $app1; \& mount "/bar" => builder { \& enable "Foo"; \& $app2; \& }; \& }; .Ve .PP See Plack::App::URLMap's \f(CW\*(C`map\*(C'\fR method to see what they mean. With \&\f(CW\*(C`builder\*(C'\fR you can't use \f(CW\*(C`map\*(C'\fR as a DSL, for the obvious reason :) .PP \&\fBNOTE\fR: Once you use \f(CW\*(C`mount\*(C'\fR in your builder code, you have to use \&\f(CW\*(C`mount\*(C'\fR for all the paths, including the root path (\f(CW\*(C`/\*(C'\fR). You can't have the default app in the last line of \f(CW\*(C`builder\*(C'\fR like: .PP .Vb 4 \& my $app = sub { \& my $env = shift; \& ... \& }; \& \& builder { \& mount "/foo" => sub { ... }; \& $app; # THIS DOESN\*(AqT WORK \& }; .Ve .PP You'll get warnings saying that your mount configuration will be ignored. Instead you should use \f(CW\*(C`mount "/" => ...\*(C'\fR in the last line to set the default fallback app. .PP .Vb 4 \& builder { \& mount "/foo" => sub { ... }; \& mount "/" => $app; \& } .Ve .PP Note that the \f(CW\*(C`builder\*(C'\fR DSL returns a whole new PSGI application, which means .IP \(bu 4 \&\f(CW\*(C`builder { ... }\*(C'\fR should normally be the last statement of a \f(CW\*(C`.psgi\*(C'\fR file, because the return value of \f(CW\*(C`builder\*(C'\fR is the application that is actually executed. .IP \(bu 4 You can nest your \f(CW\*(C`builder\*(C'\fR blocks, mixed with \f(CW\*(C`mount\*(C'\fR statements (see "URLMap support" above): .Sp .Vb 5 \& builder { \& mount "/foo" => builder { \& mount "/bar" => $app; \& } \& } .Ve .Sp will locate the \f(CW$app\fR under \f(CW\*(C`/foo/bar\*(C'\fR, since the inner \f(CW\*(C`builder\*(C'\fR block puts it under \f(CW\*(C`/bar\*(C'\fR and it results in a new PSGI application which is located under \f(CW\*(C`/foo\*(C'\fR because of the outer \f(CW\*(C`builder\*(C'\fR block. .SH "CONDITIONAL MIDDLEWARE SUPPORT" .IX Header "CONDITIONAL MIDDLEWARE SUPPORT" You can use \f(CW\*(C`enable_if\*(C'\fR to conditionally enable middleware based on the runtime environment. .PP .Vb 4 \& builder { \& enable_if { $_[0]\->{REMOTE_ADDR} eq \*(Aq127.0.0.1\*(Aq } \*(AqStackTrace\*(Aq, force => 1; \& $app; \& }; .Ve .PP See Plack::Middleware::Conditional for details. .SH "OBJECT ORIENTED INTERFACE" .IX Header "OBJECT ORIENTED INTERFACE" Object oriented interface supports the same functionality with the DSL version in a clearer interface, probably with more typing required. .PP .Vb 6 \& # With mount \& my $builder = Plack::Builder\->new; \& $builder\->add_middleware(\*(AqFoo\*(Aq, opt => 1); \& $builder\->mount(\*(Aq/foo\*(Aq => $foo_app); \& $builder\->mount(\*(Aq/\*(Aq => $root_app); \& $builder\->to_app; \& \& # Nested builders. Equivalent to: \& # builder { \& # mount \*(Aq/foo\*(Aq => builder { \& # enable \*(AqFoo\*(Aq; \& # $app; \& # }; \& # mount \*(Aq/\*(Aq => $app2; \& # }; \& my $builder_out = Plack::Builder\->new; \& my $builder_in = Plack::Builder\->new; \& $builder_in\->add_middleware(\*(AqFoo\*(Aq); \& $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 \*(Aq127.0.0.1\*(Aq }, \*(AqStackTrace\*(Aq); \& $builder\->wrap($app); .Ve .SH "SEE ALSO" .IX Header "SEE ALSO" Plack::Middleware Plack::App::URLMap Plack::Middleware::Conditional