Exception::Stringy - a Perl Exceptions module where exceptions are not objects but simple strings.
version 0.20
use Exception::Stringy; Exception::Stringy->declare_exceptions( 'MyException', 'YetAnotherException' => { isa => 'AnotherException', }, 'ExceptionWithFields' => { isa => 'YetAnotherException', fields => [ 'grandiosity', 'quixotic' ], throw_alias => 'throw_fields', }, ); ### with Try::Tiny use Try::Tiny; try { # throw an exception MyException->throw('I feel funny.'); # or use an alias throw_fields 'Error message', grandiosity => 1; # or with fields ExceptionWithFields->throw('I feel funny.', quixotic => 1, grandiosity => 2); # you can build exception step by step my $e = ExceptionWithFields->new("The error message"); $e->$xfield(quixotic => "some_value"); $e->$xthrow(); } catch { if ( $_->$xisa('Exception::Stringy') ) { warn $_->$xerror, "\n"; } if ( $_->$xisa('ExceptionWithFields') ) { if ( $_->$xfield('quixotic') ) { handle_quixotic_exception(); } else { handle_non_quixotic_exception(); } } else { $_->$xrethrow; } }; ### without Try::Tiny eval { # ... MyException->throw('I feel funny.'); 1; } or do { my $e = $@; # .. same as above with $e instead of $_ }
This module allows you to declare exceptions, and provides a simple interface to declare, throw, and interact with them. It can be seen as a light version of Exception::Class, except that there is a catch: exceptions are not objects, they are normal strings, with a pattern that contains properties.
Exception::Class
This modules has one external dependancy: Scalar::Util. It requires Perl 5.8.9 or above.
Scalar::Util
Having exceptions be objects is sometimes very annoying. What if some code is calling you, and isn't expecting objects exceptions ? Sometimes string overloading doesn't work. Sometimes, external code tamper with your exception. Consider:
use Exception::Class ('MyException'); use Scalar::Util qw( blessed ); use Try::Tiny; $SIG{__DIE__} = sub { die "FATAL: $_[0]" }; try { MyException->throw("foo"); } catch { die "this is not a Exception::Class" unless blessed $_ && $_->isa('Exception::Class'); if ($_->isa('MyException')) { ... } };
In this example, the exception thrown is a Exception::Class instance, but it gets forced to a string by the signal handler. When in the catch block, it's not an object anymore, it's a regular string, and the code fails to see that it's was once 'MyException'.
Using Exception::Stringy, exceptions are regular strings, that embed in themselves a small pattern to contain their properties. They can be stringified, concatenated, and tampered with in any way, as long as the pattern isn't removed (it can be moved inside the string though).
Exception::Stringy
As a result, exceptions are more robust, while still retaining all features you'd expect from similar modules like Exception::Class
use Exception::Stringy; Exception::Stringy->declare_exceptions('MyException'); use Try::Tiny; $SIG{__DIE__} = sub { die "FATAL: $_[0]" }; try { MyException->throw("foo"); } catch { die "this is not a Exception::Stringy" unless $_->$xisa('Exception::Stringy'); if ($_->$xisa('MyException')) { ... } };
Defining exception classes is done by calling declare_exceptions:
declare_exceptions
use Exception::Stringy; Exception::Stringy->declare_exceptions( 'MyException', 'ExceptionWithFields' => { isa => 'MyException', fields => [ qw(field1 field2) ], throw_alias => 'throw_fields', }, );
In the previous code, MyException is a simple exception, with no field, and it simply inherits from Exception::Stringy (all exceptions inherits from it). ExceptionWithFields inherits from MyException, has two fields defined, and throw_fields can be used as a shortcut to throw it.
MyException
ExceptionWithFields
throw_fields
Here are the details about what can be in the exception definitions:
The keys of the definition's hash are reggular class name string, with an exception: they cannot start with a underscore ( _ ), keys starting with an underscore are reserved for options specification (see "ADVANCED OPTIONS");
_
Expects a name (Str). If set, the exception will inherit from the given name. Using this mechanism, an exception class can inherits fields from an other exception class, and add its own fields. Only simple inlheritance is supported.
Expects a list of field names (ArrayRef). If set, the exceptions will be able to set/get these fields. Fields values should be short scalars (no references).
Expects a function name (Str). If set, the user will be able to use this function as a shortcut to throw the exception. From the example above, throw_fields(...) will be equivalent to <ExceptionWithFields-throw(...)>>
throw_fields(...)
<ExceptionWithFields-
Expects a function name (Str). If set, the user will be able to use this name as an alias for the class name. Warning, if you use it, make sure to enclose the declare_exceptions call in a BEGIN block, otherwise the alias won't be available in the rest of the file (it will however be available in other modules loading it)
BEGIN
Expects a boolean (defaults to false). If set to true, then an already registered exception can be updated.
ExceptionWithFields->throw("error message", grandiosity => 42);
The pseudo methods (see below) are loaded when use-ing the Exception::Stringy module:
use
use Exception::Stringy; eval { ... 1; } or do { my $e = $@; if ($e->$xisa('Exception::Stringy')) { if ($e->$xisa('ExceptionWithFields')) { ... } elsif ($e->$xisa('YetAnotherException')) { ... } } else { # this works on anything, even objects or bare strings e->$xrethrow; } };
See "BASIC USAGE" above.
# both are exactly the same ExceptionWithFields->throw("error message", grandiosity => 42); ExceptionWithFields->raise("error message", grandiosity => 42);
Creates a string exception from the given class, with the error message and fields, then throws the exception. The exception is thrown using croak() from the Carp module.
croak()
Carp
The error message is always the first argument. If ommited, it'll default to empty string. Optional fields are provided as flat key / value pairs.
my $e = ExceptionWithFields->new("error message", grandiosity => 42);
Takes the same arguments as throw() but doesn't throw the exception. Instead, the exception is returned.
throw()
my @fields = ExceptionWithFields->registered_fields;
Returns the possible fields that an exception of the given class can have.
my @class_names = Exception::Stringy->registered_exception_classes;
Returns the exceptions classes that have been registered.
The syntax is a bit strange, but that's because exceptions are bare strings, and not blessed references, so we have to use a trick to have the arrow syntax working.
By default, the methods are prefixed by x (mnemonic: eXception) but you can change that by specifying a other method_prefix option at import time (see "IMPORT OPTIONS" below)
x
method_prefix
$exception->$xthrow(); $exception->$xrethrow(); $exception->$xraise();
Throws the exception.
my $class = $exception->$xclass();
Returns the exception class name.
if ($exception->$xisa('ExceptionClass')) {... }
Returns true if the class of the given exception -isa()> the class given in parameter. Otherwise, return false.
-
$xisa is more useful than it seems:
$xisa
When applied on an Exception::Stringy exception, it'll properly etract the exception class and perform the isa call on it.
isa
When applied on a blessed reference, it'll do the right thing, and work like standard isa().
isa()
When applied on something else, it won't die, but return false.
So it means that you can safely use $exception-$xisa('SomeClass')> whatever $exception is, no need to do additional testing on it.
$exception-
$exception
my @fields = $exception->$xfields();
Returns the list of field names that are in the exception.
my $value = $exception->$xfield('field_name'); $exception->$xfield(field_name => $value);
Set or get the given field. If the value contains one of the following caracters, then it is transparently base64 encoded and decoded.
The list of forbidden caracters are:
:
the semicolon
|
the pipe
\034
\034, the 0x28 seperator ASCII caracter.
my $text = $exception->$xmessage(); my $text = $exception->$xerror(); $exception->$xmessage("Error message"); $exception->$xerror("Error message");
Set or get the error message of the exception
It's often useful to gather exceptions declarations in a common module. You can easily do that, because declaring new exceptions is decoupled from use-ing the module. All you need is to implement a module that uses <Exception::Stringy-declare_exceptions(...)>> one or more times. Other modules can then use this module. Note however that they also will have to use Exception::Stringy to have pseudo-methods imported in their namespaces.
<Exception::Stringy-
It's recommended to use normal class names, with a common prefix, like MyException::Network::Connection, and to reflect the inheritance relationship within the name. So MyException::Networking::Connection would inherit from MyException::Networking. Multiple inheritance or roles aren't provided by this package.
MyException::Network::Connection
MyException::Networking::Connection
MyException::Networking
Sometimes, you'll have to work with some of the exceptions being Exception::Stringy exceptions, and some other exceptions being objects (blessedd references), for example coming from Exception::Class.
In this case, I recommend using $xisa method to handle them appropriately, as $exception-$xisa('SomeClass')> will work on any type of $exception, and will not die. Instead, it'll always return a true or false value.
use Exception::Stringy ( method_prefix => 'exception_', ); Exception::Stringy->declare_exceptions('MyException'); my $e = MyException->new("error message"); say $e->$exception_message();
When use-ing this module, you can specify parameters as a Hash. Here is as list of supported options:
If set, pseudo methods imported in the calling methods use the specified prefix. By default, it is x, so methods will look like:
$e->$xthrow(); $e->$xfields(); ...
But if for instance you specify method_prefix to be instead exception_, then imported pseudo methods will be like this:
exception_
$e->$exception_throw(); $e->$exception_fields(); ...
Damien Krotkine <dams@cpan.org>
This software is Copyright (c) 2014 by Damien Krotkine.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
To install Exception::Stringy, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Exception::Stringy
CPAN shell
perl -MCPAN -e shell install Exception::Stringy
For more information on module installation, please visit the detailed CPAN module installation guide.