/usr/share/perl5/Plack/Builder.pm is in libplack-perl 0.9985-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 | 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?
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};
}
sub to_app {
my($self, $app) = @_;
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 add { Carp::carp("add is deprecated. Use 'enable'"); $_add->(@_) }
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("You used mount() in a builder block, but the last line (app) isn't using mount().\n" .
"This causes all mount() mappings to be ignored. See perldoc Plack::Builder for details.");
} else {
$app = $app->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 "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);
=head1 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 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, 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 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 is supposed to return another code reference
which is PSGI application that consumes C<$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);
=head1 URLMap support
Plack::Builder has a native support for L<Plack::App::URLMap> with C<mount> method.
use Plack::Builder;
my $app = builder {
mount "/foo" => $app1;
mount "/bar" => builder {
enable "Plack::Middleware::Foo";
$app2;
};
};
See L<Plack::App::URLMap>'s C<map> method to see what they mean. With
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
actually is executed.
=item *
You can nest your C<builder> block, mixed with C<mount> (see 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 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. See L<Plack::Middleware::Conditional> for
details.
=head1 SEE ALSO
L<Plack::Middleware> L<Plack::App::URLMap> L<Plack::Middleware::Conditional>
=cut
|