Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
added around_exception and around_not_found hooks
  • Loading branch information
kraih committed Jul 4, 2012
1 parent b973403 commit e9fc8ed
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 90 deletions.
3 changes: 2 additions & 1 deletion Changes
@@ -1,5 +1,6 @@

3.03 2012-07-04
3.03 2012-07-05
- Added around_exception and around_not_found hooks.
- Improved documentation.
- Improved tests.
- Fixed small namespace detection bug in Mojo::DOM.
Expand Down
114 changes: 103 additions & 11 deletions lib/Mojolicious.pm
Expand Up @@ -3,6 +3,7 @@ use Mojo::Base 'Mojo';

use Carp 'croak';
use Mojo::Exception;
use Mojo::Home;
use Mojolicious::Commands;
use Mojolicious::Controller;
use Mojolicious::Plugins;
Expand Down Expand Up @@ -36,6 +37,15 @@ has types => sub { Mojolicious::Types->new };
our $CODENAME = 'Rainbow';
our $VERSION = '3.03';

# Bundled templates
our $H = Mojo::Home->new;
$H->parse($H->parse($H->mojo_lib_dir)->rel_dir('Mojolicious/templates'));
our $MOJOBAR = $H->slurp_rel_file('mojobar.html.ep');
my $EXCEPTION = $H->slurp_rel_file('exception.html.ep');
my $DEV_EXCEPTION = $H->slurp_rel_file('exception.development.html.ep');
my $NOT_FOUND = $H->slurp_rel_file('not_found.html.ep');
my $DEV_NOT_FOUND = $H->slurp_rel_file('not_found.development.html.ep');

# "These old doomsday devices are dangerously unstable.
# I'll rest easier not knowing where they are."
sub AUTOLOAD {
Expand Down Expand Up @@ -155,14 +165,16 @@ sub handler {
weaken $c->{tx};

# Dispatcher
unless ($self->{dispatch}) {
unless ($self->{started}) {
$self->hook(
around_dispatch => sub {
my ($next, $c) = @_;
$c->app->dispatch($c);
}
);
$self->{dispatch}++;
$self->hook(around_exception => sub { _exception(pop) });
$self->hook(around_not_found => sub { _not_found(pop) });
$self->{started}++;
}

# Process
Expand Down Expand Up @@ -203,6 +215,56 @@ sub start { ($ENV{MOJO_APP} = shift)->commands->start(@_) }

sub startup { }

sub _exception {
my $c = shift;

# Render with fallbacks
my $mode = $c->app->mode;
my $options = {
template => "exception.$mode",
format => $c->stash->{format} || 'html',
handler => undef,
status => 500
};
my $inline = $mode eq 'development' ? $DEV_EXCEPTION : $EXCEPTION;
return if _fallbacks($c, $options, 'exception', $inline);
_fallbacks($c, {%$options, format => 'html'}, 'exception', $inline);
}

sub _fallbacks {
my ($c, $options, $template, $inline) = @_;

# Mode specific template
unless ($c->render($options)) {

# Template
$options->{template} = $template;
unless ($c->render($options)) {

# Inline template
my $stash = $c->stash;
return unless $stash->{format} eq 'html';
delete $stash->{layout};
delete $stash->{extends};
delete $options->{template};
return $c->render(%$options, inline => $inline, handler => 'ep');
}
}
}

sub _not_found {
my $c = shift;

# Render with fallbacks
my $mode = $c->app->mode;
my $format = $c->stash->{format} || 'html';
my $options
= {template => "not_found.$mode", format => $format, status => 404};
my $inline = $mode eq 'development' ? $DEV_NOT_FOUND : $NOT_FOUND;
return if _fallbacks($c, $options, 'not_found', $inline);
_fallbacks($c, {%$options, format => 'html'}, 'not_found', $inline);
}

1;

=head1 NAME
Expand Down Expand Up @@ -451,9 +513,7 @@ Extend L<Mojolicious> with hooks.
These hooks are currently available and are emitted in the listed order:
=over 2
=item B<after_build_tx>
=head3 C<after_build_tx>
Emitted right after the transaction is built and before the HTTP request gets
parsed.
Expand All @@ -468,7 +528,7 @@ rather advanced features such as upload progress bars possible, just note that
it will not work for embedded applications. (Passed the transaction and
application object)
=item B<before_dispatch>
=head3 C<before_dispatch>
Emitted right before the static dispatcher and router start their work.
Expand All @@ -480,7 +540,7 @@ Emitted right before the static dispatcher and router start their work.
Very useful for rewriting incoming requests and other preprocessing tasks.
(Passed the default controller object)
=item B<after_static_dispatch>
=head3 C<after_static_dispatch>
Emitted in reverse order after the static dispatcher determined if a static
file should be served and before the router starts its work.
Expand All @@ -493,7 +553,7 @@ file should be served and before the router starts its work.
Mostly used for custom dispatchers and post-processing static file responses.
(Passed the default controller object)
=item B<after_dispatch>
=head3 C<after_dispatch>
Emitted in reverse order after a response has been rendered. Note that this
hook can trigger before C<after_static_dispatch> due to its dynamic nature.
Expand All @@ -506,7 +566,41 @@ hook can trigger before C<after_static_dispatch> due to its dynamic nature.
Useful for rewriting outgoing responses and other post-processing tasks.
(Passed the current controller object)
=item B<around_dispatch>
=head3 C<around_exception>
Emitted when an exception occurs or
L<Mojolicious::Controller/"render_exception"> has been called. The last hook
in the chain will use the renderer to try and find an exception template or
fall back to a built-in one.
$app->hook(around_exception => sub {
my ($next, $c) = @_;
...
$next->();
...
});
Useful for monitoring and generating custom exception responses. (Passed a
closure leading to the next hook and the current controller object)
=head3 C<around_not_found>
Emitted when the static dispatcher and router didn't handle a request or
L<Mojolicious::Controller/"render_not_found"> has been called. The last hook
in the chain will use the renderer to try and find a not found template or
fall back to a built-in one.
$app->hook(around_not_found => sub {
my ($next, $c) = @_;
...
$next->();
...
});
Useful for monitoring and generating custom fallback responses. (Passed a
closure leading to the next hook and the current controller object)
=head3 C<around_dispatch>
Emitted right before the C<before_dispatch> hook and wraps around the whole
dispatch process, so you have to manually forward to the next hook if you want
Expand All @@ -526,8 +620,6 @@ customize application wide exception handling for example, consider it the
sledgehammer in your toolbox. (Passed a closure leading to the next hook and
the default controller object)
=back
=head2 C<plugin>
$app->plugin('some_thing');
Expand Down
87 changes: 14 additions & 73 deletions lib/Mojolicious/Controller.pm
Expand Up @@ -5,7 +5,6 @@ use Carp ();
use Mojo::ByteStream;
use Mojo::Cookie::Response;
use Mojo::Exception;
use Mojo::Home;
use Mojo::Transaction::HTTP;
use Mojo::URL;
use Mojo::Util;
Expand All @@ -20,15 +19,6 @@ has match => sub {
};
has tx => sub { Mojo::Transaction::HTTP->new };

# Bundled files
our $H = Mojo::Home->new;
$H->parse($H->parse($H->mojo_lib_dir)->rel_dir('Mojolicious/templates'));
our $MOJOBAR = $H->slurp_rel_file('mojobar.html.ep');
my $EXCEPTION = $H->slurp_rel_file('exception.html.ep');
my $DEV_EXCEPTION = $H->slurp_rel_file('exception.development.html.ep');
my $NOT_FOUND = $H->slurp_rel_file('not_found.html.ep');
my $DEV_NOT_FOUND = $H->slurp_rel_file('not_found.development.html.ep');

# Reserved stash values
my %RESERVED = map { $_ => 1 } (
qw(action app cb controller data extends format handler json layout),
Expand Down Expand Up @@ -242,33 +232,19 @@ sub render_data { shift->render(data => @_) }
# Neat."
sub render_exception {
my ($self, $e) = @_;
$e = Mojo::Exception->new($e);

# Recursion
# Log exception
my $app = $self->app;
$app->log->error($e);
my $stash = $self->stash;
return if $stash->{'mojo.exception'};
$app->log->error($e = Mojo::Exception->new($e));

# Filtered stash snapshot
my $stash = $self->stash;
my %snapshot = map { $_ => $stash->{$_} }
grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash;

# Render with fallbacks
my $mode = $app->mode;
my $options = {
template => "exception.$mode",
format => $stash->{format} || 'html',
handler => undef,
status => 500,
snapshot => \%snapshot,
exception => $e,
'mojo.exception' => 1
};
my $inline = $mode eq 'development' ? $DEV_EXCEPTION : $EXCEPTION;
return if $self->_render_fallbacks($options, 'exception', $inline);
$options->{format} = 'html';
$self->_render_fallbacks($options, 'exception', $inline);
# Delegate
%$stash = (%$stash, snapshot => \%snapshot, exception => $e);
$app->plugins->emit_chain(around_exception => $self);
}

# "If you hate intolerance and being punched in the face by me,
Expand All @@ -281,23 +257,7 @@ sub render_later { shift->stash->{'mojo.rendered'}++ }
# Lick my frozen metal ass."
sub render_not_found {
my $self = shift;

# Recursion
my $stash = $self->stash;
return if $stash->{'mojo.exception'} || $stash->{'mojo.not_found'};

# Render with fallbacks
my $mode = $self->app->mode;
my $options = {
template => "not_found.$mode",
format => $stash->{format} || 'html',
status => 404,
'mojo.not_found' => 1
};
my $inline = $mode eq 'development' ? $DEV_NOT_FOUND : $NOT_FOUND;
return if $self->_render_fallbacks($options, 'not_found', $inline);
$options->{format} = 'html';
$self->_render_fallbacks($options, 'not_found', $inline);
$self->app->plugins->emit_chain(around_not_found => $self);
}

# "You called my thesis a fat sack of barf, and then you stole it?
Expand Down Expand Up @@ -522,27 +482,6 @@ sub write_chunk {
return $self->rendered;
}

sub _render_fallbacks {
my ($self, $options, $template, $inline) = @_;

# Mode specific template
unless ($self->render($options)) {

# Template
$options->{template} = $template;
unless ($self->render($options)) {

# Inline template
my $stash = $self->stash;
return unless $stash->{format} eq 'html';
delete $stash->{layout};
delete $stash->{extends};
delete $options->{template};
return $self->render(%$options, inline => $inline, handler => 'ep');
}
}
}

1;

=head1 NAME
Expand Down Expand Up @@ -743,8 +682,9 @@ not be encoded. All additional values get merged into the C<stash>.
$c->render_exception('Oops!');
$c->render_exception(Mojo::Exception->new('Oops!'));
Render the exception template C<exception.$mode.$format.*> or
C<exception.$format.*> and set the response status code to C<500>.
Emit C<around_exception> plugin hook, render the exception template
C<exception.$mode.$format.*> or C<exception.$format.*> and set the response
status code to C<500>.
=head2 C<render_json>
Expand Down Expand Up @@ -774,8 +714,9 @@ useful.
$c->render_not_found;
Render the not found template C<not_found.$mode.$format.*> or
C<not_found.$format.*> and set the response status code to C<404>.
Emit C<around_not_found> plugin hook, render the not found template
C<not_found.$mode.$format.*> or C<not_found.$format.*> and set the response
status code to C<404>.
=head2 C<render_partial>
Expand Down Expand Up @@ -817,7 +758,7 @@ of the response, which is C<text/html;charset=UTF-8> by default.
$c = $c->rendered;
$c = $c->rendered(302);
Finalize response and run C<after_dispatch> plugin hook, defaults to using a
Finalize response and emit C<after_dispatch> plugin hook, defaults to using a
C<200> response code.
# Stream content directly from file
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Plugin/PODRenderer.pm
Expand Up @@ -12,7 +12,7 @@ use Pod::Simple::Search;
my @PATHS = map { $_, "$_/pods" } @INC;

# Bundled files
my $PERLDOC = $Mojolicious::Controller::H->slurp_rel_file('perldoc.html.ep');
my $PERLDOC = $Mojolicious::H->slurp_rel_file('perldoc.html.ep');

# "This is my first visit to the Galaxy of Terror and I'd like it to be a
# pleasant one."
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/templates/exception.development.html.ep
Expand Up @@ -98,7 +98,7 @@
% end
</head>
<body onload="prettyPrint()">
%= include inline => $Mojolicious::Controller::MOJOBAR
%= include inline => $Mojolicious::MOJOBAR
<div id="wrapperlicious">
<div id="nothing" class="box spaced"></div>
% my $cv = begin
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/templates/not_found.development.html.ep
Expand Up @@ -74,7 +74,7 @@
% end
</head>
<body onload="prettyPrint()">
%= include inline => $Mojolicious::Controller::MOJOBAR
%= include inline => $Mojolicious::MOJOBAR
<div id="wrapperlicious">
<div id="routes">
<h1>Page not found... yet!</h1>
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/templates/perldoc.html.ep
Expand Up @@ -61,7 +61,7 @@
% end
</head>
<body onload="prettyPrint()">
%= include inline => $Mojolicious::Controller::MOJOBAR
%= include inline => $Mojolicious::MOJOBAR
% my $link = begin
%= link_to shift, shift, class => "mojoscroll"
% end
Expand Down

0 comments on commit e9fc8ed

Please sign in to comment.