NAME

SPOPS::Exception - Base class for exceptions in SPOPS

SYNOPSIS

# As a user

use SPOPS::Exception;

eval { $user->save };
if ( $@ ) {
   print "Error: $@",
         "Stack trace: ", $@->trace->as_string, "\n";
}

# Get all exceptions (including from subclasses that don't override
# throw()) since the stack was last cleared

my @errors = SPOPS::Exception->get_stack;
print "Errors found:\n";
foreach my $e ( @errors ) {
   print "ERROR: ", $e->message, "\n";
}

# As a developer

use SPOPS::Exception;

my $rv = eval { $dbh->do( $sql ) };
if ( $@ ) {
    SPOPS::Exception->throw( $@ );
}

# Use the shortcut

use SPOPS::Exception qw( spops_error );
my $rv = eval { $dbh->do( $sql ) };
spops_error( $@ ) if ( $@ );

# Throw an exception that subclasses SPOPS::Exception with extra
# fields

my $rv = eval { $dbh->do( $sql ) };
if ( $@ ) {
    SPOPS::Exception::DBI->throw( $@, { sql    => $sql,
                                        action => 'do' } );
}

# Throw an exception with a longer message and parameters

SPOPS::Exception->throw( "This is a very very very very ",
                         "very long message, even though it ",
                         "doesn't say too much.",
                         { action => 'blah' } );

# Catch an exception, do some cleanup then rethrow it

my $rv = eval { $object->important_spops_operation };
if ( $@ ) {
    my $exception = $@;
    close_this_resource();
    close_that_resource();
    SPOPS::Exception->throw( $exception );
}

DESCRIPTION

This class is the base for all exceptions in SPOPS. An exception is generally used to indicate some sort of error condition rather than a situation that might normally be encountered. For instance, you would not throw an exception if you tried to fetch() a record not in a datastore. But you would throw an exception if the query failed because the database schema was changed and the SQL statement referred to removed fields.

This module replaces SPOPS::Error and the error handling it used. There is a backwards compatible function in place so that the variables get set in SPOPS::Error, but this is not permanent. If you use these you should modify your code ASAP.

You can easily create new classes of exceptions if you like, see SUBCLASSING below.

METHODS

Class Methods

throw( $message, [ $message...], [ \%params ] )

throw( $exception )

This is the main action method and normally the only one you will ever use. The most common use is to create a new exception object with the message consisting of all the parameters concatenated together.

More rarely, you can pass another exception object as the first argument. If you do we just rethrow it -- the original stack trace and all information should be maintained.

Additionally with the common use: if the last argument is a hashref we add the additional information from it to the exception, as supported. (For instance, you can write a custom exception to accept a 'module' parameter which declares which of the plugins to your accounting system generated the error.)

Once we create the exception we then call die with the object. Before calling die we first do the following:

  1. Check \%params for any parameters matching fieldnames returned by get_fields(), and if found set the field in the object to the parameter.

  2. Fill the object with the relevant calling information: package, filename, line, method.

  3. Set the trace property of the object to a Devel::StackTrace object.

  4. Call initialize() so that subclasses can do any object initialization/tracking they need to do. (See SUBCLASSING below.)

  5. Track the object in our internal stack.

get_fields()

Returns a list of property names used for this class. If a subclass wants to add properties to the base exception object, the common idiom is:

my @FIELDS = qw( this that );
My::Custom::Exception->mk_accessors( @FIELDS );
sub get_fields { return ( $_[0]->SUPER::get_fields(), @FIELDS ) }

So that all fields are represented. (The mk_accessors() method is inherited from this class, since it inherits from Class::Accessor.

Object Methods

creation_location

Returns a string with information about where the exception was thrown. It looks like (all on one line):

Created in [%package%] in method [%method%];
at file [%filename%] at line [%line%]

to_string

Return a stringified version of the exception object. The default is probably good enough for most exception objects -- it just returns the message the exception was created with.

However, if the class variable ShowTrace is set to a true value in the exception class, then we also include the output of the as_string() method on a Devel::StackTrace object.

fill_error_variables

You normally do not need to call this since it is done from throw(). This exists only for backward compatibility with SPOPS::Error. The exception fills up the relevant SPOPS::Error package variables with its information.

PROPERTIES

message

This is the message the exception is created with -- there should be one with every exception. (It is bad form to throw an exception with no message.)

package

The package the exception was thrown from.

filename

The file the exception was thrown from.

line

The line number in filename the exception was thrown from.

method

The subroutine the exception was thrown from.

trace

Returns a Devel::StackTrace object. If you set a package variable 'ShowTrace' in your exception then the output of to_string() (along with the stringification output) will include the stack trace output as well as the message.

This output may produce redundant messages in the default to_string() method -- just override the method in your exception class if you want to create your own output.

SUBCLASSING

It is very easy to create your own SPOPS or application errors:

package My::Custom::Exception;

use strict;
use base qw( SPOPS::Exception );

Easy! If you want to include different information that can be passed via new():

package My::Custom::Exception;

use strict;
use base qw( SPOPS::Exception );
my @FIELDS = qw( this that );
My::Custom::Exception->mk_accessors( @FIELDS );

sub get_fields { return ( $_[0]->SUPER::get_fields(), @FIELDS ) }

And now your custom exception can take extra parameters:

My::Custom::Exception->throw( $@, { this => 'bermuda shorts',
                                    that => 'teva sandals' });

If you want to do extra initialization, data checking or whatnot, just create a method initialize(). It gets called just before the die is called in throw(). Example:

package My::Custom::Exception;

# ... as above

my $COUNT = 0;
sub initialize {
    my ( $self, $params ) = @_;
    $COUNT++;
    if ( $COUNT > 5 ) {
        $self->message(
              $self->message . "-- More than five errors?! ($COUNT) Whattsamatta?" );
    }
}

BUGS

None known.

TO DO

Nothing known.

SEE ALSO

Devel::StackTrace

Class::Accessor

Exception::Class for lots of good ideas -- once we get rid of backwards compatibility we will probably switch to using this as a base class.

COPYRIGHT

Copyright (c) 2001-2002 intes.net, inc.. All rights reserved.

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

AUTHORS

Chris Winters <chris@cwinters.com>