NAME

Error::Base - Simple structured errors with full backtrace

VERSION

This document describes Error::Base version 0.0.3

SYNOPSIS

use Error::Base;
Error::Base->crash('Sanity check failed');  # die() with backtrace

my $err     = Error::Base->new('Foo');      # construct object first
    yourcodehere(...);                  # ... do other stuff
$err->crash;                                # as object method

my $err     = Error::Base->new(
                'Foo error',                # args start with text
                -quiet    => 1,             # no backtrace
                grink     => 'grunt',       # store somethings
                puppy     => 'dog',         # your keys, no leading dash 
            );
$err->crash;

$err->crank;                    # get cranky: warn() but don't die()
my $err = Error::Base->crank('Me!');        # also a constructor

eval{ Error::Base->crash( 'car', -foo => 'bar' ) }; 
my $err     = $@ if $@;         # catch and examine the object

DESCRIPTION

    J'avais cru plus difficile de mourir. -- Louis XIV

Die early, die often. Make frequent sanity checks and die when a check fails. See neat dumps of the caller stack with each error. Construct a group of error messages in one object or write error text ad hoc. Trap an error object and examine the contents; or let it tell its sad tale and end it.

Error::Base usage can be simple or complex. For quick sanity checks, construct and throw a simple fatal error in one line. At the other extreme, you can override methods in your own error subclasses.

Error::Base is lightweight. It defines no global variables, uses no non-core modules (and few of those), exports no symbols, and is purely object-oriented. I hope you will be able to use it commonly instead of a simple die(). You are not required to subclass it.

METHODS

new()

my $err     = Error::Base->new('Foo');      # constructor
my $err     = Error::Base->new(             # with named args
                -text       => 'Bar error: ',
                -quiet      => 1,
                -top        => 3,
                -prepend    => '@! Globalcorpcoapp: ',
                -indent     => '@!                   ',
                foo         => bar,
            );
my $err     = Error::Base->new(             # okay to pass both
                    'bartender: '           # lone string first...
                -text   => 'Bar error: ',   # ... and named args
                _beer   => 'out of beer',   # your private attribute(s)
            );

The constructor must be called as a class method; there is no mutator returning a new object based on an old one. You do have some freedom in how you call, though.

Called with an even number of args, they are all considered key/value pairs. Keys with leading dash ('-') are reserved for use by Error::Base; all others are free to use as you see fit. Error message text is stored in -text as a single string.

Called with an odd number of args, the first arg is shifted off and appended to the error message text. This shorthand may be offensive to some; in which case, don't do that.

You may stash any arbitrary data inside the returned object (during construction or later) and do whatever you like with it. You might choose to supply additional optional texts for later access.

See PARAMETERS.

crash()

Error::Base->crash('Sanity check failed');  # as class method
my $err = Error::Base->crash('Flat tire:'); # also a constructor
$err->crash;                                # as object method
$err->crash(                    # all the same args are okay in call
            'bartender: '
        -text   => 'Bar error: ',
        -key    => '_beer',                 # append additional text
    );
eval{ $err->crash }; 
my $err     = $@ if $@;         # catch and examine the object

crash() and other public methods may be called as class or object methods. If called as a class method, then new() is called internally. Call new() yourself first if you want to call crash() as an object method.

crash() is a very thin wrapper, easy to subclass. It differs from similar methods in that instead of returning its object, it die()-s with it. If uncaught, the error will stringify; if caught, the entire object is yours.

crank()

Error::Base->crank('More gruel!');          # as class method
$err->crank;                                # as object method
my $err = Error::Base->crank('Me!');        # also a constructor

This is exactly like crash() except that it warn()s instead of die()-ing. Therefore it can also usefully be used as a constructor of an object for later use.

crank() is also a very thin wrapper. You may subclass it; you may catch the entire object or let it stringify.

cuss()

my $err = Error::Base->cuss('x%@#*!');      # also a constructor

Again, exactly like crash() or crank() except that it neither die()-s nor warn()s; it only returns the object.

The difference between new() and the other methods is that new() returns the constructed object containing only what was passed in as arguments. crash(), crank(), and cuss() perform a full stack backtrace (if not passed -quiet) and format the result for stringified display.

You may find cuss() useful in testing your subclass or to see how your error will be thrown without the bother of actually catching crash().

init()

$err->init(@args);

Probably, it is not useful to call this object method directly. Perhaps you might subclass it or call it from within your subclass constructor. The calling conventions are exactly the same as for the other public methods.

PARAMETERS

All public methods accept the same arguments, with the same conventions. All parameter names begin with a leading dash ('-'); please choose other names for your private keys.

If the same parameter is set multiple times, the most recent argument completely overwrites the previous:

my $err     = Error::Base->new( -top    => 3, );
    # -top is now 3
$err->cuss(  -top    => 0, );
    # -top is now 0
$err->crank( -top    => 1, );
    # -top is now 1

The exceptions are the various ways of setting -text. Later assignments will be appended to previous assignments. To clear out the previous value:

delete $err->{-text};

You are cautioned that deleting other keys may be unwise.

-text

scalar string default: 'Undefined error'

$err->crash;                        # emits 'Undefined error'
$err->crash('Foo');                 # emits 'Foo'
$err->crash( -text => 'Bar');       # emits 'Bar'
$err->crash(
          zap   => 'Yip',
          -key  => 'zap',
      );                            # emits 'Yip'

The value of -text is printed in the first line of the stringified error object after a call to crash(), crank(), or cuss(). As a convenience, if the number of arguments passed in is odd, then the first arg is shifted off and assigned to -text. This is done to simplify writing one-off, one-line sanity checks:

open( my $in_fh, '<', $filename )
    or Error::Base->crash("Couldn't open $filename for reading.");

Either way, it is expected that the argument be a single scalar. If you need to pass a multi-line string then please embed escaped newlines ('\n').

-key

scalar string default: undef

my $err     = Error::Base->new(
                _err00  => 'frobnitz error',
                _err01  => 'What, me worry?',
            );
$assertion      or $err->crash( -key => '_err00' );
$expectation    or $err->crank( -key => '_err01' );

You may store arbitrary error text against arbitrary keys and access them later with -key. This may suit you if you like to group all your error messages in one place; you need only create a single object to hold them all. The value of whatever key you pass will be assigned to -text. The leading underbar ('_') is merely a suggested convention.

Note that multiple assignments to -text are currently supported but the exact approach may change. Currently, they are all concatenated.

-quiet

scalar boolean default: undef

$err->crash( -quiet         => 1, );        # no backtrace

By default, you get a full stack backtrace. If you want none, set this parameter. Only -text will be emitted.

-top

scalar unsigned integer default: 2

$err->crash( -top           => 0, );        # really full backtrace

By default, you get a full stack backtrace: "full" meaning, from the point of invocation. Some stack frames are added by the process of crash()-ing itself; by default, these are not seen. If you want more or fewer frames you may set this parameter.

Beware that future implementations may change the number of stack frames added internally by Error::Base; and also you may see a different number of frames if you subclass, depending on how you do that. The safe way:

my $err     = Error::Base->new('Foo');      # construct object
$err->{ -top => ($err->{-top})++ };         # drop the first frame
$err->crash();

This is ugly and you may get a convenience method in future.

-prepend

scalar string default: undef

-indent

scalar string default: first char of -prepend, padded with spaces to length

-prepend_all

scalar string default: undef

my $err     = Error::Base->new(
                -prepend    => '#! Globalcorpcoapp: ',
            );
$err->crash ('Boy Howdy!');
    # emits '@! Globalcorpcoapp: Boy Howdy!
    #        @                   in main::fubar at line 42    [test.pl]'

Any string passed to -prepend will be prepended to the first line only of the formatted error message. If -indent is defined then that will be prepended to all following lines. If -indent is undefined then it will be formed from the first character only of -prepend, padded with spaces to the length of -prepend. -prepend_all will be prepended to all lines.

This is a highly useful feature that improves readability in the middle of a dense dump. So the default may be changed to form -prepend in some way if not defined. If you are certain you want no prepending or indentation, pass the empty string, q{}.

OTHER KEYS

-lines

array of strings

The formatted error message, fully expanded.

-frames

array of hashrefs

The raw stack dump.

SUBCLASSING

use base 'Error::Base';
sub init{
    my $self    = shift;
    _munge_my_args(@_);
    $self->SUPER::init(@_);
    return $self;
};

While useful standing alone, Error::Base is written to be subclassed, if you so desire. Perhaps the most useful method to subclass may be init(). You might also subclass crash(), crank(), or cuss() if you want to do something first:

use base 'Error::Base';
sub crash{
    my $self    = _fuss(@_);
    $self->a_kiss_before_dying();
    die $self;
};

The author hopes that most users will not be driven to subclassing but if you do so, successfully or not, please be so kind as to notify.

SEE ALSO

Many error-related modules are available on CPAN. Some do bizarre things.

Error is self-deprecated in its own POD as "black magic"; which recommends Exception::Class instead.

Exception installs a $SIG{__DIE__} handler that converts text passed to die into an exception object. It permits environment variables and setting global state; and implements a try syntax. This module may be closest in spirit to Error::Base. For some reason, I can't persuade cpan to find it.

Carp is well-known and indeed, does a full backtrace with confess(). The better-known carp() may be a bit too clever and in any case, the dump is not formatted to my taste. The module is full of global variable settings. It's not object-oriented and an error object can't easily be pre-created.

The pack leader seems to be Exception::Class. Error::Base differs most strongly in that it has a shorter learning curve (since it does much less); confines itself to error message emission (catching errors is another job); and does a full stack backtrace dump by default. Less code may also be required for simple tasks.

To really catch errors, I like Test::Trap ('block eval on steroids'). It has a few shortcomings but is extremely powerful. I don't see why its use should be confined to testing.

The line between emitting a message and catching it is blurred in many related modules. I did not want a jack-in-the-box object that phoned home if it was thrown under a full moon. The only clever part of an Error::Base object is that it stringifies.

It may be true to say that many error modules seem to expect to be caught. I usually expect my errors to cause all execution to come to a fatal, non-recoverable crash. Oh, yes; I agree it's sometimes needful to catch such errors, especially during testing. But if you're regularly throwing and catching, the term 'exception' may be appropriate but perhaps not 'error'.

INSTALLATION

This module is installed using Module::Build.

DIAGNOSTICS

This module emits error message for you; it is hoped you won't encounter any from within itself.

Error::Base internal error: excessive backtrace:

You attempted to dump too many frames of backtrace. You probably mis-set -top, rational values of which are perhaps 0..5.

Error::Base internal error: unpaired args:

You do not have to pass paired args to most public methods. You probably passed an odd number of args to a private method.

CONFIGURATION AND ENVIRONMENT

Error::Base requires no configuration files or environment variables.

DEPENDENCIES

There are no non-core dependencies.

version 0.94 # Perl extension for Version Objects

overload # Overload Perl operations

Scalar::Util # General-utility scalar subroutines

This module should work with any version of perl 5.8.8 and up.

INCOMPATIBILITIES

None known.

BUGS AND LIMITATIONS

This is a very early release. Reports will be warmly welcomed.

Please report any bugs or feature requests to bug-error-base@rt.cpan.org, or through the web interface at http://rt.cpan.org.

THANKS

Grateful acknowledgement deserved by AMBRUS for coherent API suggestions. Any failure to grasp them is mine.

AUTHOR

Xiong Changnian <xiong@cpan.org>

LICENSE

Copyright (C) 2011 Xiong Changnian <xiong@cpan.org>

This library and its contents are released under Artistic License 2.0:

http://www.opensource.org/licenses/artistic-license-2.0.php