File Coverage

blib/lib/Dancer2/Core/Dispatcher.pm
Criterion Covered Total %
statement 31 31 100.0
branch 4 4 100.0
condition n/a
subroutine 6 6 100.0
pod 1 1 100.0
total 42 42 100.0


line stmt bran cond sub pod time code
1             package Dancer2::Core::Dispatcher;
2             # ABSTRACT: Class for dispatching request to the appropriate route handler
3             $Dancer2::Core::Dispatcher::VERSION = '0.400001';
4 142     142   1087 use Moo;
  142         429  
  142         976  
5              
6 142     142   48973 use Dancer2::Core::Types;
  142         391  
  142         1192  
7 142     142   1766297 use Dancer2::Core::Request;
  142         390  
  142         3968  
8 142     142   66629 use Dancer2::Core::Response;
  142         574  
  142         48699  
9              
10             has apps => (
11             is => 'rw',
12             isa => ArrayRef,
13             default => sub { [] },
14             );
15              
16             has apps_psgi => (
17             is => 'ro',
18             isa => ArrayRef,
19             lazy => 1,
20             builder => '_build_apps_psgi',
21             );
22              
23             sub _build_apps_psgi {
24 34     34   480 my $self = shift;
25 34         85 return [ map +( $_->name, $_->to_app ), @{ $self->apps } ];
  34         627  
26             }
27              
28             sub dispatch {
29 120     120 1 23228 my ( $self, $env ) = @_;
30 120         214 my @apps = @{ $self->apps_psgi };
  120         2784  
31              
32 120         1302 DISPATCH: while (1) {
33 137         503 for ( my $i = 0; $i < @apps; $i += 2 ) {
34 179         526 my ( $app_name, $app ) = @apps[ $i, $i + 1 ];
35              
36 179         547 my $response = $app->($env);
37              
38             # check for an internal request
39 179 100       8158 delete Dancer2->runner->{'internal_forward'}
40             and next DISPATCH;
41              
42             # the app raised a flag saying it couldn't match anything
43             # which is different than "I matched and it's a 404"
44             delete Dancer2->runner->{'internal_404'}
45 162 100       388 or do {
46 111         308 delete Dancer2->runner->{'internal_request'};
47 111         489 return $response;
48             };
49             }
50              
51             # don't run anymore
52 9         34 delete Dancer2->runner->{'internal_request'};
53 9         100 last;
54             } # while
55              
56             # a 404 on all apps, using the first app
57 9         226 my $default_app = $self->apps->[0];
58 9         84 my $request = $default_app->build_request($env);
59 9         36 return $default_app->response_not_found($request)->to_psgi;
60             }
61              
62             1;
63              
64             __END__
65              
66             =pod
67              
68             =encoding UTF-8
69              
70             =head1 NAME
71              
72             Dancer2::Core::Dispatcher - Class for dispatching request to the appropriate route handler
73              
74             =head1 VERSION
75              
76             version 0.400001
77              
78             =head1 SYNOPSIS
79              
80             use Dancer2::Core::Dispatcher;
81              
82             # Create an instance of dispatcher
83             my $dispatcher = Dancer2::Core::Dispatcher->new( apps => [$app] );
84              
85             # Dispatch a request
86             my $resp = $dispatcher->dispatch($env)->to_psgi;
87              
88             # Capture internal error of a response (if any) after a dispatch
89             $dispatcher->response_internal_error($app, $error);
90              
91             # Capture response not found for an application the after dispatch
92             $dispatcher->response_not_found($env);
93              
94             =head1 ATTRIBUTES
95              
96             =head2 apps
97              
98             The apps is an array reference to L<Dancer2::Core::App>.
99              
100             =head2 default_content_type
101              
102             The default_content_type is a string which represents the context of the
103             request. This attribute is read-only.
104              
105             =head1 METHODS
106              
107             =head2 dispatch
108              
109             The C<dispatch> method accepts the list of applications, hash reference for
110             the B<env> attribute of L<Dancer2::Core::Request> and optionally the request
111             object and an env as input arguments.
112              
113             C<dispatch> returns a response object of L<Dancer2::Core::Response>.
114              
115             Any before hook and matched route code is wrapped to allow DSL keywords such
116             as forward and redirect to short-circuit remaining code, returning across
117             multiple stack frames without having to throw an exception.
118              
119             =head2 response_internal_error
120              
121             The C<response_internal_error> takes as input the list of applications and
122             a variable error and returns an object of L<Dancer2::Core::Error>.
123              
124             =head2 response_not_found
125              
126             The C<response_not_found> consumes as input the list of applications and an
127             object of type L<Dancer2::Core::App> and returns an object
128             L<Dancer2::Core::Error>.
129              
130             =head1 AUTHOR
131              
132             Dancer Core Developers
133              
134             =head1 COPYRIGHT AND LICENSE
135              
136             This software is copyright (c) 2023 by Alexis Sukrieh.
137              
138             This is free software; you can redistribute it and/or modify it under
139             the same terms as the Perl 5 programming language system itself.
140              
141             =cut