1 | package Mojolicious::Routes::Match;
|
---|
2 | use Mojo::Base -base;
|
---|
3 |
|
---|
4 | use Mojo::Util;
|
---|
5 |
|
---|
6 | has [qw(endpoint root)];
|
---|
7 | has position => 0;
|
---|
8 | has stack => sub { [] };
|
---|
9 |
|
---|
10 | sub find { $_[0]->_match($_[0]->root, $_[1], $_[2]) }
|
---|
11 |
|
---|
12 | sub path_for {
|
---|
13 | my ($self, $name, %values) = (shift, Mojo::Util::_options(@_));
|
---|
14 |
|
---|
15 | # Current route
|
---|
16 | my $route;
|
---|
17 | if (!$name || $name eq 'current') {
|
---|
18 | return {} unless $route = $self->endpoint;
|
---|
19 | }
|
---|
20 |
|
---|
21 | # Find endpoint
|
---|
22 | else { return {path => $name} unless $route = $self->root->lookup($name) }
|
---|
23 |
|
---|
24 | # Merge values (clear format)
|
---|
25 | my $captures = $self->stack->[-1] || {};
|
---|
26 | %values = (%$captures, format => undef, %values);
|
---|
27 | my $pattern = $route->pattern;
|
---|
28 | $values{format}
|
---|
29 | //= defined $captures->{format}
|
---|
30 | ? $captures->{format}
|
---|
31 | : $pattern->defaults->{format}
|
---|
32 | if $pattern->constraints->{format};
|
---|
33 |
|
---|
34 | my $path = $route->render(\%values);
|
---|
35 | return {path => $path, websocket => $route->has_websocket};
|
---|
36 | }
|
---|
37 |
|
---|
38 | sub _match {
|
---|
39 | my ($self, $r, $c, $options) = @_;
|
---|
40 |
|
---|
41 | # Pattern
|
---|
42 | my $path = $options->{path};
|
---|
43 | my $partial = $r->partial;
|
---|
44 | my $detect = (my $endpoint = $r->is_endpoint) && !$partial;
|
---|
45 | return undef
|
---|
46 | unless my $captures = $r->pattern->match_partial(\$path, $detect);
|
---|
47 | local $options->{path} = $path;
|
---|
48 | local @{$self->{captures} ||= {}}{keys %$captures} = values %$captures;
|
---|
49 | $captures = $self->{captures};
|
---|
50 |
|
---|
51 | # Method
|
---|
52 | my $methods = $r->via;
|
---|
53 | return undef if $methods && !grep { $_ eq $options->{method} } @$methods;
|
---|
54 |
|
---|
55 | # Conditions
|
---|
56 | if (my $over = $r->over) {
|
---|
57 | my $conditions = $self->{conditions} ||= $self->root->conditions;
|
---|
58 | for (my $i = 0; $i < @$over; $i += 2) {
|
---|
59 | return undef unless my $condition = $conditions->{$over->[$i]};
|
---|
60 | return undef if !$condition->($r, $c, $captures, $over->[$i + 1]);
|
---|
61 | }
|
---|
62 | }
|
---|
63 |
|
---|
64 | # WebSocket
|
---|
65 | return undef if $r->is_websocket && !$options->{websocket};
|
---|
66 |
|
---|
67 | # Partial
|
---|
68 | my $empty = !length $path || $path eq '/';
|
---|
69 | if ($partial) {
|
---|
70 | $captures->{path} = $path;
|
---|
71 | $self->endpoint($r);
|
---|
72 | $empty = 1;
|
---|
73 | }
|
---|
74 |
|
---|
75 | # Endpoint (or intermediate destination)
|
---|
76 | if (($endpoint && $empty) || $r->inline) {
|
---|
77 | push @{$self->stack}, {%$captures};
|
---|
78 | if ($endpoint && $empty) {
|
---|
79 | my $format = $captures->{format};
|
---|
80 | if ($format) { $_->{format} = $format for @{$self->stack} }
|
---|
81 | return !!$self->endpoint($r);
|
---|
82 | }
|
---|
83 | delete @$captures{qw(app cb)};
|
---|
84 | }
|
---|
85 |
|
---|
86 | # Match children
|
---|
87 | my $snapshot = $r->parent ? [@{$self->stack}] : [];
|
---|
88 | for my $child (@{$r->children}) {
|
---|
89 | return 1 if $self->_match($child, $c, $options);
|
---|
90 | $self->stack([@$snapshot]);
|
---|
91 | }
|
---|
92 | }
|
---|
93 |
|
---|
94 | 1;
|
---|
95 |
|
---|
96 | =encoding utf8
|
---|
97 |
|
---|
98 | =head1 NAME
|
---|
99 |
|
---|
100 | Mojolicious::Routes::Match - Find routes
|
---|
101 |
|
---|
102 | =head1 SYNOPSIS
|
---|
103 |
|
---|
104 | use Mojolicious::Controller;
|
---|
105 | use Mojolicious::Routes;
|
---|
106 | use Mojolicious::Routes::Match;
|
---|
107 |
|
---|
108 | # Routes
|
---|
109 | my $r = Mojolicious::Routes->new;
|
---|
110 | $r->get('/:controller/:action');
|
---|
111 | $r->put('/:controller/:action');
|
---|
112 |
|
---|
113 | # Match
|
---|
114 | my $c = Mojolicious::Controller->new;
|
---|
115 | my $match = Mojolicious::Routes::Match->new(root => $r);
|
---|
116 | $match->find($c => {method => 'PUT', path => '/foo/bar'});
|
---|
117 | say $match->stack->[0]{controller};
|
---|
118 | say $match->stack->[0]{action};
|
---|
119 |
|
---|
120 | # Render
|
---|
121 | say $match->path_for->{path};
|
---|
122 | say $match->path_for(action => 'baz')->{path};
|
---|
123 |
|
---|
124 | =head1 DESCRIPTION
|
---|
125 |
|
---|
126 | L<Mojolicious::Routes::Match> finds routes in L<Mojolicious::Routes>
|
---|
127 | structures.
|
---|
128 |
|
---|
129 | =head1 ATTRIBUTES
|
---|
130 |
|
---|
131 | L<Mojolicious::Routes::Match> implements the following attributes.
|
---|
132 |
|
---|
133 | =head2 endpoint
|
---|
134 |
|
---|
135 | my $route = $match->endpoint;
|
---|
136 | $match = $match->endpoint(Mojolicious::Routes::Route->new);
|
---|
137 |
|
---|
138 | The route endpoint that matched, usually a L<Mojolicious::Routes::Route>
|
---|
139 | object.
|
---|
140 |
|
---|
141 | =head2 position
|
---|
142 |
|
---|
143 | my $position = $match->position;
|
---|
144 | $match = $match->position(2);
|
---|
145 |
|
---|
146 | Current position on the L</"stack">, defaults to C<0>.
|
---|
147 |
|
---|
148 | =head2 root
|
---|
149 |
|
---|
150 | my $root = $match->root;
|
---|
151 | $match = $match->root(Mojolicious::Routes->new);
|
---|
152 |
|
---|
153 | The root of the route structure, usually a L<Mojolicious::Routes> object.
|
---|
154 |
|
---|
155 | =head2 stack
|
---|
156 |
|
---|
157 | my $stack = $match->stack;
|
---|
158 | $match = $match->stack([{action => 'foo'}, {action => 'bar'}]);
|
---|
159 |
|
---|
160 | Captured parameters with nesting history.
|
---|
161 |
|
---|
162 | =head1 METHODS
|
---|
163 |
|
---|
164 | L<Mojolicious::Routes::Match> inherits all methods from L<Mojo::Base> and
|
---|
165 | implements the following new ones.
|
---|
166 |
|
---|
167 | =head2 find
|
---|
168 |
|
---|
169 | $match->find(Mojolicious::Controller->new, {method => 'GET', path => '/'});
|
---|
170 |
|
---|
171 | Match controller and options against L</"root"> to find an appropriate
|
---|
172 | L</"endpoint">.
|
---|
173 |
|
---|
174 | =head2 path_for
|
---|
175 |
|
---|
176 | my $info = $match->path_for;
|
---|
177 | my $info = $match->path_for(foo => 'bar');
|
---|
178 | my $info = $match->path_for({foo => 'bar'});
|
---|
179 | my $info = $match->path_for('named');
|
---|
180 | my $info = $match->path_for('named', foo => 'bar');
|
---|
181 | my $info = $match->path_for('named', {foo => 'bar'});
|
---|
182 |
|
---|
183 | Render matching route with parameters into path.
|
---|
184 |
|
---|
185 | =head1 SEE ALSO
|
---|
186 |
|
---|
187 | L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
|
---|
188 |
|
---|
189 | =cut
|
---|