PAGI-Tools
view release on metacpan or search on metacpan
lib/PAGI/App/Cascade.pm view on Meta::CPAN
Entries in C<apps> (and arguments to C<add>) accept anything L<PAGI::Utils/to_app> accepts: a coderef, a component object with a C<to_app> method, or a class name.
=item * C<catch> - Arrayref of status codes to catch (default: [404, 405])
=back
=head1 METHODS
=head2 add($app)
Add an app to the cascade.
=cut
lib/PAGI/Utils.pm view on Meta::CPAN
=item * a class name with a C<to_app> method - auto-required if needed,
then compiled by calling C<< $class->to_app >>
=back
Anything else croaks. A middleware object or class (something with C<wrap>
but no C<to_app>) gets a croak pointing at C<enable()> instead, since
middleware belongs in middleware position, not app position.
All composition points in this distribution (builder mounts, router
targets, cascades, the test client) call this for you, so user code can
pass components and class names directly:
mount '/static' => PAGI::App::File->new(root => $dir);
mount '/api' => 'MyApp::API';
=cut
=head2 is_response
croak "handler did not return a response" unless is_response($value);
t/app/02-routing.t view on Meta::CPAN
};
};
# =============================================================================
# Test: PAGI::App::Cascade
# =============================================================================
subtest 'App::Cascade tries apps in sequence' => sub {
subtest 'returns first non-404 response' => sub {
my $cascade = PAGI::App::Cascade->new(
apps => [
make_response_app(404, 'Not Found'),
make_response_app(200, 'Found'),
make_response_app(200, 'Never Reached'),
],
);
my $app = $cascade->to_app;
my @sent;
run_async(async sub {
await $app->(
{ type => 'http', path => '/test' },
async sub { { type => 'http.disconnect' } },
async sub {
my ($event) = @_; push @sent, $event },
);
});
is $sent[0]{status}, 200, 'returns 200';
is $sent[1]{body}, 'Found', 'correct app responded';
};
subtest 'returns 404 if all apps return 404' => sub {
my $cascade = PAGI::App::Cascade->new(
apps => [
make_response_app(404, 'Not Found 1'),
make_response_app(404, 'Not Found 2'),
],
);
my $app = $cascade->to_app;
my @sent;
run_async(async sub {
await $app->(
{ type => 'http', path => '/test' },
async sub { { type => 'http.disconnect' } },
async sub {
my ($event) = @_; push @sent, $event },
);
});
is $sent[0]{status}, 404, 'returns 404 when all fail';
};
subtest 'custom catch codes' => sub {
my $cascade = PAGI::App::Cascade->new(
apps => [
make_response_app(403, 'Forbidden'),
make_response_app(200, 'Success'),
],
catch => [403, 404],
);
my $app = $cascade->to_app;
my @sent;
run_async(async sub {
await $app->(
{ type => 'http', path => '/test' },
async sub { { type => 'http.disconnect' } },
async sub {
my ($event) = @_; push @sent, $event },
);
});
t/app/02-routing.t view on Meta::CPAN
@sent = ();
$app->({ type => 'http', method => 'GET', path => '/nomatch' },
sub { Future->done }, $send)->get;
is $sent[1]{body}, 'fallback', 'default coerced too';
};
subtest 'Cascade coerces apps list and add()' => sub {
require TestApps::Component;
require PAGI::App::NotFound;
my $cascade = PAGI::App::Cascade->new(
apps => [ PAGI::App::NotFound->new ], # always 404s -> falls through
);
$cascade->add(TestApps::Component->new(body => 'second'));
my $app = $cascade->to_app;
my @sent;
my $send = sub { my ($msg) = @_; push @sent, $msg; Future->done };
$app->({ type => 'http', method => 'GET', path => '/x' },
sub { Future->done }, $send)->get;
is $sent[0]{status}, 200, 'fell through the 404 component';
is $sent[1]{body}, 'second', 'component object added without ->to_app';
};
( run in 1.168 second using v1.01-cache-2.11-cpan-9581c071862 )