Badger

 view release on metacpan or  search on metacpan

lib/Badger/Exception.pm  view on Meta::CPAN

    $text  =~ s/<(\w+)>/defined $self->{ $1 } ? $self->{ $1 } : "(no $1)"/eg;

    # TODO: not sure we should add file and line automatically - better to
    # leave it up to the $FORMAT
    $text .= " in $self->{ file }"      if $self->{ file };
    $text .= " at line $self->{ line }" if $self->{ line };

    if ($self->{ trace } && (my $trace = $self->stack_trace)) {
        $text .= "\n" . $trace;
    }

    return $text;
}


sub stack_trace {
    my $self = shift;
    my @lines;

    if (my $stack = $self->{ stack }) {
        foreach my $caller (@$stack) {
            my @args = $COLOUR
                ? (
                    cyan($caller->[0]),
                    cyan($caller->[1]),
                    yellow($caller->[2]),
                    yellow($caller->[3]),
                  )
                : @$caller;
            push(@lines, $self->message( caller => @args ));
        }
    }

    return join("\n", @lines);
}


sub trace {
    my $self = shift;
    if (ref $self) {
        return @_
            ? ($self->{ trace } = shift )
            :  $self->{ trace };
    }
    else {
        return @_
            ? $self->class->var( TRACE => shift )
            : $self->class->var('TRACE');
    }
}

sub throw {
    my $self = shift;

    # save relevant information from caller stack for enhanced debugging,
    # but only the first time the exception is thrown
    if ($self->{ trace } && ! $self->{ stack }) {
        my @stack;
        my $i = 1;
        while (1) {
            my @info = caller($i++);
            last unless @info;
            push(@stack, \@info);
        }
        $self->{ stack } = \@stack;
    }

    die $self;
}




#------------------------------------------------------------------------
# match_type(@types)
#
# Selects the most appropriate handler for the current exception type,
# from the list of types passed in as arguments.  The method returns the
# item which is an exact match for type or the closest, more
# generic handler (e.g. foo being more generic than foo.bar, etc.)
#------------------------------------------------------------------------

sub match_type {
    my $self  = shift;
    my $types = @_ == 1 ? shift :  [@_];
    my $type  = $self->{ type };

    $types = [ split(DELIMITER, $types) ]
        unless ref $types;

    $types = { map { $_ => $_ } @$types }
        if ref $types eq ARRAY;

    return $self->error( invalid => 'type match' => $types )
        unless ref $types eq HASH;

    while ($type) {
        return $types->{ $type }
            if $types->{ $type };

        # strip .element from the end of the exception type to find a
        # more generic handler
        $type =~ s/\.?[^\.]*$//;
    }

    return undef;
}



1;
__END__

=head1 NAME

Badger::Exception - structured exception for error handling

=head1 SYNOPSIS

    use Badger::Exception;

lib/Badger/Exception.pm  view on Meta::CPAN

(more specific) handler name will always match in preference to a shorter
(more general) handler as shown in the next example:

    $exception->type('database.connection');

    my $match = $exception->match_type('database', 'database.connection')
        || die "no match for exception\n";

    print $match;    # database.connection

When there is no exact match, the C<match_type()> method will return
something more general that matches.  In the following example, there
is no specific handler type for C<database.exploded>, but the more
general C<database> type still matches.

    $exception->type('database.exploded');

    my $match = $exception->match_type('database', 'database.connection')
        || die "no match for exception\n";

    print $match;    # database

You can also specify multiple exception types using a reference to a list.

    if ($exception->match_type(['warp.drive', 'shields'])) {
        ...
    }

Or using a single string of whitespace delimited exception types.

    if ($exception->match_type('warp.drive shields')) {
        ...
    }

You can also pass a reference to a hash array in which the keys are exception
types.  The corresponding value for a matching type will be returned.

    my $type_map = {
        'warp.drive'    => 'propulsion',
        'impulse.drive' => 'propulsion',
        'shields'       => 'defence',
        'phasers'       => 'defence'
    };

    if ($exception->match_type($type_map)) {
        ...
    }

=head2 throw()

This method throws the exception by calling C<die()> with the exception object
as an argument. If the C<$TRACE> flag is set to a true value then the method
will first save the pertinent details from a stack backtrace into the
exception object before throwing it.

=head2 stack()

If stack tracing is enabled then this method will return a reference to a list
of information from the caller stack at the point at which the exception was
thrown. Each item in the list is a reference to a list containing the
information returned by the inbuilt C<caller()> method. See
C<perldoc -f caller> for further information.

    use Badger::Exception trace => 1;

    eval {
        # some code that throws an exception object
        $exception->throw();
    };

    my $catch = $@;                 # exception object
    my $stack = $catch->stack;

    foreach my $caller (@$stack) {
        my ($pkg, $file, $line, @other_stuff) = @$caller;
        # do something
    }

The first set of information relates to the immediate caller of the
L<throw()> method.  The next item is the caller of that method, and so
on.

=head2 stack_trace()

If stack tracing is enabled then this method returns a text string summarising
the caller stack at the point at which the exception was thrown.

    use Badger::Exception trace => 1;

    eval {
        # some code that throws an exception object
        $exception->throw();
    };
    if ($@) {
        print $@->stack_trace;
    }

=head1 AUTHOR

Andy Wardley L<http://wardley.org/>

=head1 COPYRIGHT

Copyright (C) 1996-2009 Andy Wardley.  All Rights Reserved.

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

=head1 SEE ALSO

L<Badger::Base>

=cut

# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:



( run in 1.027 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )