package Mojolicious::Routes::Match; use Mojo::Base -base; has captures => sub { {} }; has [qw(endpoint root)]; has stack => sub { [] }; sub new { my $self = shift->SUPER::new; $self->{method} = uc shift; $self->{path} = shift; $self->{websocket} = shift; return $self; } sub match { my ($self, $r, $c) = @_; # Pattern $self->root($r) unless $self->root; my $path = $self->{path}; my $pattern = $r->pattern; return unless my $captures = $pattern->shape_match(\$path, $r->is_endpoint); $self->{path} = $path; $captures = {%{$self->captures}, %$captures}; # Method if (my $methods = $r->via) { my $method = $self->{method} eq 'HEAD' ? 'GET' : $self->{method}; return unless grep { $_ eq $method } @$methods; } # Conditions if (my $over = $r->over) { my $conditions = $self->{conditions} ||= $self->root->conditions; for (my $i = 0; $i < @$over; $i += 2) { return unless my $condition = $conditions->{$over->[$i]}; return if !$condition->($r, $c, $captures, $over->[$i + 1]); } } # WebSocket return if $r->is_websocket && !$self->{websocket}; # Partial my $empty = !length $path || $path eq '/'; if ($r->partial) { $captures->{path} = $path; $self->endpoint($r); $empty = 1; } # Update stack $self->captures($captures); my $endpoint = $r->is_endpoint; if ($r->inline || ($endpoint && $empty)) { push @{$self->stack}, {%$captures}; delete $captures->{$_} for qw(app cb); } # Endpoint return $self->endpoint($r) if $endpoint && $empty; # Match children my $snapshot = [@{$self->stack}]; for my $child (@{$r->children}) { $self->match($child, $c); # Endpoint found return if $self->endpoint; # Reset $self->{path} = $path; if ($r->parent) { $self->captures($captures)->stack([@$snapshot]) } else { $self->captures({})->stack([]) } } } sub path_for { my $self = shift; # Single argument my (%values, $name); if (@_ == 1) { # Hash %values = %{shift()} if ref $_[0] eq 'HASH'; # Name $name = $_[0] if $_[0]; } # Multiple arguments elsif (@_ > 1) { # Odd if (@_ % 2) { ($name, %values) = (shift, @_) } # Even else { # Name and hash if (ref $_[1] eq 'HASH') { ($name, %values) = (shift, %{shift()}) } # Just values else { %values = @_ } } } # Current route my $endpoint; if ($name && $name eq 'current' || !$name) { return unless $endpoint = $self->endpoint; } # Find endpoint else { return $name unless $endpoint = $self->root->lookup($name) } # Merge values my $captures = $self->captures; %values = (%$captures, format => undef, %values); my $pattern = $endpoint->pattern; $values{format} = defined $captures->{format} ? $captures->{format} : $pattern->defaults->{format} if $pattern->constraints->{format}; # Render my $path = $endpoint->render('', \%values); return wantarray ? ($path, $endpoint->has_websocket) : $path; } 1; =head1 NAME Mojolicious::Routes::Match - Routes visitor =head1 SYNOPSIS use Mojolicious::Controller; use Mojolicious::Routes; use Mojolicious::Routes::Match; # Routes my $r = Mojolicious::Routes->new; $r->get('/foo')->to(action => 'foo'); $r->put('/bar')->to(action => 'bar'); # Match my $c = Mojolicious::Controller->new; my $m = Mojolicious::Routes::Match->new(PUT => '/bar'); $m->match($r, $c); say $m->captures->{action}; =head1 DESCRIPTION L is a visitor for L structures. =head1 ATTRIBUTES L implements the following attributes. =head2 captures my $captures = $m->captures; $m = $m->captures({foo => 'bar'}); Captured parameters. =head2 endpoint my $endpoint = $m->endpoint; $m = $m->endpoint(Mojolicious::Routes->new); The route endpoint that actually matched. =head2 root my $root = $m->root; $m = $m->root($routes); The root of the route tree. =head2 stack my $stack = $m->stack; $m = $m->stack([{foo => 'bar'}]); Captured parameters with nesting history. =head1 METHODS L inherits all methods from L and implements the following new ones. =head2 new my $m = Mojolicious::Routes::Match->new(GET => '/foo'); my $m = Mojolicious::Routes::Match->new(GET => '/foo', $ws); Construct a new L object. =head2 match $m->match(Mojolicious::Routes->new, Mojolicious::Controller->new); Match against a route tree. =head2 path_for my $path = $m->path_for; my $path = $m->path_for(foo => 'bar'); my $path = $m->path_for({foo => 'bar'}); my $path = $m->path_for('named'); my $path = $m->path_for('named', foo => 'bar'); my $path = $m->path_for('named', {foo => 'bar'}); my ($path, $ws) = $m->path_for; my ($path, $ws) = $m->path_for(foo => 'bar'); my ($path, $ws) = $m->path_for({foo => 'bar'}); my ($path, $ws) = $m->path_for('named'); my ($path, $ws) = $m->path_for('named', foo => 'bar'); my ($path, $ws) = $m->path_for('named', {foo => 'bar'}); Render matching route with parameters into path. =head1 SEE ALSO L, L, L. =cut