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