summaryrefslogtreecommitdiff
path: root/lib/Dancer2/Core/Dispatcher.pm
blob: dc477b9cad8466c4c29bfd41d27c205258bd1e28 (plain)
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
package Dancer2::Core::Dispatcher;
# ABSTRACT: Class for dispatching request to the appropriate route handler
$Dancer2::Core::Dispatcher::VERSION = '1.1.0';
use Moo;

use Dancer2::Core::Types;
use Dancer2::Core::Request;
use Dancer2::Core::Response;

has apps => (
    is      => 'rw',
    isa     => ArrayRef,
    default => sub { [] },
);

has apps_psgi => (
    is      => 'ro',
    isa     => ArrayRef,
    lazy    => 1,
    builder => '_build_apps_psgi',
);

sub _build_apps_psgi {
    my $self = shift;
    return [ map +( $_->name, $_->to_app ), @{ $self->apps } ];
}

sub dispatch {
    my ( $self, $env ) = @_;
    my @apps = @{ $self->apps_psgi };

    DISPATCH: while (1) {
        for ( my $i = 0; $i < @apps; $i += 2 ) {
            my ( $app_name, $app ) = @apps[ $i, $i + 1 ];

            my $response = $app->($env);

            # check for an internal request
            delete Dancer2->runner->{'internal_forward'}
                and next DISPATCH;

            # the app raised a flag saying it couldn't match anything
            # which is different than "I matched and it's a 404"
            delete Dancer2->runner->{'internal_404'}
                or do {
                    delete Dancer2->runner->{'internal_request'};
                    return $response;
                };
        }

        # don't run anymore
        delete Dancer2->runner->{'internal_request'};
        last;
    } # while

    # a 404 on all apps, using the first app
    my $default_app = $self->apps->[0];
    my $request     = $default_app->build_request($env);
    return $default_app->response_not_found($request)->to_psgi;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Dancer2::Core::Dispatcher - Class for dispatching request to the appropriate route handler

=head1 VERSION

version 1.1.0

=head1 SYNOPSIS

    use Dancer2::Core::Dispatcher;

    # Create an instance of dispatcher
    my $dispatcher = Dancer2::Core::Dispatcher->new( apps => [$app] );

    # Dispatch a request
    my $resp = $dispatcher->dispatch($env)->to_psgi;

    # Capture internal error of a response (if any) after a dispatch
    $dispatcher->response_internal_error($app, $error);

    # Capture response not found for an application the after dispatch
    $dispatcher->response_not_found($env);

=head1 ATTRIBUTES

=head2 apps

The apps is an array reference to L<Dancer2::Core::App>.

=head2 default_content_type

The default_content_type is a string which represents the context of the
request. This attribute is read-only.

=head1 METHODS

=head2 dispatch

The C<dispatch> method accepts the list of applications, hash reference for
the B<env> attribute of L<Dancer2::Core::Request> and optionally the request
object and an env as input arguments.

C<dispatch> returns a response object of L<Dancer2::Core::Response>.

Any before hook and matched route code is wrapped to allow DSL keywords such
as forward and redirect to short-circuit remaining code, returning across
multiple stack frames without having to throw an exception.

=head2 response_internal_error

The C<response_internal_error> takes as input the list of applications and
a variable error and returns an object of L<Dancer2::Core::Error>.

=head2 response_not_found

The C<response_not_found> consumes as input the list of applications and an
object of type L<Dancer2::Core::App> and returns an object
L<Dancer2::Core::Error>.

=head1 AUTHOR

Dancer Core Developers

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2023 by Alexis Sukrieh.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut