Skip to content

Commit

Permalink
added match method to Mojolicious::Routes
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Mar 9, 2014
1 parent 0794b38 commit 50afa0d
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 52 deletions.
1 change: 1 addition & 0 deletions Changes
@@ -1,6 +1,7 @@

4.88 2014-03-09
- Added build_controller method to Mojolicious.
- Added match method to Mojolicious::Routes.
- Improved Mojo::Server::Daemon to handle setuid/setgid errors more
gracefully.
- Improved Mojo::Server::Prefork to handle lock file errors more gracefully.
Expand Down
80 changes: 42 additions & 38 deletions lib/Mojolicious/Routes.pm
Expand Up @@ -48,41 +48,8 @@ sub continue {

sub dispatch {
my ($self, $c) = @_;

# Path (partial path gets priority)
my $req = $c->req;
my $path = $c->stash->{path};
if (defined $path) { $path = "/$path" if $path !~ m!^/! }
else { $path = $req->url->path->to_route }

# Method (HEAD will be treated as GET)
my $method = uc $req->method;
$method = 'GET' if $method eq 'HEAD';

# Check cache
my $cache = $self->cache;
my $ws = $c->tx->is_websocket ? 1 : 0;
my $match = Mojolicious::Routes::Match->new(root => $self);
$c->match($match);
if ($cache && (my $cached = $cache->get("$method:$path:$ws"))) {
$match->endpoint($cached->{endpoint})->stack($cached->{stack});
}

# Check routes
else {
my $options = {method => $method, path => $path, websocket => $ws};
$match->match($c => $options);

# Cache routes without conditions
if ($cache && (my $endpoint = $match->endpoint)) {
my $result = {endpoint => $endpoint, stack => $match->stack};
$cache->set("$method:$path:$ws" => $result)
unless $endpoint->has_conditions;
}
}

return undef unless @{$c->match->stack};
$self->continue($c);
$self->match($c);
@{$c->match->stack} ? $self->continue($c) : return undef;
return 1;
}

Expand All @@ -102,6 +69,37 @@ sub lookup {
return $reverse->{$name} = $route;
}

sub match {
my ($self, $c) = @_;

# Path (partial path gets priority)
my $req = $c->req;
my $path = $c->stash->{path};
if (defined $path) { $path = "/$path" if $path !~ m!^/! }
else { $path = $req->url->path->to_route }

# Method (HEAD will be treated as GET)
my $method = uc $req->method;
$method = 'GET' if $method eq 'HEAD';

# Check cache
my $ws = $c->tx->is_websocket ? 1 : 0;
my $match = Mojolicious::Routes::Match->new(root => $self);
$c->match($match);
my $cache = $self->cache;
my $cached = $cache ? $cache->get("$method:$path:$ws") : undef;
return $match->endpoint($cached->{endpoint})->stack($cached->{stack})
if $cached;

# Check routes
$match->match($c => {method => $method, path => $path, websocket => $ws});

# Cache routes without conditions
return unless $cache && (my $endpoint = $match->endpoint);
my $result = {endpoint => $endpoint, stack => $match->stack};
$cache->set("$method:$path:$ws" => $result) unless $endpoint->has_conditions;
}

sub route {
shift->add_child(Mojolicious::Routes::Route->new(@_))->children->[-1];
}
Expand Down Expand Up @@ -327,14 +325,14 @@ Automatic rendering.
$r->continue(Mojolicious::Controller->new);
Continue dispatch chain.
Continue dispatch chain and emit the hook L<Mojolicious/"around_action"> for
every action.
=head2 dispatch
my $bool = $r->dispatch(Mojolicious::Controller->new);
Match routes with L<Mojolicious::Routes::Match> and dispatch, emits the hook
L<Mojolicious/"around_action"> for every action.
Match routes with L</"match"> and dispatch with L</"continue">.
=head2 hide
Expand All @@ -355,6 +353,12 @@ Check if controller attribute or method is hidden from router.
Find route by name with L<Mojolicious::Routes::Route/"find"> and cache all
results for future lookups.
=head2 match
$r->match(Mojolicious::Controller->new);
Match routes with L<Mojolicious::Routes::Match>.
=head2 route
my $route = $r->route;
Expand Down
39 changes: 25 additions & 14 deletions t/mojolicious/dispatch.t
Expand Up @@ -9,15 +9,12 @@ use Mojo::Base 'Mojolicious::Controller';

has 'render_called';

sub render { shift->render_called(1) }

sub reset_state {
my $self = shift;
$self->render_called(0);
my $stash = $self->stash;
delete @$stash{keys %$stash};
sub new {
shift->SUPER::new(@_)->tap(sub { $_->app->log->level('fatal') });
}

sub render { shift->render_called(1) }

package main;
use Mojo::Base -strict;

Expand Down Expand Up @@ -104,38 +101,52 @@ is_deeply [$c->param], [], 'values are hidden';

# Controller with application and routes
$c = Test::Controller->new;
$c->app->log->level('fatal');
my $d = $c->app->routes;
ok $d, 'initialized';
$d->namespaces(['Test']);
$d->route('/')->over([])->to(controller => 'foo', action => 'home');
$d->route('/foo/(capture)')->to(controller => 'foo', action => 'bar');

# Cache
$c->reset_state;
$c = Test::Controller->new;
my $tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/');
$c->tx($tx);
ok $d->dispatch($c), 'dispatched';
is $c->stash->{controller}, 'foo', 'right value';
is $c->stash->{action}, 'home', 'right value';
is $c->match->stack->[0]{controller}, 'foo', 'right value';
is $c->match->stack->[0]{action}, 'home', 'right value';
ok $c->render_called, 'rendered';
my $cache = $d->cache->get('GET:/:0');
ok $cache, 'route has been cached';
$c->reset_state;
$c = Test::Controller->new;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/');
$c->tx($tx);
$d->match($c);
is $c->stash->{controller}, undef, 'no value';
is $c->stash->{action}, undef, 'no value';
is $c->match->stack->[0]{controller}, 'foo', 'right value';
is $c->match->stack->[0]{action}, 'home', 'right value';
ok !$c->render_called, 'not rendered';
$c = Test::Controller->new;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/');
$c->tx($tx);
ok $d->dispatch($c), 'dispatched';
is $c->stash->{controller}, 'foo', 'right value';
is $c->stash->{action}, 'home', 'right value';
is $c->match->stack->[0]{controller}, 'foo', 'right value';
is $c->match->stack->[0]{action}, 'home', 'right value';
ok $c->render_called, 'rendered';
is_deeply $d->cache->get('GET:/:0'), $cache, 'cached route has been reused';

# 404 clean stash
$c->reset_state;
$c = Test::Controller->new;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/not_found');
Expand All @@ -145,7 +156,7 @@ is_deeply $c->stash, {}, 'empty stash';
ok !$c->render_called, 'nothing rendered';

# No escaping
$c->reset_state;
$c = Test::Controller->new;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('POST');
$tx->req->url->parse('/foo/hello');
Expand Down Expand Up @@ -193,7 +204,7 @@ is_deeply [$c->param], [qw(action bar capture foo)], 'right names';
ok $c->render_called, 'rendered';

# Escaping
$c->reset_state;
$c = Test::Controller->new;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/foo/hello%20there');
Expand All @@ -208,7 +219,7 @@ is $c->param('capture'), 'hello there', 'right value';
ok $c->render_called, 'rendered';

# Escaping UTF-8
$c->reset_state;
$c = Test::Controller->new;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/foo/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82');
Expand Down

0 comments on commit 50afa0d

Please sign in to comment.