NAME

ConditionSystem - A Common Lisp like condition/restart system for exceptions

SYNOPSIS

{ package MalformedLogEntry; use Moose; extends 'Throwable::Error';

has bad_data => ( is => 'ro' );

package LogParser;
use Conditions;
sub parse_log_entry {
    my $entry = shift or die "Must specify entry";
    if($entry =~ /(\d+-\d+-\d+) (\d+:\d+:\d+) (\w+) (.*)/) {
        return ($1, $2, $3, $4);
    }
    else {
        restart_case {
            MalformedLogEntry->new($entry),
        }
        bind_continue(use_value => sub { return shift }),
        bind_continue(log => sub {
            warn "*** Invalid entry: $entry";
            return undef;
        });
    }
};

package MyApp;
use Conditions;
my @logs = with_handlers {
    [ parse_log_entry('2010-01-01 10:09:5 WARN Test') ],
    [ parse_log_entry('Oh no bad data') ],
    [ parse_log_entry('2010-10-12 12:11:03 INFO Notice it still carries on!') ];
}
handle(MalformedLogEntry => restart('log'));

# @logs contains 3 logs, the 2nd of which is 'undef'
# A single warning will have been printed to STDERR as well.
};

DESCRIPTION

This distribution implements a Common Lisp-like approach to exception handling, providing both a mechanism for throwing/catching exceptions, but also a mechanism for continuing on from an exception via a non-local exit. This essentially allows you "fix" the code that was throwing an exception from outside that code itself, rather than trying to handle stuff when it's already too late.

For a good introduction to the condition system (that this was all inspired by), I highly recommend Practical Common Lisp, in particular the chapter Beyond Exception Handling

HALT! This module is both very new, and does some fairly crazy things, and as such may not be ready for prime time usage. However, the basic test cases do pass, so maybe you will have some luck. I encourage the usage of this module for a bit of fun, and exploration for now. Hopefully it will mature into a production ready module, but it's not there yet. But with your help, it can be so... please submit patches, bug reports and all that goodness.

FUNCTIONS

with_handlers

Run a block of code, and if any exception is raised, try and invoke one of the handlers.

with_handlers {
    # Dangerous code...
}
handle(ExceptionType => sub {
    # Recovery
});

continue_with

Return from a restart with a specific value.

with_handlers {
    my $foo = restart_case {
        Exception->new
    }
    # foo is 500
}
handle(Exception => continue_with { 500 });

restart

Invoke a restart with a specific name, and pass extra arguments through.

with_handlers { restart_case { Exception->new } bind_restart(Log => sub { warn "An Exception was raised"; }); } handle(Exception => restart('Log'))

restart_case

Throw an exception (from a specified block) with pre-defined strategies on how to resume execution later.

restart_case { Exception->new }
bind_restart(delegate_responsibility => sub {
    Boss->email($bug_report)
})

The body of restart_case must yield an exception, and will be when the restart case is invoked. There may be 0 to many restarts provided. Restarts are invoked by restart, called from a handler set up with with_handlers.

handle

Create a handler for a given exception type, and associated code reference:

handle('Exception::Class' => sub {
    # Handle exception here...
});

bind_continue

Bind a restart for the scope of a restart_case block, with a given name and code reference:

bind_continue(panic => sub {
    warn "OMG OMG OMG OMG";
});

AUTHOR

Oliver Charles

COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Oliver Charles <oliver.g.charles@googlemail.com>.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.