What you describe in that code is not catching an exception, it's handling a return value, which means that it's doing the work too near to the failure. Have your
login_user method use a real exception class, so if login fails you'd do:
...
die Exception::NoUser->new(-requested_uri => $request->uri);
...
Then, way up at the top level, in your basic handler method that everything uses, you would have:
my $result = eval { $app->process($request) };
if ($@) {
$result = ref($@) ? $@ :
Exception::Unknown->new(-requested_uri => $request->uri,
-message => $@);
}
$self->emit($result);
There are big problems with your code though, in that it makes assumptions about how it's being used, which makes it very hard to write tests for it. For instance, what is it that's inherent in your Book object that means it needs to know it's being used via a webpage? Slightly more controversially, why does it need to know it's being fetched from a database? As someone else suggested, maybe your application's
process method needs to do something like:
sub process {
my($self, $request) = @_;
local Exception::current_uri = $request->uri;
$self->get_resources($request);
$self->authenticate_user($request);
$self->authorize_user($request);
$self->generate_response($request);
}
sub get_resources {
my($self,$request) = @_;
# Fetch your book from the database.
$self->set_book($book);
}
sub authenticate_user {
my($self, $request) = @_;
my $user = $self->make_user_from_request($request) or
die Exception::NoUser->new;
$self->set_user($user);
}
sub authorize_user {
my $self = shift;
$self->user_has_required_access($user) or
die Exception::InsufficientAccess->new(user => $user);
}
If you set up your inheritance tree (or compose your objects) correctly, you'll only have to override one or two methods for each distinct page. And you'll have a collection of methods that you can test without having to pretend to be a webserver all the time, and that's a big win.