package Error::Base::Cookbook;
use 5.008008;
use strict;
use warnings;
use version; our $VERSION = qv('v1.0.2');
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# Do not use this module directly. It only implements the POD snippets. #
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
my @td ;
sub _get_test_data { @td };
#~ -end => 1, # # # # # # # END TESTING HERE # # # # # # # # #
#~ -do => 1,
#----------------------------------------------------------------------------#
sub _words { # sloppy match these strings
my @words = @_;
my $regex = q{};
for (@words) {
$_ = lc $_;
$regex = $regex . $_ . '.*';
};
return qr/$regex/is;
};
#----------------------------------------------------------------------------#
=head1 NAME
Error::Base::Cookbook - Examples of Error::Base usage
=head1 VERSION
This document describes Error::Base version v1.0.2
=head1 WHAT'S NEW
=over
=item *
Update examples to track API changes.
=back
=head1 DESCRIPTION
Basic use of L<Error::Base|Error::Base> is quite simple;
and advanced usage is not hard.
The author hopes that nobody is forced to consult this Cookbook. But I am
myself quite fond of cookbook-style documentation; I get more from seeing it
all work together than from cut-and-dried reference manuals. I like those too,
though; and comprehensive reference documentation is found in
L<Error::Base|Error::Base>.
If you make use of Error::Base and don't find a similar example here in its
Cookbook, please be so kind as to send your use case to me for future
inclusion. Thank you very much.
=head1 EXAMPLES
The examples shown here in POD are also present as executable code.
=head2 Sanity Check
=cut
{ #
push @td, {
-case => 'sanity-zero',
-do => 1,
-code => sub{
#
my $obviously_true = 0;
Error::Base->crash('Unexpected zero')
unless $obviously_true;
#
},
-lby => 'die',
-want => qr/Unexpected zero/,
};
} #
=pod
my $obviously_true = 0;
Error::Base->crash('Unexpected zero')
unless $obviously_true;
You are certain that this will never happen but you decide to check it anyway.
No need to plan ahead; just drop in a sanity check.
=cut
{ #
my ($case1, $case2, $case3, $pointer);
push @td, {
-case => 'sanity-case',
-do => 1,
-code => sub{
#
if ( $case1 ) { $pointer++ }
elsif ( $case2 ) { $pointer-- }
elsif ( $case3 ) { }
else { Error::Base->crash('Unimplemented case') };
#
},
-lby => 'die',
-want => qr/Unimplemented case/,
};
} #
=pod
if ( $case1 ) { $pointer++ }
elsif ( $case2 ) { $pointer-- }
elsif ( $case3 ) { }
else { Error::Base->crash('Unimplemented case') };
In constructs like this, it's tempting to think you've covered every possible
case. Avoid this fallacy by checking explicitly for each implemented case.
Error::Base->crash; # emits 'Undefined error.' with backtrace.
Don't forget to pass some error message text. Unless you're in real big foo.
=head2 Construct First
=cut
{ #
push @td, {
-case => 'construct-first-foo',
-do => 1,
-code => sub{
#
my $err = Error::Base->new('Foo');
$err->crash;
#
},
-lby => 'die',
-want => qr/Foo/,
};
} #
{ #
push @td, {
-case => 'construct-first-123',
-do => 1,
-code => sub{
#
my $err = Error::Base->new(
'Third',
-base => 'First',
-type => 'Second',
);
$err->crash;
#
},
-lby => 'die',
-want => qr/First Second Third/,
};
} #
=pod
my $err = Error::Base->new('Foo');
$err->crash;
my $err = Error::Base->new(
'Third',
-base => 'First',
-type => 'Second',
);
$err->crash;
If you like to plan your error ahead of time, invoke
L<new()|Error::Base/new()> with any set of arguments you please.
This will help keep your code uncluttered.
=head2 Construct and Throw in One Go
=cut
{ #
push @td, {
-case => 'one-go',
-do => 1,
-code => sub{
#
Error::Base->crash(
-base => 'First',
-type => 'Second',
-mesg => 'Third',
);
#
},
-lby => 'die',
-want => qr/First Second Third/,
};
} #
=pod
Error::Base->crash(
-base => 'First',
-type => 'Second',
-mesg => 'Third',
);
You aren't I<required> to construct first, though. Each of the public methods
L<crash()|Error::Base/crash()>, L<crank()|Error::Base/crank()>,
and L<cuss()|Error::Base/cuss()> function as constructors and may be called
either as a class or object method. Each method accepts all the same
parameters as L<new()|Error::Base/new()>.
=head2 Avoiding Death
=cut
{ #
push @td, {
-case => 'avoid-death-crank-gruel',
-do => 1,
-code => sub{
#
Error::Base->crank('More gruel!'); # as class method
#
},
-lby => 'warn',
-want => qr/More gruel!/,
};
} #
{ #
push @td, {
-case => 'avoid-death-un-err',
-do => 1,
-code => sub{
#
my $err = Error::Base->new;
$err->crank; # as object method
#
},
-lby => 'warn',
-want => qr/Undefined error/,
};
} #
{ #
push @td, {
-case => 'avoid-death-tommy',
-do => 1,
-code => sub{
#
my $err = Error::Base->new('See me');
$err->cuss('Feel me'); # trace stored in object
#
},
-lby => 'return-scalar',
-want => qr/Feel me/,
};
} #
{ #
push @td, {
-case => 'avoid-death-cusswords',
-do => 1,
-code => sub{
#
my $err = Error::Base->cuss('x%@#*!'); # also a constructor
#
},
-lby => 'return-object',
-want => _words(qw/
bless frames eval file line package sub
lines
at line
at line
____ at line
error base
/),
};
} #
=pod
Error::Base->crank('More gruel!'); # as class method
my $err = Error::Base->new;
$err->crank; # as object method
my $err = Error::Base->new('See me');
$err->cuss('Feel me'); # trace stored in object
my $err = Error::Base->cuss('x%@#*!'); # also a constructor
L<crank()|Error::Base/crank()> B<warn>s of your error condition. Perhaps it's
not that serious. The current fashion is to make almost all errors fatal but
it's your call.
L<cuss()|Error::Base/cuss()> neither B<die>s nor B<warn>s but it does perform
a full backtrace from the point of call. You might find it most useful when
debugging your error handling itself; substitute 'crash' or 'crank' later.
=head2 Escalation
=begin fool_pod_coverage
Really think we don't want to document the dummies.
=head2 cook_dinner
=head2 serve_chili
=head2 add_recipie
=end fool_pod_coverage
=cut
# dummy subs
sub cook_dinner {};
sub serve_chili {};
sub add_recipie {};
#~ my $err = Error::Base->new( -base => 'Odor detected:', -quiet => 1 );
#~ my $err = Error::Base->new( -base => 'Odor detected:' );
{ #
my $err = Error::Base->new( -base => 'Odor detected:' );
my ( $fart, $room, $fire ) = ( 0, 0, 0 );
push @td, { # no fart
-case => 'escalate-odor',
-do => 1,
-code => sub{
#
cook_dinner;
$err->init( _cooked => 1 );
serve_chili('mild');
$err->cuss ( -type => $fart ) if $fart;
$err->crank( -type => 'Air underflow' ) if $fart > $room;
add_recipie( $err );
serve_chili('hot');
$err->crash( -type => 'Evacuate now' ) if $fire;
#
$err;
},
-lby => 'return-object',
-want => qr/Odor detected:/,
};
} #
{ #
my $err = Error::Base->new( -base => 'Odor detected:' );
my ( $fart, $room, $fire ) = ( 1, 1, 0 );
push @td, { # some fart
-case => 'escalate-fart',
-do => 1,
-code => sub{
#
cook_dinner;
$err->init( _cooked => 1 );
serve_chili('mild');
$err->cuss ( -type => $fart ) if $fart;
$err->crank( -type => 'Air underflow' ) if $fart > $room;
add_recipie( $err );
serve_chili('hot');
$err->crash( -type => 'Evacuate now' ) if $fire;
#
$err;
},
-lby => 'return-object',
-want => qr/Odor detected: 1/,
};
} #
{ #
my $err = Error::Base->new( -base => 'Odor detected:' );
my ( $fart, $room, $fire ) = ( 5, 1, 0 );
push @td, { # too much fart
-case => 'escalate-room',
-do => 1,
-code => sub{
#
cook_dinner;
$err->init( _cooked => 1 );
serve_chili('mild');
$err->cuss ( -type => $fart ) if $fart;
$err->crank( -type => 'Air underflow' ) if $fart > $room;
add_recipie( $err );
serve_chili('hot');
$err->crash( -type => 'Evacuate now' ) if $fire;
#
$err;
},
-lby => 'return-object',
-want => qr/Odor detected: Air underflow/,
-cranky => 1,
};
} #
{ #
my $err = Error::Base->new( -base => 'Odor detected:' );
my ( $fart, $room, $fire ) = ( 0, 0, 1 );
push @td, { # FIRE
-case => 'escalate-fire',
-do => 1,
-code => sub{
#
cook_dinner;
$err->init( _cooked => 1 );
serve_chili('mild');
$err->cuss ( -type => $fart ) if $fart;
$err->crank( -type => 'Air underflow' ) if $fart > $room;
add_recipie( $err );
serve_chili('hot');
$err->crash( -type => 'Evacuate now' ) if $fire;
#
},
-lby => 'die',
-want => qr/Odor detected: Evacuate now/,
-cranky => 1,
};
} #
=pod
my $err = Error::Base->new( -base => 'Odor detected:' );
cook_dinner;
$err->init( _cooked => 1 );
serve_chili('mild');
$err->cuss ( -type => $fart ) if $fart;
$err->crank( -type => 'Air underflow' ) if $fart > $room;
add_recipie( $err );
serve_chili('hot');
$err->crash( -type => 'Evacuate now' ) if $fire;
Once constructed, the same object may be thrown repeatedly, with multiple
methods. On each invocation, new arguments overwrite old ones but previously
declared attributes, public and private, remain in force if not overwritten.
Also on each invocation, the stack is traced afresh and the error message text
re-composed and re-formatted.
=head2 Trapping the Fatal Error Object
=cut
{ #
push @td, {
-case => 'eval',
-do => 1,
-code => sub{
#
eval{ Error::Base->crash('Houston...') }; # trap...
my $err = $@ if $@; # ... and examine the object
#
},
-lby => 'return-object',
-want => _words(qw/
bless frames eval file line package sub
lines
houston
at line
at line
____ at line
error base
/),
};
} #
=pod
eval{ Error::Base->crash('Houston...') }; # trap...
my $err = $@ if $@; # ... and examine the object
L<crash()|Error::Base/crash()> does, internally, construct an object if called
as a class method. If you trap the error you can capture the object and look
inside it.
=head2 Backtrace Control
=cut
{ #
my $err = Error::Base->new;
push @td, {
-case => 'backtrace-quiet',
-do => 1,
-code => sub{
#
$err->crash( -quiet => 1, ); # no backtrace
#
},
-lby => 'die',
-want => qr/Undefined error\.$/,
};
} #
{ #
my $err = Error::Base->new;
push @td, {
-case => 'backtrace-nest(-2)',
-do => 1,
-code => sub{
#
$err->crash( -nest => -2, ); # really full backtrace
#
},
-lby => 'die',
-want => _words(qw/
undefined error
error base fuss at line
error base crash at line
at line
at line
____ at line
/),
};
} #
{ # # this test could be better: but implementation will change
my $err = Error::Base->new;
push @td, {
-case => 'backtrace-nest(+2)',
-do => 1,
-code => sub{
#
$err->crash( -nest => +2, ); # skip top five frames
#
},
-lby => 'die',
-want => _words(qw/
undefined error
at line
at line
____ at line
/),
};
} #
=pod
$err->crash( -quiet => 1, ); # no backtrace
$err->crash( -nest => -2, ); # really full backtrace
$err->crash( -nest => +2, ); # skip two more top frames
Set L<-quiet|Error::Base/-quiet> to any TRUE value to silence stack
backtrace entirely.
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
L<-nest|Error::Base/-nest> to a different value.
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.
=cut
=head2 Wrapper Routine
=cut
{ #
sub _crash { Error::Base->crash( @_, -nest => +1 ) };
my $obviously_true;
push @td, {
-case => 'wrapper',
-do => 1,
-code => sub{
#
# ... later...
_crash('Unexpected zero')
unless $obviously_true;
#
},
-lby => 'die',
-want => qr/Unexpected zero/,
};
} #
=pod
sub _crash { Error::Base->crash( @_, -nest => +1 ) };
# ... later...
_crash('Unexpected zero')
unless $obviously_true;
Write a wrapper routine when trying to wedge sanity checks into dense code.
Error::Base is purely object-oriented and exports nothing.
Don't forget to use L<-nest|Error::Base/-nest> to drop additional frames
if you don't want to see the wrapper in your backtrace.
=head2 Dress Left
=cut
{ #
push @td, {
-case => 'prepend-only',
-do => 1,
-code => sub{
#
Error::Base->crash (
-mesg => 'Let\'s eat!',
-prepend => '@! Black Tie Lunch:',
);
# emits "@! Black Tie Lunch: Let's eat!
# @ in main::fubar at line 42 [test.pl]"
#
},
-lby => 'die',
-want => qr/\@! Black Tie Lunch: Let's eat!.\@ in/s,
};
} #
{ #
push @td, {
-case => 'prepend-indent',
-do => 1,
-code => sub{
#
Error::Base->crash (
-mesg => 'Let\'s eat!',
-prepend => '@! Black Tie Lunch:',
-indent => '%--'
);
# emits "@! Black Tie Lunch: Let's eat!
# %-- in main::fubar at line 42 [test.pl]"
#
},
-lby => 'die',
-want => qr/\@! Black Tie Lunch: Let's eat!.%-- in/s,
};
} #
{ #
push @td, {
-case => 'indent-only',
-do => 1,
-code => sub{
#
Error::Base->crash (
-mesg => 'Let\'s eat!',
-indent => '%--'
);
# emits "%-- Let's eat!
# %-- in main::fubar at line 42 [test.pl]"
#
},
-lby => 'die',
-want => qr/%-- Let's eat!.%-- in/s,
};
} #
=pod
Error::Base->crash (
-mesg => 'Let\'s eat!',
-prepend => '@! Black Tie Lunch:',
);
# emits "@! Black Tie Lunch: Let's eat!
# @ in main::fubar at line 42 [test.pl]"
Error::Base->crash (
-mesg => 'Let\'s eat!',
-prepend => '@! Black Tie Lunch:',
-indent => '%--'
);
# emits "@! Black Tie Lunch: Let's eat!
# %-- in main::fubar at line 42 [test.pl]"
Error::Base->crash (
-mesg => 'Let\'s eat!',
-indent => '%--'
);
# emits "%-- Let's eat!
# %-- in main::fubar at line 42 [test.pl]"
Any string passed to L<-prepend|Error::Base/-prepend> will be prepended to
the first line only of the formatted error message.
If L<-indent|Error::Base/-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)
and (padded with spaces to the length of -prepend).
If only -indent is defined then it will be prepended to all lines.
You can override default actions by passing the empty string.
=head2 Message Composition
=cut
{ #
my $err = Error::Base->new;
push @td, {
-case => 'null',
-do => 1,
-code => sub{
#
$err->crash; # 'Undefined error'
#
},
-lby => 'die',
-want => qr/Undefined error/s,
};
} #
{ #
my $err = Error::Base->new;
push @td, {
-case => 'pronto-only',
-do => 1,
-code => sub{
#
$err->crash( 'Pronto!' ); # 'Pronto!'
#
},
-lby => 'die',
-want => qr/Pronto!/s,
};
} #
{ #
my $err = Error::Base->new;
push @td, {
-case => 'base-and-type',
-do => 1,
-code => sub{
#
$err->crash(
-base => 'Bar',
-type => 'last call',
); # 'Bar last call'
#
},
-lby => 'die',
-want => qr/Bar last call/s,
};
} #
{ #
my $err = Error::Base->new;
push @td, {
-case => 'base-type-pronto',
-do => 1,
-code => sub{
#
$err->crash(
'Pronto!',
-base => 'Bar',
-type => 'last call',
); # 'Bar last call Pronto!'
#
},
-lby => 'die',
-want => qr/Bar last call Pronto!/s,
};
} #
{ #
my $err = Error::Base->new;
push @td, {
-case => 'base-type-mesg',
-do => 1,
-code => sub{
#
$err->crash(
-base => 'Bar',
-type => 'last call',
-mesg => 'Pronto!',
); # 'Bar last call Pronto!'
#
},
-lby => 'die',
-want => qr/Bar last call Pronto!/s,
};
} #
{ #
my $err = Error::Base->new;
push @td, {
-case => 'mesg-aryref',
-do => 1,
-code => sub{
#
my ( $n1, $n2, $n3 ) = ( 'Huey', 'Dewey', 'Louie' );
$err->crash(
-mesg => [ 'Meet', $n1, $n2, $n3, 'tonight!' ],
); # 'Meet Huey Dewey Louie tonight!'
#
},
-lby => 'die',
-want => qr/Meet Huey Dewey Louie tonight!/s,
};
} #
=pod
my $err = Error::Base->new;
$err->crash; # 'Undefined error'
$err->crash( 'Pronto!' ); # 'Pronto!'
$err->crash(
-base => 'Bar',
-type => 'last call',
); # 'Bar last call'
$err->crash(
'Pronto!',
-base => 'Bar',
-type => 'last call',
); # 'Bar last call Pronto!'
$err->crash(
-base => 'Bar',
-type => 'last call',
-mesg => 'Pronto!',
); # 'Bar last call Pronto!'
my $err = Error::Base->new;
my ( $n1, $n2, $n3 ) = ( 'Huey', 'Dewey', 'Louie' );
$err->crash(
-mesg => [ 'Meet', $n1, $n2, $n3, 'tonight!' ],
); # 'Meet Huey Dewey Louie tonight!'
As a convenience, if the number of arguments passed in is odd, then the first
arg is shifted off and appnended to the error message. This is done to
simplify writing one-off, one-line
L<sanity checks|Error::Base::Cookbook/Sanity Check>.
For a little more structure, you may pass values to
L<-base|Error::Base/-base>,
L<-type|Error::Base/-type>, and
L<-mesg|Error::Base/-mesg>.
All values supplied will be joined; by default, with a single space.
If you pass an array reference to C<-mesg> then you can print out
any number of strings, one after the other.
=cut
{ #
push @td, {
-case => 'pep-boys',
-do => 1,
-code => sub{
#
my $err = Error::Base->new(
'Manny',
-base => 'Pep Boys:',
);
$err->init('Moe');
$err->crash('Jack'); # emits 'Pep Boys: Jack' and backtrace
#
},
-lby => 'die',
-want => qr/Pep Boys: Jack/,
};
} #
=pod
my $err = Error::Base->new(
'Manny',
-base => 'Pep Boys:',
);
$err->init('Moe');
$err->crash('Jack'); # emits 'Pep Boys: Jack' and backtrace
Remember, new arguments overwrite old values. The L<init()|Error::Base/init()>
method can be called directly on an existing object to overwrite object
attributes without expanding the message or tracing the stack. If you I<mean>
to expand and trace without throwing, invoke L<cuss()|Error::Base/cuss()>.
=head2 Interpolation in Scope
=cut
{ #
push @td, {
-case => 'interpolation-in-scope',
-do => 1,
-code => sub{
#
my $filename = 'debug246.log';
open( my $in_fh, '<', $filename )
or Error::Base->crash("Failed to open $filename for reading.");
#
},
-lby => 'die',
-want => qr/Failed to open debug246\.log for reading\./,
};
} #
=pod
my $filename = 'debug246.log';
open( my $in_fh, '<', $filename )
or Error::Base->crash("Failed to open $filename for reading.");
Nothing special here; as usual, double quotes interpolate a variable that is
in scope at the place where the error is thrown.
=head2 Late Interpolation
=begin fool_pod_coverage
Really think we don't want to document the dummies.
=head2 bar
=end fool_pod_coverage
=cut
{ #
push @td, {
-case => 'late-interpolation',
-do => 1,
-code => sub{
#
my $err = Error::Base->new(
'Failed to open $filename for reading.',
-base => 'My::Module error:',
);
bar($err);
#
},
-lby => 'die',
-want => qr/Failed to open debug246\.log for reading\./,
};
sub bar {
my $err = shift;
my $filename = 'debug246.log';
# open( my $in_fh, '<', $filename )
# or
$err->crash(
'$filename' => \$filename,
); # 'Failed to open debug246.log for reading.'
};
} #
=pod
my $err = Error::Base->new(
'Failed to open $filename for reading.',
-base => 'My::Module error:',
);
bar($err);
sub bar {
my $err = shift;
my $filename = 'debug246.log';
open( my $in_fh, '<', $filename )
or $err->crash(
'$filename' => \$filename,
); # 'Failed to open debug246.log for reading.'
If we want to declare lengthy error text well ahead of time, double-quotey
interpolation will serve us poorly. In the example, C<$filename> isn't in
scope when we construct C<$err>. Hey, we don't even know what the filename
will be.
Enclose the string to be late-interpolated in B<single quotes> (to avoid a
failed attempt to interpolate immediately) and pass the value when you have
it ready, in scope. For clarity, I suggest you pass a reference to the
I<variable> C<$foo> as the value of the I<key> C<'$foo'>.
The key must be quoted to avoid it being parsed as a variable.
As with normal, in-scope interpolation, you can late-interpolate scalars,
arrays, array slices, hash slices, or various escape sequences. There is the
same potential for ambiguity, since the actual interpolation is
eventually done by perl.
See L<perlop/Quote and Quote-like Operators>.
Late interpolation is performed I<after> the entire error message is composed
and I<before> any prepending, indentation, line-breaking, or stack tracing.
=cut
{ #
push @td, {
-case => 'late-interpolate-all',
-do => 1,
-code => sub{
#
my $err = Error::Base->new(
'$sca' => 'one',
'@ary_ref' => [ 'white', 'black' ],
'%hash_ref' => { hash => 'hog', toe => 'jam' },
);
$err->crash( '|$sca|@ary_ref|$ary_ref[1]|@hash_ref{ qw/ hash toe / }|' );
# emits '|one|white black|black|hog jam|'
#
},
-lby => 'die',
-want => qr/|one|white black|black|hog jam|/,
};
} #
=pod
my $err = Error::Base->new(
'$sca' => 'one',
'@ary_ref' => [ 'white', 'black' ],
'%hash_ref' => { hash => 'hog', toe => 'jam' },
);
$err->crash( '|$sca|@ary_ref|$ary_ref[1]|@hash_ref{ qw/ hash toe / }|' );
# emits '|one|white black|black|hog jam|'
You may use scalar or array placeholders, signifying them with the usual
sigils. Although you pass a reference, use the appropriate
C<$>, C<@> or C<%> sigil to lead the corresponding key. As a convenience, you
may pass simple scalars directly. (It's syntactically ugly to pass a
reference to a literal scalar.) Any value that is I<not> a
reference will be late-interpolated directly; anything else will be
deferenced (once).
This is Perlish interpolation, only delayed. You can interpolate escape
sequences and anything else you would in a double-quoted string. You can pass
a reference to a package variable; but do so against a simple key such as
C<'$aryref'>.
=cut
{ #
push @td, {
-case => 'late-interpolate-self',
-do => 1,
-code => sub{
#
my $err = Error::Base->new(
'$trigger' => 1,
-base => 'Trouble:',
-type => 'Right here in $self->{_where}!',
);
$err->crash( _where => 'River City' );
# emits 'Trouble: Right here in River City!'
#
},
-lby => 'die',
-want => qr/Trouble: Right here in River City!/,
};
} #
=pod
my $err = Error::Base->new(
'$trigger' => 1, # unused key triggers "late"
-base => 'Trouble:',
-type => 'Right here in $self->{_where}!',
);
$err->crash( _where => 'River City' );
# emits 'Trouble: Right here in River City!'
As a further convenience, you may interpolate a value from the error object
itself. In the previous example, C<< '$self->{_where}' >> is late-interpolated
into C<< -type >> (please note the single quotes). And also,
C<< _where >> is defined as C<< 'River City' >>.
B<Note> that Error::Base has no idea what you have called your error object
(perhaps '$err'); use the placeholder C<< '$self' >>
in the string to be expanded.
Don't forget to store your value against the appropriate key!
This implementation of this feature does not peek into your pad.
You may not receive an 'uninitialized' warning if a value is missing.
If you don't like this feature, don't use it and it won't bug you.
B<Currently> you must pass a sigiled key to trigger late interpolation.
=head2 Local List Separator
=cut
{ #
push @td, {
-case => 'local-list-separator',
-do => 1,
-code => sub{
#
local $" = '=';
Error::Base->crash(
'Third',
-base => 'First',
-type => 'Second',
); # emits 'First=Second=Third'
#
},
-lby => 'die',
-want => qr/First=Second=Third/,
};
} #
=pod
local $" = '=';
Error::Base->crash(
'Third',
-base => 'First',
-type => 'Second',
); # emits 'First=Second=Third'
Rationally, I think, message parts should be joined by a single space.
Note that array elements are normally interpolated, separated by spaces.
Perl uses the value of C<$"> (C<$LIST_SEPARATOR>).
If, for some reason, you wish to see message parts and interpolated elements
joined by something else, localize C<$">.
=head1 EXAMPLE CODE
This module contains executable code matching each snippet you see in POD;
this code is exercised by the Error::Base test suite. You're welcome to look.
Please, don't try to C<use> the ::Cookbook!
=head1 DEMO
Included in this distribution is a script, C<error-base-demo.pl>;
output shown here. A single error object is constructed and used throughout.
First there is a silent invocation of C<cuss()>; then you see a warning with
C<crank()>; then a fatal error is thrown with C<crash()>, trapped, printed,
and finally dumped using L<Devel::Comments|Devel::Comments>.
Each invocation generates a stack backtrace from the point of throw.
Note that when printed, the error object stringifies to the intended
error message and backtrace. The dump shows the true contents of the object.
Note also that the private key C<_private> is retained in the object;
while the message text and backtrace is re-created at each invocation.
Demo: cranking in eluder
in Spathi::eluder at line 38 [demo/error-base-demo.pl]
in Pkunk::fury at line 31 [demo/error-base-demo.pl]
_________________ at line 19 [demo/error-base-demo.pl]
Demo: crashing in scout
in (eval) at line 50 [demo/error-base-demo.pl]
in Shofixti::scout at line 50 [demo/error-base-demo.pl]
in Spathi::eluder at line 42 [demo/error-base-demo.pl]
in Pkunk::fury at line 31 [demo/error-base-demo.pl]
__________________ at line 19 [demo/error-base-demo.pl]
### $trap: bless( {
### '-all' => 'Demo: crashing in scout',
### '-base' => 'Demo:',
### '-frames' => [
### {
### '-eval' => undef,
### '-file' => 'demo/error-base-demo.pl',
### '-line' => '50',
### '-package' => 'Shofixti',
### '-sub' => '(eval) '
### },
### {
### '-eval' => undef,
### '-file' => 'demo/error-base-demo.pl',
### '-line' => '50',
### '-package' => 'Shofixti',
### '-sub' => 'Shofixti::scout'
### },
### {
### '-eval' => undef,
### '-file' => 'demo/error-base-demo.pl',
### '-line' => '42',
### '-package' => 'Spathi',
### '-sub' => 'Spathi::eluder '
### },
### {
### '-eval' => undef,
### '-file' => 'demo/error-base-demo.pl',
### '-line' => '31',
### '-package' => 'Pkunk',
### '-sub' => 'Pkunk::fury '
### },
### {
### '-bottom' => 1,
### '-eval' => undef,
### '-file' => 'demo/error-base-demo.pl',
### '-line' => '19',
### '-package' => 'main',
### '-sub' => '_______________'
### }
### ],
### '-lines' => [
### 'Demo: crashing in scout',
### 'in (eval) at line 50 [demo/error-base-demo.pl]',
### 'in Shofixti::scout at line 50 [demo/error-base-demo.pl]',
### 'in Spathi::eluder at line 42 [demo/error-base-demo.pl]',
### 'in Pkunk::fury at line 31 [demo/error-base-demo.pl]',
### '__________________ at line 19 [demo/error-base-demo.pl]'
### ],
### '-mesg' => '',
### '-top' => 2,
### '-type' => 'crashing in scout',
### _private => 'foo'
### }, 'Error::Base' )
=head1 PHILOSOPHY
Many error-related modules are available on CPAN. Some do bizarre things.
L<Error> is self-deprecated in its own POD as "black magic";
which recommends L<Exception::Class> instead.
L<Exception> installs a C<< $SIG{__DIE__} >> handler that converts text
passed to C<die> into an exception object. It permits environment variables
and setting global state; and implements a C<try> syntax. This module may be
closest in spirit to Error::Base.
For some reason, I can't persuade C<cpan> to find it.
L<Carp> is well-known and indeed, does a full backtrace with C<confess()>.
The better-known C<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 L<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 L<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 I<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'.
=head1 AUTHOR
Xiong Changnian C<< <xiong@cpan.org> >>
=head1 LICENCE
Copyright (C) 2011, 2013 Xiong Changnian C<< <xiong@cpan.org> >>
This library and its contents are released under Artistic License 2.0:
L<http://www.opensource.org/licenses/artistic-license-2.0.php>
=head1 SEE ALSO
L<Error::Base>(3)
=cut
## END MODULE
1;
__END__