/usr/share/perl5/Plack/Builder.pm is in libplack-perl 1.0030-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | package Plack::Builder;
use strict;
use parent qw( Exporter );
our @EXPORT = qw( builder add enable enable_if mount );
use Carp ();
use Plack::App::URLMap;
use Plack::Middleware::Conditional; # TODO delayed load?
use Scalar::Util ();
sub new {
my $class = shift;
bless { middlewares => [ ] }, $class;
}
sub add_middleware {
my($self, $mw, @args) = @_;
if (ref $mw ne 'CODE') {
my $mw_class = Plack::Util::load_class($mw, 'Plack::Middleware');
$mw = sub { $mw_class->wrap($_[0], @args) };
}
push @{$self->{middlewares}}, $mw;
}
sub add_middleware_if {
my($self, $cond, $mw, @args) = @_;
if (ref $mw ne 'CODE') {
my $mw_class = Plack::Util::load_class($mw, 'Plack::Middleware');
$mw = sub { $mw_class->wrap($_[0], @args) };
}
push @{$self->{middlewares}}, sub {
Plack::Middleware::Conditional->wrap($_[0], condition => $cond, builder => $mw);
};
}
# do you want remove_middleware() etc.?
sub _mount {
my ($self, $location, $app) = @_;
if (!$self->{_urlmap}) {
$self->{_urlmap} = Plack::App::URLMap->new;
}
$self->{_urlmap}->map($location => $app);
$self->{_urlmap}; # for backward compat.
}
sub to_app {
my($self, $app) = @_;
if ($app) {
$self->wrap($app);
} elsif ($self->{_urlmap}) {
$self->{_urlmap} = $self->{_urlmap}->to_app
if Scalar::Util::blessed($self->{_urlmap});
$self->wrap($self->{_urlmap});
} else {
Carp::croak("to_app() is called without mount(). No application to build.");
}
}
sub wrap {
my($self, $app) = @_;
if ($self->{_urlmap} && $app ne $self->{_urlmap}) {
Carp::carp("WARNING: wrap() and mount() can't be used altogether in Plack::Builder.\n" .
"WARNING: This causes all previous mount() mappings to be ignored.");
}
for my $mw (reverse @{$self->{middlewares}}) {
$app = $mw->($app);
}
$app;
}
# DSL goes here
our $_add = our $_add_if = our $_mount = sub {
Carp::croak("enable/mount should be called inside builder {} block");
};
sub enable { $_add->(@_) }
sub enable_if(&$@) { $_add_if->(@_) }
sub mount {
my $self = shift;
if (Scalar::Util::blessed($self)) {
$self->_mount(@_);
}else{
$_mount->($self, @_);
}
}
sub builder(&) {
my $block = shift;
my $self = __PACKAGE__->new;
my $mount_is_called;
my $urlmap = Plack::App::URLMap->new;
local $_mount = sub {
$mount_is_called++;
$urlmap->map(@_);
$urlmap;
};
local $_add = sub {
$self->add_middleware(@_);
};
local $_add_if = sub {
$self->add_middleware_if(@_);
};
my $app = $block->();
if ($mount_is_called) {
if ($app ne $urlmap) {
Carp::carp("WARNING: You used mount() in a builder block, but the last line (app) isn't using mount().\n" .
"WARNING: This causes all mount() mappings to be ignored.\n");
} else {
$app = $app->to_app;
}
}
$app = $app->to_app if $app and Scalar::Util::blessed($app) and $app->can('to_app');
$self->to_app($app);
}
1;
__END__
=head1 NAME
Plack::Builder - OO and DSL to enable Plack Middlewares
=head1 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);
=head1 DESCRIPTION
Plack::Builder gives you a quick domain specific language (DSL) to
wrap your application with L<Plack::Middleware> subclasses. The
middleware you're trying to use should use L<Plack::Middleware> as a
base class to use this DSL, inspired by Rack::Builder.
Whenever you call C<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. C<"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 C<enable> middleware from outer to inner.
=head1 INLINE MIDDLEWARE
Plack::Builder allows you to code middleware inline using a nested
code reference.
If the first argument to C<enable> is a code reference, it will be
passed an C<$app> and should return another code reference
which is a PSGI application that consumes C<$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);
=head1 URLMap support
Plack::Builder has a native support for L<Plack::App::URLMap> via the C<mount> method.
use Plack::Builder;
my $app = builder {
mount "/foo" => $app1;
mount "/bar" => builder {
enable "Foo";
$app2;
};
};
See L<Plack::App::URLMap>'s C<map> method to see what they mean. With
C<builder> you can't use C<map> as a DSL, for the obvious reason :)
B<NOTE>: Once you use C<mount> in your builder code, you have to use
C<mount> for all the paths, including the root path (C</>). You can't
have the default app in the last line of C<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 C<< mount "/" => ... >> in the last
line to set the default fallback app.
builder {
mount "/foo" => sub { ... };
mount "/" => $app;
}
Note that the C<builder> DSL returns a whole new PSGI application, which means
=over 4
=item *
C<builder { ... }> should normally the last statement of a C<.psgi>
file, because the return value of C<builder> is the application that
is actually executed.
=item *
You can nest your C<builder> blocks, mixed with C<mount> statements (see L</"URLMap support">
above):
builder {
mount "/foo" => builder {
mount "/bar" => $app;
}
}
will locate the C<$app> under C</foo/bar>, since the inner C<builder>
block puts it under C</bar> and it results in a new PSGI application
which is located under C</foo> because of the outer C<builder> block.
=back
=head1 CONDITIONAL MIDDLEWARE SUPPORT
You can use C<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 L<Plack::Middleware::Conditional> for details.
=head1 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);
=head1 SEE ALSO
L<Plack::Middleware> L<Plack::App::URLMap> L<Plack::Middleware::Conditional>
=cut
|