ConditionSystem - A Common Lisp like condition/restart system for exceptions
{ 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. };
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.
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 });
Return from a restart with a specific value.
with_handlers { my $foo = restart_case { Exception->new } # foo is 500 } handle(Exception => continue_with { 500 });
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'))
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.
restart_case
Create a handler for a given exception type, and associated code reference:
handle('Exception::Class' => sub { # Handle exception here... });
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"; });
Oliver Charles
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.
To install ConditionSystem, copy and paste the appropriate command in to your terminal.
cpanm
cpanm ConditionSystem
CPAN shell
perl -MCPAN -e shell install ConditionSystem
For more information on module installation, please visit the detailed CPAN module installation guide.