Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Various cleanup for the hooks #331

Merged
merged 8 commits into from

2 participants

@franckcuny

The following changes are fixing some issues with the hooks. Some were missing from Dancer and are now implemented (except for on_handler_exception, but a ticket was created).

The hooks related to errors were not working because they were not implemented in the correct location.

There's a few changes to the documentation, to move everything related to hooks to the (already existing) Hooks.pod.

franckcuny added some commits
@franckcuny franckcuny Hooks' name are compatible with Dancer1.
Some hooks are implemented in Dancer2 with different names that they use to have
in Dancer1.  Using the `hook_aliases` function, we alias them to their new name.

The documentation is also updated to let the users know that they have different
names now.
7a4f458
@franckcuny franckcuny Fix documentation for the `before` hook.
This hook receives a Dancer2::Core::Contex object, not a Dancer2::Core::Route.
0b760bd
@franckcuny franckcuny Move all the hooks' documentation to Dancer2::Manual::Hooks.
This avoid some duplication in the documentation, and it's easier for the
developer to find all the things related to hooks in a single place.
52c3a29
@franckcuny franckcuny Add the 'on_route_exception' hook.
This hook was implemented in Dancer and is missing in Dancer2.  It's triggered
when an error occurs while executing a route.  It receives as argument the
context adn the error as as string.
90dff98
@franckcuny franckcuny Remove documentation for the 'on_handler_exception'.
This hook is not implemented for now, and implementing it will require some
refactoring.
6c552a6
@franckcuny franckcuny Move errors' hooks to Dancer2::Core::App.
The hooks for errors need to be in the application in order to be executed.
Having them in Dancer2::Core::Error can't work because we never store the
hooks inside this object.
e881bc1
@franckcuny franckcuny Fix POD error for Dancer2::Manual::Hooks. 57e4753
@franckcuny franckcuny When a hook is executed from an app, log the name of the hook. 966ae00
@ambs
Owner

Good work, @franckcuny! :+1:

@ambs ambs merged commit 966ae00 into devel

1 check failed

Details default The Travis CI build could not complete due to an error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 27, 2013
  1. @franckcuny

    Hooks' name are compatible with Dancer1.

    franckcuny authored
    Some hooks are implemented in Dancer2 with different names that they use to have
    in Dancer1.  Using the `hook_aliases` function, we alias them to their new name.
    
    The documentation is also updated to let the users know that they have different
    names now.
  2. @franckcuny

    Fix documentation for the `before` hook.

    franckcuny authored
    This hook receives a Dancer2::Core::Contex object, not a Dancer2::Core::Route.
  3. @franckcuny

    Move all the hooks' documentation to Dancer2::Manual::Hooks.

    franckcuny authored
    This avoid some duplication in the documentation, and it's easier for the
    developer to find all the things related to hooks in a single place.
  4. @franckcuny

    Add the 'on_route_exception' hook.

    franckcuny authored
    This hook was implemented in Dancer and is missing in Dancer2.  It's triggered
    when an error occurs while executing a route.  It receives as argument the
    context adn the error as as string.
  5. @franckcuny

    Remove documentation for the 'on_handler_exception'.

    franckcuny authored
    This hook is not implemented for now, and implementing it will require some
    refactoring.
  6. @franckcuny

    Move errors' hooks to Dancer2::Core::App.

    franckcuny authored
    The hooks for errors need to be in the application in order to be executed.
    Having them in Dancer2::Core::Error can't work because we never store the
    hooks inside this object.
  7. @franckcuny
  8. @franckcuny
This page is out of date. Refresh to see the latest.
View
6 lib/Dancer2/Core/App.pm
@@ -36,6 +36,10 @@ sub supported_hooks {
qw/
core.app.before_request
core.app.after_request
+ core.app.route_exception
+ core.error.before
+ core.error.after
+ core.error.init
/;
}
@@ -458,8 +462,6 @@ sub compile_hooks {
return
if ( $self->context && $self->context->response->is_halted );
- # TODO: log entering the hook '$position'
- #warn "entering hook '$position'";
eval { $hook->(@_) };
# TODO : do something with exception there
View
2  lib/Dancer2/Core/Dispatcher.pm
@@ -84,6 +84,8 @@ sub dispatch {
my $error = $@;
if ($error) {
$app->log( error => "Route exception: $error" );
+ $app->execute_hook(
+ 'core.app.route_exception', $context, $error);
return $self->response_internal_error( $context, $error );
}
}
View
22 lib/Dancer2/Core/Error.pm
@@ -7,8 +7,6 @@ use Dancer2::Core::Types;
use Data::Dumper;
use Dancer2::FileUtils 'path';
-with 'Dancer2::Core::Role::Hookable';
-
=head1 SYNOPSIS
# taken from send_file:
@@ -96,15 +94,6 @@ my %error_title = (
=cut
-
-sub supported_hooks {
- qw/
- core.error.before
- core.error.after
- core.error.init
- /;
-}
-
=attr show_errors
=cut
@@ -292,7 +281,8 @@ has context => (
sub BUILD {
my ($self) = @_;
- $self->execute_hook( 'core.error.init', $self );
+ $self->has_context &&
+ $self->context->app->execute_hook( 'core.error.init', $self );
}
has exception => (
@@ -356,7 +346,8 @@ sub throw {
croak "error has no response to throw at" unless $self->response;
- $self->execute_hook( 'core.error.before', $self );
+ $self->has_context &&
+ $self->context->app->execute_hook( 'core.error.before', $self );
my $message = $self->content;
$message .= "\n\n" . $self->exception
@@ -365,10 +356,11 @@ sub throw {
$self->response->status( $self->status );
$self->response->header( $self->content_type );
$self->response->content($message);
- $self->response->halt(1);
- $self->execute_hook( 'core.error.after', $self->response );
+ $self->has_context &&
+ $self->context->app->execute_hook('core.error.after', $self->response);
+ $self->response->halt(1);
return $self->response;
}
View
11 lib/Dancer2/Core/Role/Hookable.pm
@@ -29,11 +29,19 @@ sub hook_aliases {
after_file_render => 'handler.file.after_render',
before_template_render => 'engine.template.before_render',
after_template_render => 'engine.template.after_render',
+ before_layout_render => 'engine.template.before_layout_render',
+ after_layout_render => 'engine.template.after_layout_render',
before_serializer => 'engine.serializer.before',
after_serializer => 'engine.serializer.after',
init_error => 'core.error.init',
before_error => 'core.error.before',
after_error => 'core.error.after',
+ on_route_exception => 'core.app.route_exception',
+
+ # compatibility from Dancer1
+ before_error_render => 'core.error.before',
+ after_error_render => 'core.error.after',
+ before_error_init => 'core.error.init',
};
}
@@ -127,6 +135,9 @@ sub execute_hook {
croak "Hook '$name' does not exist"
if !$self->has_hook($name);
+ ref($self) eq 'Dancer2::Core::App' &&
+ $self->engine('logger')->core("Entering hook $name");
+
my $res;
$res = $_->(@args) for @{ $self->hooks->{$name} };
return $res;
View
181 lib/Dancer2/Manual/DSL.pod
@@ -306,186 +306,7 @@ Adds a hook at some position. For example :
There can be multiple hooks assigned to a given position, and each will be
executed in order.
-(For details on how to register new hooks from within plugins, see
-L<Dancer2::Manual::Hooks>.)
-Supported B<before> hooks (in order of execution):
-
-=over
-
-=item before_deserializer
-
-This hook receives no arguments.
-
- hook before_deserializer => sub {
- ...
- };
-
-=item before_file_render
-
-This hook receives as argument the path of the file to render.
-
- hook before_file_render => sub {
- my $path = shift;
- ...
- };
-
-
-=item before_error_init
-
-This hook receives as argument a L<Dancer2::Core::Error> object.
-
- hook before_error_init => sub {
- my $error = shift;
- ...
- };
-
-
-=item before_error_render
-
-This hook receives as argument a L<Dancer2::Core::Error> object.
-
- hook before_error_render => sub {
- my $error = shift;
- };
-
-=item before
-
-This hook receives one argument, the route being executed (a
-L<Dancer2::Core::Route> object).
-
- hook before => sub {
- my $route_handler = shift;
- ...
- };
-
-=item before_template_render
-
-This is an alias to 'before_template'.
-
-This hook receives as argument a HashRef, containing the tokens that
-will be passed to the template. You can use it to add more tokens, or
-delete some specific token.
-
- hook before_template_render => sub {
- my $tokens = shift;
- delete $tokens->{user};
- $tokens->{time} = localtime;
- };
-
-is equivalent to
-
- hook before_template => sub {
- my $tokens = shift;
- delete $tokens->{user};
- $tokens->{time} = localtime;
- };
-
-=item before_layout_render
-
-This hook receives two arguments. The first one is a HashRef containing the
-tokens. The second is a ScalarRef representing the content of the template.
-
- hook before_layout_render => sub {
- my ($tokens, $html_ref) = @_;
- ...
- };
-
-=item before_serializer
-
-This hook receives as argument a L<Dancer2::Core::Response> object.
-
- hook before_serializer => sub {
- my $response = shift;
- $response->content->{start_time} = time();
- };
-
-=back
-
-Supported B<after> hooks (in order of execution):
-
-=over
-
-=item after_deserializer
-
-This hook receives no arguments.
-
- hook after_deserializer => sub {
- ...
- };
-
-=item after_file_render
-
-This hook receives as argument a L<Dancer2::Core::Response> object.
-
- hook after_file_render => sub {
- my $response = shift;
- };
-
-=item after_template_render
-
-This hook receives as argument a ScalarRef representing the content generated
-by the template.
-
- hook after_template_render => sub {
- my $html_ref = shift;
- };
-
-=item after_layout_render
-
-This hook receives as argument a ScalarRef representing the content generated
-by the layout
-
- hook after_layout_render => sub {
- my $html_ref = shift;
- };
-
-=item after
-
-This is an alias for C<after>.
-
-This hook runs after a request has been processed, but before the response is
-sent.
-
-It receives a L<Dancer2::Core::Response> object, which it can modify
-if it needs to make changes to the response which is about to be sent.
-
- hook after => sub {
- my $response = shift;
- };
-
-This is equivalent to
-
- after sub {
- my $response = shift;
- };
-
-=item after_error_render
-
-This hook receives as argument a L<Dancer2::Core::Response> object.
-
- hook after_error_render => sub {
- my $response = shift;
- };
-
-=item on_handler_exception
-
-This hook is called when an exception has been caught, at the handler level,
-just before creating and rendering L<Dancer2::Core::Error>.
-
- hook on_handler_exception => sub {
- my $exception = shift;
- };
-
-=item on_route_exception
-
-This hook is called when an exception has been caught, at the route level, just
-before rethrowing it higher. This hook receives the exception as argument.
-
- hook on_route_exception => sub {
- my $exception = shift;
- };
-
-=back
+All the hooks are documented in L<Dancer2::Manual::Hooks>.
=head2 info
View
68 lib/Dancer2/Manual/Hooks.pod
@@ -9,8 +9,11 @@ This manual documents all the hooks that are supported by the core framework.
=head2 Request workflow
C<before> hooks are evaluated before each request within the context of the
-request and can modify the request and response. It's possible to define
-variables which will be accessible in the action blocks with the keyword 'var'.
+request and receives as argument the context (a L<Dancer2::Core::Context>
+object).
+
+It's possible to define variables which will be accessible in the action blocks
+with the keyword 'var'.
hook before => sub {
var note => 'Hi there';
@@ -22,7 +25,6 @@ variables which will be accessible in the action blocks with the keyword 'var'.
vars->{note}; # 'Hi there'
};
-
For another example, this can be used along with session support to easily
give non-logged-in users a login page:
@@ -34,23 +36,26 @@ give non-logged-in users a login page:
}
};
-
-The request keyword returns the current Dancer2::Request object representing the
-incoming request. See the documentation of the L<Dancer2::Core::Request> module for
-details.
+The request keyword returns the current L<Dancer2::Core::Request> object
+representing the incoming request.
C<after> hooks are evaluated after the response has been built by a route
handler, and can alter the response itself, just before it's sent to the
client.
+This hook runs after a request has been processed, but before the response is
+sent.
+
+It receives a L<Dancer2::Core::Response> object, which it can modify
+if it needs to make changes to the response which is about to be sent.
+
The filter is given the response object as its first argument:
hook after => sub {
my $response = shift;
- $response->{content} = 'after filter got here!';
+ $response->(content, 'after filter got here!');
};
-
=head2 Templates
C<before_template_render> hooks are called whenever a template is going to be
@@ -108,24 +113,41 @@ passed to the hook.
hook init_error => sub {
my $error = shift;
- # do something with $error
+ # do something with $error
};
+I<This hook was named B<before_error_init> in Dancer, and is now aliased to
+this hook.>
+
C<before_error> hooks are called whenever an error is going to be thrown, it
receives the error object as its first and unique argument.
hook before_error => sub {
my $error = shift;
- # do something with $error
+ # do something with $error
};
+I<This hook was named B<before_error_render> in Dancer, and is now aliased to
+this hook.>
+
C<after_error> hooks are called whenever an error object has been thrown, it
-receives the generated content as the first argument.
+receives a L<Dancer2::Core::Response> object as the first argument.
hook after_error => sub {
- my $content = shift;
+ my $response = shift;
};
+I<This hook was named <after_error_render> in Dancer, and is now aliased to
+this hook.>
+
+C<on_route_exception> is called when an exception has been caught, at the
+route level, just before rethrowing it higher. This hook receives a
+L<Dancer2::Core::Context> and the error as arguments.
+
+ hook on_route_exception => sub {
+ my ($context, $error) = @_;
+ };
+
=head2 File rendering
Whenever a content is produced out of the parsing of a static file, the
@@ -145,6 +167,24 @@ content produced. It receives the response object (L<Dancer2::Core::Response>)
produced.
hook after_file_render => sub {
- my $response = shift;
+ my $response = shift;
};
+=head2 Serializers
+
+C<before_serializer> is called before serializing the content, and eceives as
+argument the content to serialize.
+
+ hook before_serializer => sub {
+ my @data = @_;
+ $response->content->{start_time} = time();
+ };
+
+C<after_serializer> is called after the payload was serialized, and receives
+the serialized content as an argument.
+
+ hook before_deserializer => sub {
+ my $content = shift;
+ };
+
+
View
32 t/hooks.t
@@ -2,6 +2,7 @@ use strict;
use warnings;
use Test::More import => ['!pass'];
use File::Spec;
+use Capture::Tiny 0.12 'capture_stderr';
use Carp;
@@ -21,6 +22,8 @@ my @hooks = qw(
before_serializer
after_serializer
+
+ on_route_exception
);
my $tests_flags = {};
@@ -63,6 +66,8 @@ my $tests_flags = {};
get '/intercepted' => sub {'not intercepted'};
+ get '/route_exception' => sub {die 'this is a route exception'};
+
hook before => sub {
my $c = shift;
return unless $c->request->path eq '/intercepted';
@@ -71,6 +76,29 @@ my $tests_flags = {};
$c->response->halt;
};
+ hook on_route_exception => sub {
+ my ($context, $error) = @_;
+ is ref($context), 'Dancer2::Core::Context';
+ like $error, qr/this is a route exception/;
+ };
+
+ hook init_error => sub {
+ my ($error) = @_;
+ is ref($error), 'Dancer2::Core::Error';
+ };
+
+ hook before_error => sub {
+ my ($error) = @_;
+ is ref($error), 'Dancer2::Core::Error';
+ };
+
+ hook after_error => sub {
+ my ($response) = @_;
+ is ref($response), 'Dancer2::Core::Response';
+ ok !$response->is_halted;
+ is $response->content, 'Internal Server Error';
+ };
+
# make sure we compile all the apps without starting a webserver
main->dancer_app->finish;
}
@@ -118,4 +146,8 @@ subtest 'before can halt' => sub {
is join( "\n", @{ $resp->[2] } ) => 'halted by before';
};
+subtest 'route_exception' => sub {
+ capture_stderr { dancer_response get => '/route_exception' };
+};
+
done_testing;
Something went wrong with that request. Please try again.