Skip to content

Commit

Permalink
added around_action hook
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Jun 18, 2013
1 parent 17afad4 commit 92fb02f
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 24 deletions.
3 changes: 2 additions & 1 deletion Changes
@@ -1,6 +1,7 @@

4.15 2013-06-18
- Improved router to make the current controller object available to actions
- Added around_action hook.
- Improved ojo to make the current controller object available to actions
as $_. (jberger, sri)
- Fixed a few error reporting bugs in Mojo::IOLoop::Client and
Mojo::IOLoop::Server.
Expand Down
24 changes: 22 additions & 2 deletions lib/Mojolicious.pm
Expand Up @@ -154,6 +154,7 @@ sub handler {

# Dispatcher has to be last in the chain
++$self->{dispatch}
and $self->hook(around_action => sub { $_[2]->($_[1]) })
and $self->hook(around_dispatch => sub { $_[1]->app->dispatch($_[1]) })
unless $self->{dispatch};

Expand Down Expand Up @@ -540,6 +541,25 @@ applications will only work for the application that is rendering.
Useful for rewriting outgoing responses and other post-processing tasks.
(Passed the current controller object)
=item around_action
Emitted right before an action gets invoked and wraps around it, so you have
to manually forward to the next hook if you want to continue the chain.
Default action dispatching is the last hook in the chain, yours will run
before it.
$app->hook(around_action => sub {
my ($next, $c, $action, $last) = @_;
...
return $next->();
});
This is a very powerful hook and should not be used lightly, it allows you for
example to pass additional arguments to actions or handle return values
differently. (Passed a callback leading to the next hook, the current
controller object, the action callback and a flag indicating if this action is
an endpoint)
=item around_dispatch
Emitted right before the C<before_dispatch> hook and wraps around the whole
Expand All @@ -555,8 +575,8 @@ and a call to C<dispatch> the last, yours will be in between.
...
});
This is a very powerful hook and should not be used lightly, it allows you to
customize application wide exception handling for example, consider it the
This is a very powerful hook and should not be used lightly, it allows you for
example to customize application wide exception handling, consider it the
sledgehammer in your toolbox. (Passed a callback leading to the next hook and
the default controller object)
Expand Down
3 changes: 1 addition & 2 deletions lib/Mojolicious/Lite.pm
Expand Up @@ -152,8 +152,7 @@ every change.
Routes are basically just fancy paths that can contain different kinds of
placeholders and usually lead to an action. The first argument passed to all
actions (the invocant C<$self>) is a L<Mojolicious::Controller> object
containing both the HTTP request and response, which, for convenience, is also
available as C<$_>.
containing both the HTTP request and response.
use Mojolicious::Lite;
Expand Down
14 changes: 8 additions & 6 deletions lib/Mojolicious/Routes.pm
Expand Up @@ -59,7 +59,6 @@ sub dispatch {
}
}

# Dispatch
return undef unless $self->_walk($c);
$self->auto_render($c);
return 1;
Expand All @@ -85,6 +84,8 @@ sub route {
shift->add_child(Mojolicious::Routes::Route->new(@_))->children->[-1];
}

sub _action { $_[0]->emit_chain(around_action => $_[1], $_[2], !$_[3]) }

sub _add {
my ($self, $attr, $name, $cb) = @_;
$self->$attr->{$name} = $cb;
Expand All @@ -94,8 +95,9 @@ sub _add {
sub _callback {
my ($self, $c, $field, $nested) = @_;
$c->stash->{'mojo.routed'}++;
$c->app->log->debug('Routing to a callback.');
return $field->{cb}->(local $_ = $c) || !$nested;
my $app = $c->app;
$app->log->debug('Routing to a callback.');
return _action($app->plugins, $c, $field->{cb}, $nested) || !$nested;
}

sub _class {
Expand Down Expand Up @@ -147,7 +149,8 @@ sub _controller {

# Application
my $class = ref $new;
my $log = $old->app->log;
my $app = $old->app;
my $log = $app->log;
if (my $sub = $new->can('handler')) {
$log->debug(qq{Routing to application "$class".});

Expand All @@ -167,7 +170,7 @@ sub _controller {

if (my $sub = $new->can($method)) {
$old->stash->{'mojo.routed'}++ unless $nested;
return 1 if $sub->(local $_ = $new);
return 1 if _action($app->plugins, $new, $sub, $nested);
}

else { $log->debug('Action not found in controller.') }
Expand Down Expand Up @@ -204,7 +207,6 @@ sub _walk {
my @keys = keys %$field;
@{$stash}{@keys} = @{$stash->{'mojo.captures'}}{@keys} = values %$field;

# Dispatch
my $continue
= $field->{cb}
? $self->_callback($c, $field, $nested)
Expand Down
6 changes: 4 additions & 2 deletions lib/ojo.pm
Expand Up @@ -20,6 +20,7 @@ sub import {
my $caller = caller;
eval "package $caller; use Mojolicious::Lite;";
$UA->app($caller->app);
$UA->app->hook(around_action => sub { local $_ = $_[1]; $_[0]->() });

$UA->max_redirects(10) unless defined $ENV{MOJO_MAX_REDIRECTS};
$UA->detect_proxy unless defined $ENV{MOJO_PROXY};
Expand Down Expand Up @@ -81,8 +82,9 @@ L<ojo> implements the following functions.
my $app = a('/hello' => sub { $_->render(json => {hello => 'world'}) });
Create a route with L<Mojolicious::Lite/"any"> and return the current
L<Mojolicious::Lite> object. See also the L<Mojolicious::Lite> tutorial for
more argument variations.
L<Mojolicious::Lite> object. The current controller object is also available
to actions as C<$_>. See also the L<Mojolicious::Lite> tutorial for more
argument variations.
$ perl -Mojo -E 'a("/hello" => {text => "Hello Mojo!"})->start' daemon
Expand Down
36 changes: 29 additions & 7 deletions t/mojolicious/dispatcher_lite_app.t
Expand Up @@ -74,6 +74,27 @@ hook after_static => sub {
$self->res->headers->cache_control('max-age=3600, must-revalidate');
};

# Make controller available as $_
hook around_action => sub {
my ($next, $c) = @_;
local $_ = $c;
return $next->();
};

# Render return value
hook around_action => sub {
my ($next, $c, $action, $last) = @_;
my $value = $next->();
$c->render(text => $value) if $last && $c->stash->{return};
return $value;
};

# Pass argument to action
hook around_action => sub {
my ($next, $c, $action) = @_;
return $c->$action('works');
};

# Response generating condition "res" for /res.txt
app->routes->add_condition(
res => sub {
Expand All @@ -86,20 +107,21 @@ app->routes->add_condition(
}
);

get '/' => sub { shift->render(text => 'works') };

# Never called if custom dispatchers work
get '/custom' => sub { shift->render(text => 'does not work') };

# Custom response
get '/res.txt' => (res => 1) => sub {
my $self = shift;
my $res
= Mojo::Message::Response->new(code => 202)->body('Custom response!');
$self->tx->res($res);
$self->rendered;
$_->tx->res(
Mojo::Message::Response->new(code => 202)->body('Custom response!'));
$_->rendered;
};

# Allow rendering of return value
under '/' => {return => 1} => sub {1};

get sub { return pop };

my $t = Test::Mojo->new;

# Normal route
Expand Down
13 changes: 11 additions & 2 deletions t/mojolicious/external/lib/MyApp.pm
Expand Up @@ -14,10 +14,19 @@ sub startup {
}
);

$r->get('/test' => sub { $_->render(text => $_->config->{whatever}) });
$r->get(
'/test' => sub {
my $self = shift;
$self->render(text => $self->config->{whatever});
}
);

$r->get(
'/secondary' => sub { $_->render(text => ++$_->session->{secondary}) });
'/secondary' => sub {
my $self = shift;
$self->render(text => ++$self->session->{secondary});
}
);
}

1;
5 changes: 4 additions & 1 deletion t/mojolicious/lib/MojoliciousTest/Foo.pm
Expand Up @@ -19,7 +19,10 @@ sub index {
$self->stash(handler => 'xpl', msg => 'Hello World!');
}

sub plugin_camel_case { $_->render(text => $_->some_plugin) }
sub plugin_camel_case {
my $self = shift;
$self->render(text => $self->some_plugin);
}

sub plugin_upper_case {
my $self = shift;
Expand Down
5 changes: 4 additions & 1 deletion t/mojolicious/lite_app.t
Expand Up @@ -43,7 +43,10 @@ get '/☃' => sub {
text => $self->url_for . $self->url_for({}) . $self->url_for('current'));
};

get '/uni/aäb' => sub { $_->render(text => $_->url_for) };
get '/uni/aäb' => sub {
my $self = shift;
$self->render(text => $self->url_for);
};

get '/unicode/:stuff' => sub {
my $self = shift;
Expand Down

0 comments on commit 92fb02f

Please sign in to comment.