The Errors of Our Way

This is a preview of the error throwing mechanism that is coming with Dancer 2. Things have been tidied a little bit, objectified in a classy manner (or classified in an objectionable manner? Anyway, turned into nice little Moo classes), and made (or so we hope) both easy to use and pleasantly versatile.

At the Core

In Dancer 2, Dancer::Core::Error is the ringleader as far as error handling is concerned. Its mission is fairly simple: take in booboos, and report on them in the most sensible way possible.

Within Dancer's code, creating a new error is done by, well, creating a new Dancer::Core::Error:

my $oopsie = Dancer::Core::Error->new(
    status  => 418,
    message => "This is the Holidays. Tea not acceptable. We want eggnog.",
    context => $context,
);

If that was not simple enough, all those attributes are actually optional. If not given, the status code defaults to a good old 500, there is no need for a message if we feel taciturn, and while the $context (which is a Dancer::Core::Context object holding all the pieces of information related to the current request) is needed if we want to take advantage of the templates, we can also do without.

The important thing to know, however, is that creating an error doesn't mean that it's automatically seen by the end-user. For that to happen, you have to...

Throw It Around

Or, put another way, we have to populate the Dancer::Core::Response object with the error's data. That is done via

$oopsie->throw($response);

Or, if we want to use the response object already present in the $context (which is usually the case):

$oopsie->throw;

This populates the status code of the response, sets its content, and throws a halt() in the dispatch process.

What The Error Going To Look Like?

Quick answer: whatever you want it to. The error object has quite a few ways to generate its content.

First, it can be explicitly given

my $oopsie = Dancer::Core::Error->new(
    content => '<html><body><h1>OMG</h1></body></html>',
);

but that's boring.

If the $context was given, the error will check if there's a template by the name of the status code (so, say you're using Template Toolkit, 418.tt) and will use it to generate the content, passing it the error's $message, $status code and $title (which, if not specified, will be the standard http error definition for the status code).

If there is no template, the error will then look for a static page (to continue with our example, 418.html) in the public/ directory.

And finally, if all of that failed, the error object will fall back on an internal template.

Throwing Errors In Routes

Now, how do we use all that stuff in our routes? In the simpliest way:

get '/xmas/gift/:gift' => sub {
    die "sorry, we're all out of ponies\n" 
        if param('gift') eq 'pony';
};

The die will be intercepted by Dancer's inner magic, converted into an error (status code 500, message set to the dying words) and shoved into the response.

In the cases where more control is required, send_error() is the way to go:

get '/glass/eggnog' => sub {
    send_error "Sorry, no eggnog here", 418;
};

And if total control is needed:

get '/xmas/wishlist' => sub {
    Dancer::Core::Error->new(
        response => response(),
        status   => 406,
        message  => "nothing but coal for you, I'm afraid",
        template => 'naughty/index',
    )->throw unless user_was_nice();

    ...;
};

In Conclusion

At last news, Dancer::Core::Error is in a working state. Some of its details are likely to change a little bit, but it's already nicely settling into a pattern that should make everyone happy. Well, as happy as anybody can be while errors are being thrown, anyway.

AUTHOR

Yanick Champoux (YANICK)