1package Dancer2::Core::Dispatcher;
2# ABSTRACT: Class for dispatching request to the appropriate route handler
3$Dancer2::Core::Dispatcher::VERSION = '0.301004';
4use Moo;
5
6use Dancer2::Core::Types;
7use Dancer2::Core::Request;
8use Dancer2::Core::Response;
9
10has apps => (
11    is      => 'rw',
12    isa     => ArrayRef,
13    default => sub { [] },
14);
15
16has apps_psgi => (
17    is      => 'ro',
18    isa     => ArrayRef,
19    lazy    => 1,
20    builder => '_build_apps_psgi',
21);
22
23sub _build_apps_psgi {
24    my $self = shift;
25    return [ map +( $_->name, $_->to_app ), @{ $self->apps } ];
26}
27
28sub dispatch {
29    my ( $self, $env ) = @_;
30    my @apps = @{ $self->apps_psgi };
31
32    DISPATCH: while (1) {
33        for ( my $i = 0; $i < @apps; $i += 2 ) {
34            my ( $app_name, $app ) = @apps[ $i, $i + 1 ];
35
36            my $response = $app->($env);
37
38            # check for an internal request
39            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                or do {
46                    delete Dancer2->runner->{'internal_request'};
47                    return $response;
48                };
49        }
50
51        # don't run anymore
52        delete Dancer2->runner->{'internal_request'};
53        last;
54    } # while
55
56    # a 404 on all apps, using the first app
57    my $default_app = $self->apps->[0];
58    my $request     = $default_app->build_request($env);
59    return $default_app->response_not_found($request)->to_psgi;
60}
61
621;
63
64__END__
65
66=pod
67
68=encoding UTF-8
69
70=head1 NAME
71
72Dancer2::Core::Dispatcher - Class for dispatching request to the appropriate route handler
73
74=head1 VERSION
75
76version 0.301004
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
98The apps is an array reference to L<Dancer2::Core::App>.
99
100=head2 default_content_type
101
102The default_content_type is a string which represents the context of the
103request. This attribute is read-only.
104
105=head1 METHODS
106
107=head2 dispatch
108
109The C<dispatch> method accepts the list of applications, hash reference for
110the B<env> attribute of L<Dancer2::Core::Request> and optionally the request
111object and an env as input arguments.
112
113C<dispatch> returns a response object of L<Dancer2::Core::Response>.
114
115Any before hook and matched route code is wrapped to allow DSL keywords such
116as forward and redirect to short-circuit remaining code, returning across
117multiple stack frames without having to throw an exception.
118
119=head2 response_internal_error
120
121The C<response_internal_error> takes as input the list of applications and
122a variable error and returns an object of L<Dancer2::Core::Error>.
123
124=head2 response_not_found
125
126The C<response_not_found> consumes as input the list of applications and an
127object of type L<Dancer2::Core::App> and returns an object
128L<Dancer2::Core::Error>.
129
130=head1 AUTHOR
131
132Dancer Core Developers
133
134=head1 COPYRIGHT AND LICENSE
135
136This software is copyright (c) 2021 by Alexis Sukrieh.
137
138This is free software; you can redistribute it and/or modify it under
139the same terms as the Perl 5 programming language system itself.
140
141=cut
142