Newer Older
204 lines | 5.157kb
add files
Yuki Kimoto authored on 2014-03-26
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