File Coverage

blib/lib/Error/Base/Cookbook.pm
Criterion Covered Total %
statement 18 26 69.2
branch n/a
condition n/a
subroutine 5 11 45.4
pod 4 4 100.0
total 27 41 65.8


line stmt bran cond sub pod time code
1             package Error::Base::Cookbook;
2              
3 1     1   817 use 5.008008;
  1         13  
  1         40  
4 1     1   6 use strict;
  1         2  
  1         26  
5 1     1   5 use warnings;
  1         2  
  1         22  
6 1     1   4 use version; our $VERSION = qv('v1.0.2');
  1         2  
  1         6  
7              
8             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
9             # #
10             # Do not use this module directly. It only implements the POD snippets. #
11             # #
12             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
13              
14             my @td ;
15 0     0   0 sub _get_test_data { @td };
16              
17             #~ -end => 1, # # # # # # # END TESTING HERE # # # # # # # # #
18             #~ -do => 1,
19              
20             #----------------------------------------------------------------------------#
21              
22             sub _words { # sloppy match these strings
23 4     4   21 my @words = @_;
24 4         6 my $regex = q{};
25            
26 4         8 for (@words) {
27 63         78 $_ = lc $_;
28 63         106 $regex = $regex . $_ . '.*';
29             };
30            
31 4         175 return qr/$regex/is;
32             };
33              
34             #----------------------------------------------------------------------------#
35              
36             =head1 NAME
37              
38             Error::Base::Cookbook - Examples of Error::Base usage
39              
40             =head1 VERSION
41              
42             This document describes Error::Base version v1.0.2
43              
44             =head1 WHAT'S NEW
45              
46             =over
47              
48             =item *
49              
50             Update examples to track API changes.
51              
52             =back
53              
54             =head1 DESCRIPTION
55              
56             Basic use of L is quite simple;
57             and advanced usage is not hard.
58             The author hopes that nobody is forced to consult this Cookbook. But I am
59             myself quite fond of cookbook-style documentation; I get more from seeing it
60             all work together than from cut-and-dried reference manuals. I like those too,
61             though; and comprehensive reference documentation is found in
62             L.
63              
64             If you make use of Error::Base and don't find a similar example here in its
65             Cookbook, please be so kind as to send your use case to me for future
66             inclusion. Thank you very much.
67              
68             =head1 EXAMPLES
69              
70             The examples shown here in POD are also present as executable code.
71              
72             =head2 Sanity Check
73              
74             =cut
75              
76             { #
77             push @td, {
78             -case => 'sanity-zero',
79             -do => 1,
80             -code => sub{
81             #
82             my $obviously_true = 0;
83             Error::Base->crash('Unexpected zero')
84             unless $obviously_true;
85             #
86             },
87             -lby => 'die',
88             -want => qr/Unexpected zero/,
89             };
90             } #
91              
92             =pod
93              
94             my $obviously_true = 0;
95             Error::Base->crash('Unexpected zero')
96             unless $obviously_true;
97              
98             You are certain that this will never happen but you decide to check it anyway.
99             No need to plan ahead; just drop in a sanity check.
100              
101             =cut
102              
103             { #
104             my ($case1, $case2, $case3, $pointer);
105             push @td, {
106             -case => 'sanity-case',
107             -do => 1,
108             -code => sub{
109             #
110             if ( $case1 ) { $pointer++ }
111             elsif ( $case2 ) { $pointer-- }
112             elsif ( $case3 ) { }
113             else { Error::Base->crash('Unimplemented case') };
114             #
115             },
116             -lby => 'die',
117             -want => qr/Unimplemented case/,
118             };
119             } #
120              
121             =pod
122              
123             if ( $case1 ) { $pointer++ }
124             elsif ( $case2 ) { $pointer-- }
125             elsif ( $case3 ) { }
126             else { Error::Base->crash('Unimplemented case') };
127              
128             In constructs like this, it's tempting to think you've covered every possible
129             case. Avoid this fallacy by checking explicitly for each implemented case.
130              
131             Error::Base->crash; # emits 'Undefined error.' with backtrace.
132            
133             Don't forget to pass some error message text. Unless you're in real big foo.
134              
135             =head2 Construct First
136              
137             =cut
138              
139             { #
140             push @td, {
141             -case => 'construct-first-foo',
142             -do => 1,
143             -code => sub{
144             #
145             my $err = Error::Base->new('Foo');
146             $err->crash;
147             #
148             },
149             -lby => 'die',
150             -want => qr/Foo/,
151             };
152             } #
153              
154             { #
155             push @td, {
156             -case => 'construct-first-123',
157             -do => 1,
158             -code => sub{
159             #
160             my $err = Error::Base->new(
161             'Third',
162             -base => 'First',
163             -type => 'Second',
164             );
165             $err->crash;
166             #
167             },
168             -lby => 'die',
169             -want => qr/First Second Third/,
170             };
171             } #
172              
173             =pod
174              
175             my $err = Error::Base->new('Foo');
176             $err->crash;
177            
178             my $err = Error::Base->new(
179             'Third',
180             -base => 'First',
181             -type => 'Second',
182             );
183             $err->crash;
184              
185             If you like to plan your error ahead of time, invoke
186             L with any set of arguments you please.
187             This will help keep your code uncluttered.
188              
189             =head2 Construct and Throw in One Go
190              
191             =cut
192              
193             { #
194             push @td, {
195             -case => 'one-go',
196             -do => 1,
197             -code => sub{
198             #
199             Error::Base->crash(
200             -base => 'First',
201             -type => 'Second',
202             -mesg => 'Third',
203             );
204             #
205             },
206             -lby => 'die',
207             -want => qr/First Second Third/,
208             };
209             } #
210              
211             =pod
212              
213             Error::Base->crash(
214             -base => 'First',
215             -type => 'Second',
216             -mesg => 'Third',
217             );
218              
219             You aren't I to construct first, though. Each of the public methods
220             L, L,
221             and L function as constructors and may be called
222             either as a class or object method. Each method accepts all the same
223             parameters as L.
224              
225             =head2 Avoiding Death
226              
227             =cut
228              
229             { #
230             push @td, {
231             -case => 'avoid-death-crank-gruel',
232             -do => 1,
233             -code => sub{
234             #
235             Error::Base->crank('More gruel!'); # as class method
236             #
237             },
238             -lby => 'warn',
239             -want => qr/More gruel!/,
240             };
241             } #
242              
243             { #
244             push @td, {
245             -case => 'avoid-death-un-err',
246             -do => 1,
247             -code => sub{
248             #
249             my $err = Error::Base->new;
250             $err->crank; # as object method
251             #
252             },
253             -lby => 'warn',
254             -want => qr/Undefined error/,
255             };
256             } #
257              
258             { #
259             push @td, {
260             -case => 'avoid-death-tommy',
261             -do => 1,
262             -code => sub{
263             #
264             my $err = Error::Base->new('See me');
265             $err->cuss('Feel me'); # trace stored in object
266             #
267             },
268             -lby => 'return-scalar',
269             -want => qr/Feel me/,
270             };
271             } #
272              
273             { #
274             push @td, {
275             -case => 'avoid-death-cusswords',
276             -do => 1,
277             -code => sub{
278             #
279             my $err = Error::Base->cuss('x%@#*!'); # also a constructor
280             #
281             },
282             -lby => 'return-object',
283             -want => _words(qw/
284             bless frames eval file line package sub
285             lines
286             at line
287             at line
288             ____ at line
289             error base
290             /),
291             };
292             } #
293              
294             =pod
295              
296             Error::Base->crank('More gruel!'); # as class method
297            
298             my $err = Error::Base->new;
299             $err->crank; # as object method
300            
301             my $err = Error::Base->new('See me');
302             $err->cuss('Feel me'); # trace stored in object
303            
304             my $err = Error::Base->cuss('x%@#*!'); # also a constructor
305              
306             L Bs of your error condition. Perhaps it's
307             not that serious. The current fashion is to make almost all errors fatal but
308             it's your call.
309              
310             L neither Bs nor Bs but it does perform
311             a full backtrace from the point of call. You might find it most useful when
312             debugging your error handling itself; substitute 'crash' or 'crank' later.
313              
314             =head2 Escalation
315              
316             =begin fool_pod_coverage
317              
318             Really think we don't want to document the dummies.
319              
320             =head2 cook_dinner
321              
322             =head2 serve_chili
323              
324             =head2 add_recipie
325              
326             =end fool_pod_coverage
327              
328             =cut
329              
330             # dummy subs
331 0     0 1   sub cook_dinner {};
332 0     0 1   sub serve_chili {};
333 0     0 1   sub add_recipie {};
334             #~ my $err = Error::Base->new( -base => 'Odor detected:', -quiet => 1 );
335             #~ my $err = Error::Base->new( -base => 'Odor detected:' );
336             { #
337             my $err = Error::Base->new( -base => 'Odor detected:' );
338             my ( $fart, $room, $fire ) = ( 0, 0, 0 );
339             push @td, { # no fart
340             -case => 'escalate-odor',
341             -do => 1,
342             -code => sub{
343             #
344             cook_dinner;
345             $err->init( _cooked => 1 );
346            
347             serve_chili('mild');
348             $err->cuss ( -type => $fart ) if $fart;
349             $err->crank( -type => 'Air underflow' ) if $fart > $room;
350             add_recipie( $err );
351            
352             serve_chili('hot');
353             $err->crash( -type => 'Evacuate now' ) if $fire;
354             #
355             $err;
356             },
357             -lby => 'return-object',
358             -want => qr/Odor detected:/,
359             };
360             } #
361              
362             { #
363             my $err = Error::Base->new( -base => 'Odor detected:' );
364             my ( $fart, $room, $fire ) = ( 1, 1, 0 );
365             push @td, { # some fart
366             -case => 'escalate-fart',
367             -do => 1,
368             -code => sub{
369             #
370             cook_dinner;
371             $err->init( _cooked => 1 );
372            
373             serve_chili('mild');
374             $err->cuss ( -type => $fart ) if $fart;
375             $err->crank( -type => 'Air underflow' ) if $fart > $room;
376             add_recipie( $err );
377            
378             serve_chili('hot');
379             $err->crash( -type => 'Evacuate now' ) if $fire;
380             #
381             $err;
382             },
383             -lby => 'return-object',
384             -want => qr/Odor detected: 1/,
385             };
386             } #
387              
388             { #
389             my $err = Error::Base->new( -base => 'Odor detected:' );
390             my ( $fart, $room, $fire ) = ( 5, 1, 0 );
391             push @td, { # too much fart
392             -case => 'escalate-room',
393             -do => 1,
394             -code => sub{
395             #
396             cook_dinner;
397             $err->init( _cooked => 1 );
398            
399             serve_chili('mild');
400             $err->cuss ( -type => $fart ) if $fart;
401             $err->crank( -type => 'Air underflow' ) if $fart > $room;
402             add_recipie( $err );
403            
404             serve_chili('hot');
405             $err->crash( -type => 'Evacuate now' ) if $fire;
406             #
407             $err;
408             },
409             -lby => 'return-object',
410             -want => qr/Odor detected: Air underflow/,
411             -cranky => 1,
412             };
413             } #
414              
415             { #
416             my $err = Error::Base->new( -base => 'Odor detected:' );
417             my ( $fart, $room, $fire ) = ( 0, 0, 1 );
418             push @td, { # FIRE
419             -case => 'escalate-fire',
420             -do => 1,
421             -code => sub{
422             #
423             cook_dinner;
424             $err->init( _cooked => 1 );
425            
426             serve_chili('mild');
427             $err->cuss ( -type => $fart ) if $fart;
428             $err->crank( -type => 'Air underflow' ) if $fart > $room;
429             add_recipie( $err );
430            
431             serve_chili('hot');
432             $err->crash( -type => 'Evacuate now' ) if $fire;
433             #
434             },
435             -lby => 'die',
436             -want => qr/Odor detected: Evacuate now/,
437             -cranky => 1,
438             };
439             } #
440              
441             =pod
442              
443             my $err = Error::Base->new( -base => 'Odor detected:' );
444             cook_dinner;
445             $err->init( _cooked => 1 );
446            
447             serve_chili('mild');
448             $err->cuss ( -type => $fart ) if $fart;
449             $err->crank( -type => 'Air underflow' ) if $fart > $room;
450             add_recipie( $err );
451            
452             serve_chili('hot');
453             $err->crash( -type => 'Evacuate now' ) if $fire;
454              
455             Once constructed, the same object may be thrown repeatedly, with multiple
456             methods. On each invocation, new arguments overwrite old ones but previously
457             declared attributes, public and private, remain in force if not overwritten.
458             Also on each invocation, the stack is traced afresh and the error message text
459             re-composed and re-formatted.
460              
461             =head2 Trapping the Fatal Error Object
462              
463             =cut
464              
465             { #
466             push @td, {
467             -case => 'eval',
468             -do => 1,
469             -code => sub{
470             #
471             eval{ Error::Base->crash('Houston...') }; # trap...
472             my $err = $@ if $@; # ... and examine the object
473             #
474             },
475             -lby => 'return-object',
476             -want => _words(qw/
477             bless frames eval file line package sub
478             lines
479             houston
480             at line
481             at line
482             ____ at line
483             error base
484             /),
485             };
486             } #
487              
488             =pod
489              
490             eval{ Error::Base->crash('Houston...') }; # trap...
491             my $err = $@ if $@; # ... and examine the object
492              
493             L does, internally, construct an object if called
494             as a class method. If you trap the error you can capture the object and look
495             inside it.
496              
497             =head2 Backtrace Control
498              
499             =cut
500              
501             { #
502             my $err = Error::Base->new;
503             push @td, {
504             -case => 'backtrace-quiet',
505             -do => 1,
506             -code => sub{
507             #
508             $err->crash( -quiet => 1, ); # no backtrace
509             #
510             },
511             -lby => 'die',
512             -want => qr/Undefined error\.$/,
513             };
514             } #
515              
516             { #
517             my $err = Error::Base->new;
518             push @td, {
519             -case => 'backtrace-nest(-2)',
520             -do => 1,
521             -code => sub{
522             #
523             $err->crash( -nest => -2, ); # really full backtrace
524             #
525             },
526             -lby => 'die',
527             -want => _words(qw/
528             undefined error
529             error base fuss at line
530             error base crash at line
531             at line
532             at line
533             ____ at line
534             /),
535             };
536             } #
537              
538             { # # this test could be better: but implementation will change
539             my $err = Error::Base->new;
540             push @td, {
541             -case => 'backtrace-nest(+2)',
542             -do => 1,
543             -code => sub{
544             #
545             $err->crash( -nest => +2, ); # skip top five frames
546             #
547             },
548             -lby => 'die',
549             -want => _words(qw/
550             undefined error
551             at line
552             at line
553             ____ at line
554             /),
555             };
556             } #
557              
558             =pod
559              
560             $err->crash( -quiet => 1, ); # no backtrace
561             $err->crash( -nest => -2, ); # really full backtrace
562             $err->crash( -nest => +2, ); # skip two more top frames
563              
564             Set L<-quiet|Error::Base/-quiet> to any TRUE value to silence stack
565             backtrace entirely.
566              
567             By default, you get a full stack backtrace: "full" meaning, from the point of
568             invocation. Some stack frames are added by the process of crash()-ing itself;
569             by default, these are not seen. If you want more or fewer frames you may set
570             L<-nest|Error::Base/-nest> to a different value.
571              
572             Beware that future implementations may change the number of stack frames
573             added internally by Error::Base; and also you may see a different number of
574             frames if you subclass, depending on how you do that.
575              
576             =cut
577              
578             =head2 Wrapper Routine
579              
580             =cut
581              
582             { #
583 0     0     sub _crash { Error::Base->crash( @_, -nest => +1 ) };
584             my $obviously_true;
585             push @td, {
586             -case => 'wrapper',
587             -do => 1,
588             -code => sub{
589             #
590             # ... later...
591             _crash('Unexpected zero')
592             unless $obviously_true;
593             #
594             },
595             -lby => 'die',
596             -want => qr/Unexpected zero/,
597             };
598             } #
599              
600             =pod
601              
602             sub _crash { Error::Base->crash( @_, -nest => +1 ) };
603             # ... later...
604             _crash('Unexpected zero')
605             unless $obviously_true;
606              
607             Write a wrapper routine when trying to wedge sanity checks into dense code.
608             Error::Base is purely object-oriented and exports nothing.
609             Don't forget to use L<-nest|Error::Base/-nest> to drop additional frames
610             if you don't want to see the wrapper in your backtrace.
611              
612             =head2 Dress Left
613              
614             =cut
615              
616             { #
617             push @td, {
618             -case => 'prepend-only',
619             -do => 1,
620             -code => sub{
621             #
622             Error::Base->crash (
623             -mesg => 'Let\'s eat!',
624             -prepend => '@! Black Tie Lunch:',
625             );
626             # emits "@! Black Tie Lunch: Let's eat!
627             # @ in main::fubar at line 42 [test.pl]"
628             #
629             },
630             -lby => 'die',
631             -want => qr/\@! Black Tie Lunch: Let's eat!.\@ in/s,
632             };
633             } #
634              
635             { #
636             push @td, {
637             -case => 'prepend-indent',
638             -do => 1,
639             -code => sub{
640             #
641             Error::Base->crash (
642             -mesg => 'Let\'s eat!',
643             -prepend => '@! Black Tie Lunch:',
644             -indent => '%--'
645             );
646             # emits "@! Black Tie Lunch: Let's eat!
647             # %-- in main::fubar at line 42 [test.pl]"
648             #
649             },
650             -lby => 'die',
651             -want => qr/\@! Black Tie Lunch: Let's eat!.%-- in/s,
652             };
653             } #
654              
655             { #
656             push @td, {
657             -case => 'indent-only',
658             -do => 1,
659             -code => sub{
660             #
661             Error::Base->crash (
662             -mesg => 'Let\'s eat!',
663             -indent => '%--'
664             );
665             # emits "%-- Let's eat!
666             # %-- in main::fubar at line 42 [test.pl]"
667             #
668             },
669             -lby => 'die',
670             -want => qr/%-- Let's eat!.%-- in/s,
671             };
672             } #
673              
674             =pod
675              
676             Error::Base->crash (
677             -mesg => 'Let\'s eat!',
678             -prepend => '@! Black Tie Lunch:',
679             );
680             # emits "@! Black Tie Lunch: Let's eat!
681             # @ in main::fubar at line 42 [test.pl]"
682              
683             Error::Base->crash (
684             -mesg => 'Let\'s eat!',
685             -prepend => '@! Black Tie Lunch:',
686             -indent => '%--'
687             );
688             # emits "@! Black Tie Lunch: Let's eat!
689             # %-- in main::fubar at line 42 [test.pl]"
690              
691             Error::Base->crash (
692             -mesg => 'Let\'s eat!',
693             -indent => '%--'
694             );
695             # emits "%-- Let's eat!
696             # %-- in main::fubar at line 42 [test.pl]"
697              
698             Any string passed to L<-prepend|Error::Base/-prepend> will be prepended to
699             the first line only of the formatted error message.
700             If L<-indent|Error::Base/-indent> is defined then that will be
701             prepended to all following lines. If -indent is undefined then it will
702             be formed (from the first character only of -prepend)
703             and (padded with spaces to the length of -prepend).
704             If only -indent is defined then it will be prepended to all lines.
705             You can override default actions by passing the empty string.
706              
707             =head2 Message Composition
708              
709             =cut
710              
711             { #
712             my $err = Error::Base->new;
713             push @td, {
714             -case => 'null',
715             -do => 1,
716             -code => sub{
717             #
718             $err->crash; # 'Undefined error'
719             #
720             },
721             -lby => 'die',
722             -want => qr/Undefined error/s,
723             };
724             } #
725              
726             { #
727             my $err = Error::Base->new;
728             push @td, {
729             -case => 'pronto-only',
730             -do => 1,
731             -code => sub{
732             #
733             $err->crash( 'Pronto!' ); # 'Pronto!'
734             #
735             },
736             -lby => 'die',
737             -want => qr/Pronto!/s,
738             };
739             } #
740              
741             { #
742             my $err = Error::Base->new;
743             push @td, {
744             -case => 'base-and-type',
745             -do => 1,
746             -code => sub{
747             #
748             $err->crash(
749             -base => 'Bar',
750             -type => 'last call',
751             ); # 'Bar last call'
752             #
753             },
754             -lby => 'die',
755             -want => qr/Bar last call/s,
756             };
757             } #
758              
759             { #
760             my $err = Error::Base->new;
761             push @td, {
762             -case => 'base-type-pronto',
763             -do => 1,
764             -code => sub{
765             #
766             $err->crash(
767             'Pronto!',
768             -base => 'Bar',
769             -type => 'last call',
770             ); # 'Bar last call Pronto!'
771             #
772             },
773             -lby => 'die',
774             -want => qr/Bar last call Pronto!/s,
775             };
776             } #
777              
778             { #
779             my $err = Error::Base->new;
780             push @td, {
781             -case => 'base-type-mesg',
782             -do => 1,
783             -code => sub{
784             #
785             $err->crash(
786             -base => 'Bar',
787             -type => 'last call',
788             -mesg => 'Pronto!',
789             ); # 'Bar last call Pronto!'
790             #
791             },
792             -lby => 'die',
793             -want => qr/Bar last call Pronto!/s,
794             };
795             } #
796              
797             { #
798             my $err = Error::Base->new;
799             push @td, {
800             -case => 'mesg-aryref',
801             -do => 1,
802             -code => sub{
803             #
804             my ( $n1, $n2, $n3 ) = ( 'Huey', 'Dewey', 'Louie' );
805             $err->crash(
806             -mesg => [ 'Meet', $n1, $n2, $n3, 'tonight!' ],
807             ); # 'Meet Huey Dewey Louie tonight!'
808             #
809              
810             },
811             -lby => 'die',
812             -want => qr/Meet Huey Dewey Louie tonight!/s,
813             };
814             } #
815              
816             =pod
817              
818             my $err = Error::Base->new;
819             $err->crash; # 'Undefined error'
820             $err->crash( 'Pronto!' ); # 'Pronto!'
821             $err->crash(
822             -base => 'Bar',
823             -type => 'last call',
824             ); # 'Bar last call'
825             $err->crash(
826             'Pronto!',
827             -base => 'Bar',
828             -type => 'last call',
829             ); # 'Bar last call Pronto!'
830             $err->crash(
831             -base => 'Bar',
832             -type => 'last call',
833             -mesg => 'Pronto!',
834             ); # 'Bar last call Pronto!'
835              
836             my $err = Error::Base->new;
837             my ( $n1, $n2, $n3 ) = ( 'Huey', 'Dewey', 'Louie' );
838             $err->crash(
839             -mesg => [ 'Meet', $n1, $n2, $n3, 'tonight!' ],
840             ); # 'Meet Huey Dewey Louie tonight!'
841              
842             As a convenience, if the number of arguments passed in is odd, then the first
843             arg is shifted off and appnended to the error message. This is done to
844             simplify writing one-off, one-line
845             L.
846              
847             For a little more structure, you may pass values to
848             L<-base|Error::Base/-base>,
849             L<-type|Error::Base/-type>, and
850             L<-mesg|Error::Base/-mesg>.
851             All values supplied will be joined; by default, with a single space.
852              
853             If you pass an array reference to C<-mesg> then you can print out
854             any number of strings, one after the other.
855              
856             =cut
857              
858             { #
859             push @td, {
860             -case => 'pep-boys',
861             -do => 1,
862             -code => sub{
863             #
864             my $err = Error::Base->new(
865             'Manny',
866             -base => 'Pep Boys:',
867             );
868             $err->init('Moe');
869             $err->crash('Jack'); # emits 'Pep Boys: Jack' and backtrace
870             #
871             },
872             -lby => 'die',
873             -want => qr/Pep Boys: Jack/,
874             };
875             } #
876              
877             =pod
878              
879             my $err = Error::Base->new(
880             'Manny',
881             -base => 'Pep Boys:',
882             );
883             $err->init('Moe');
884             $err->crash('Jack'); # emits 'Pep Boys: Jack' and backtrace
885              
886             Remember, new arguments overwrite old values. The L
887             method can be called directly on an existing object to overwrite object
888             attributes without expanding the message or tracing the stack. If you I
889             to expand and trace without throwing, invoke L.
890              
891             =head2 Interpolation in Scope
892              
893             =cut
894              
895             { #
896             push @td, {
897             -case => 'interpolation-in-scope',
898             -do => 1,
899             -code => sub{
900             #
901             my $filename = 'debug246.log';
902             open( my $in_fh, '<', $filename )
903             or Error::Base->crash("Failed to open $filename for reading.");
904             #
905             },
906             -lby => 'die',
907             -want => qr/Failed to open debug246\.log for reading\./,
908             };
909             } #
910              
911             =pod
912              
913             my $filename = 'debug246.log';
914             open( my $in_fh, '<', $filename )
915             or Error::Base->crash("Failed to open $filename for reading.");
916              
917             Nothing special here; as usual, double quotes interpolate a variable that is
918             in scope at the place where the error is thrown.
919              
920             =head2 Late Interpolation
921              
922             =begin fool_pod_coverage
923              
924             Really think we don't want to document the dummies.
925              
926             =head2 bar
927              
928             =end fool_pod_coverage
929              
930             =cut
931              
932             { #
933             push @td, {
934             -case => 'late-interpolation',
935             -do => 1,
936             -code => sub{
937             #
938             my $err = Error::Base->new(
939             'Failed to open $filename for reading.',
940             -base => 'My::Module error:',
941             );
942             bar($err);
943             #
944             },
945             -lby => 'die',
946             -want => qr/Failed to open debug246\.log for reading\./,
947             };
948             sub bar {
949 0     0 1   my $err = shift;
950 0           my $filename = 'debug246.log';
951             # open( my $in_fh, '<', $filename )
952             # or
953 0           $err->crash(
954             '$filename' => \$filename,
955             ); # 'Failed to open debug246.log for reading.'
956             };
957             } #
958              
959             =pod
960              
961             my $err = Error::Base->new(
962             'Failed to open $filename for reading.',
963             -base => 'My::Module error:',
964             );
965             bar($err);
966             sub bar {
967             my $err = shift;
968             my $filename = 'debug246.log';
969             open( my $in_fh, '<', $filename )
970             or $err->crash(
971             '$filename' => \$filename,
972             ); # 'Failed to open debug246.log for reading.'
973              
974             If we want to declare lengthy error text well ahead of time, double-quotey
975             interpolation will serve us poorly. In the example, C<$filename> isn't in
976             scope when we construct C<$err>. Hey, we don't even know what the filename
977             will be.
978              
979             Enclose the string to be late-interpolated in B (to avoid a
980             failed attempt to interpolate immediately) and pass the value when you have
981             it ready, in scope. For clarity, I suggest you pass a reference to the
982             I C<$foo> as the value of the I C<'$foo'>.
983             The key must be quoted to avoid it being parsed as a variable.
984              
985             As with normal, in-scope interpolation, you can late-interpolate scalars,
986             arrays, array slices, hash slices, or various escape sequences. There is the
987             same potential for ambiguity, since the actual interpolation is
988             eventually done by perl.
989              
990             See L.
991              
992             Late interpolation is performed I the entire error message is composed
993             and I any prepending, indentation, line-breaking, or stack tracing.
994              
995             =cut
996              
997             { #
998             push @td, {
999             -case => 'late-interpolate-all',
1000             -do => 1,
1001             -code => sub{
1002             #
1003             my $err = Error::Base->new(
1004             '$sca' => 'one',
1005             '@ary_ref' => [ 'white', 'black' ],
1006             '%hash_ref' => { hash => 'hog', toe => 'jam' },
1007             );
1008             $err->crash( '|$sca|@ary_ref|$ary_ref[1]|@hash_ref{ qw/ hash toe / }|' );
1009             # emits '|one|white black|black|hog jam|'
1010             #
1011             },
1012             -lby => 'die',
1013             -want => qr/|one|white black|black|hog jam|/,
1014             };
1015             } #
1016              
1017             =pod
1018              
1019             my $err = Error::Base->new(
1020             '$sca' => 'one',
1021             '@ary_ref' => [ 'white', 'black' ],
1022             '%hash_ref' => { hash => 'hog', toe => 'jam' },
1023             );
1024             $err->crash( '|$sca|@ary_ref|$ary_ref[1]|@hash_ref{ qw/ hash toe / }|' );
1025             # emits '|one|white black|black|hog jam|'
1026              
1027             You may use scalar or array placeholders, signifying them with the usual
1028             sigils. Although you pass a reference, use the appropriate
1029             C<$>, C<@> or C<%> sigil to lead the corresponding key. As a convenience, you
1030             may pass simple scalars directly. (It's syntactically ugly to pass a
1031             reference to a literal scalar.) Any value that is I a
1032             reference will be late-interpolated directly; anything else will be
1033             deferenced (once).
1034              
1035             This is Perlish interpolation, only delayed. You can interpolate escape
1036             sequences and anything else you would in a double-quoted string. You can pass
1037             a reference to a package variable; but do so against a simple key such as
1038             C<'$aryref'>.
1039              
1040             =cut
1041              
1042             { #
1043             push @td, {
1044             -case => 'late-interpolate-self',
1045             -do => 1,
1046             -code => sub{
1047             #
1048             my $err = Error::Base->new(
1049             '$trigger' => 1,
1050             -base => 'Trouble:',
1051             -type => 'Right here in $self->{_where}!',
1052             );
1053             $err->crash( _where => 'River City' );
1054             # emits 'Trouble: Right here in River City!'
1055             #
1056             },
1057             -lby => 'die',
1058             -want => qr/Trouble: Right here in River City!/,
1059             };
1060             } #
1061              
1062             =pod
1063              
1064             my $err = Error::Base->new(
1065             '$trigger' => 1, # unused key triggers "late"
1066             -base => 'Trouble:',
1067             -type => 'Right here in $self->{_where}!',
1068             );
1069             $err->crash( _where => 'River City' );
1070             # emits 'Trouble: Right here in River City!'
1071              
1072             As a further convenience, you may interpolate a value from the error object
1073             itself. In the previous example, C<< '$self->{_where}' >> is late-interpolated
1074             into C<< -type >> (please note the single quotes). And also,
1075             C<< _where >> is defined as C<< 'River City' >>.
1076             B that Error::Base has no idea what you have called your error object
1077             (perhaps '$err'); use the placeholder C<< '$self' >>
1078             in the string to be expanded.
1079              
1080             Don't forget to store your value against the appropriate key!
1081             This implementation of this feature does not peek into your pad.
1082             You may not receive an 'uninitialized' warning if a value is missing.
1083              
1084             If you don't like this feature, don't use it and it won't bug you.
1085             B you must pass a sigiled key to trigger late interpolation.
1086              
1087             =head2 Local List Separator
1088              
1089             =cut
1090              
1091             { #
1092             push @td, {
1093             -case => 'local-list-separator',
1094             -do => 1,
1095             -code => sub{
1096             #
1097             local $" = '=';
1098             Error::Base->crash(
1099             'Third',
1100             -base => 'First',
1101             -type => 'Second',
1102             ); # emits 'First=Second=Third'
1103             #
1104             },
1105             -lby => 'die',
1106             -want => qr/First=Second=Third/,
1107             };
1108             } #
1109              
1110             =pod
1111              
1112             local $" = '=';
1113             Error::Base->crash(
1114             'Third',
1115             -base => 'First',
1116             -type => 'Second',
1117             ); # emits 'First=Second=Third'
1118              
1119             Rationally, I think, message parts should be joined by a single space.
1120             Note that array elements are normally interpolated, separated by spaces.
1121             Perl uses the value of C<$"> (C<$LIST_SEPARATOR>).
1122              
1123             If, for some reason, you wish to see message parts and interpolated elements
1124             joined by something else, localize C<$">.
1125              
1126             =head1 EXAMPLE CODE
1127              
1128             This module contains executable code matching each snippet you see in POD;
1129             this code is exercised by the Error::Base test suite. You're welcome to look.
1130             Please, don't try to C the ::Cookbook!
1131              
1132             =head1 DEMO
1133              
1134             Included in this distribution is a script, C;
1135             output shown here. A single error object is constructed and used throughout.
1136             First there is a silent invocation of C; then you see a warning with
1137             C; then a fatal error is thrown with C, trapped, printed,
1138             and finally dumped using L.
1139             Each invocation generates a stack backtrace from the point of throw.
1140              
1141             Note that when printed, the error object stringifies to the intended
1142             error message and backtrace. The dump shows the true contents of the object.
1143             Note also that the private key C<_private> is retained in the object;
1144             while the message text and backtrace is re-created at each invocation.
1145              
1146             Demo: cranking in eluder
1147             in Spathi::eluder at line 38 [demo/error-base-demo.pl]
1148             in Pkunk::fury at line 31 [demo/error-base-demo.pl]
1149             _________________ at line 19 [demo/error-base-demo.pl]
1150            
1151             Demo: crashing in scout
1152             in (eval) at line 50 [demo/error-base-demo.pl]
1153             in Shofixti::scout at line 50 [demo/error-base-demo.pl]
1154             in Spathi::eluder at line 42 [demo/error-base-demo.pl]
1155             in Pkunk::fury at line 31 [demo/error-base-demo.pl]
1156             __________________ at line 19 [demo/error-base-demo.pl]
1157            
1158             ### $trap: bless( {
1159             ### '-all' => 'Demo: crashing in scout',
1160             ### '-base' => 'Demo:',
1161             ### '-frames' => [
1162             ### {
1163             ### '-eval' => undef,
1164             ### '-file' => 'demo/error-base-demo.pl',
1165             ### '-line' => '50',
1166             ### '-package' => 'Shofixti',
1167             ### '-sub' => '(eval) '
1168             ### },
1169             ### {
1170             ### '-eval' => undef,
1171             ### '-file' => 'demo/error-base-demo.pl',
1172             ### '-line' => '50',
1173             ### '-package' => 'Shofixti',
1174             ### '-sub' => 'Shofixti::scout'
1175             ### },
1176             ### {
1177             ### '-eval' => undef,
1178             ### '-file' => 'demo/error-base-demo.pl',
1179             ### '-line' => '42',
1180             ### '-package' => 'Spathi',
1181             ### '-sub' => 'Spathi::eluder '
1182             ### },
1183             ### {
1184             ### '-eval' => undef,
1185             ### '-file' => 'demo/error-base-demo.pl',
1186             ### '-line' => '31',
1187             ### '-package' => 'Pkunk',
1188             ### '-sub' => 'Pkunk::fury '
1189             ### },
1190             ### {
1191             ### '-bottom' => 1,
1192             ### '-eval' => undef,
1193             ### '-file' => 'demo/error-base-demo.pl',
1194             ### '-line' => '19',
1195             ### '-package' => 'main',
1196             ### '-sub' => '_______________'
1197             ### }
1198             ### ],
1199             ### '-lines' => [
1200             ### 'Demo: crashing in scout',
1201             ### 'in (eval) at line 50 [demo/error-base-demo.pl]',
1202             ### 'in Shofixti::scout at line 50 [demo/error-base-demo.pl]',
1203             ### 'in Spathi::eluder at line 42 [demo/error-base-demo.pl]',
1204             ### 'in Pkunk::fury at line 31 [demo/error-base-demo.pl]',
1205             ### '__________________ at line 19 [demo/error-base-demo.pl]'
1206             ### ],
1207             ### '-mesg' => '',
1208             ### '-top' => 2,
1209             ### '-type' => 'crashing in scout',
1210             ### _private => 'foo'
1211             ### }, 'Error::Base' )
1212              
1213             =head1 PHILOSOPHY
1214              
1215             Many error-related modules are available on CPAN. Some do bizarre things.
1216              
1217             L is self-deprecated in its own POD as "black magic";
1218             which recommends L instead.
1219              
1220             L installs a C<< $SIG{__DIE__} >> handler that converts text
1221             passed to C into an exception object. It permits environment variables
1222             and setting global state; and implements a C syntax. This module may be
1223             closest in spirit to Error::Base.
1224             For some reason, I can't persuade C to find it.
1225              
1226             L is well-known and indeed, does a full backtrace with C.
1227             The better-known C may be a bit too clever and in any case, the dump
1228             is not formatted to my taste. The module is full of global variable settings.
1229             It's not object-oriented and an error object can't easily be pre-created.
1230              
1231             The pack leader seems to be L. Error::Base differs most
1232             strongly in that it has a shorter learning curve (since it does much less);
1233             confines itself to error message emission (catching errors is another job);
1234             and does a full stack backtrace dump by default. Less code may also be
1235             required for simple tasks.
1236              
1237             To really catch errors, I like L ('block eval on steroids').
1238             It has a few shortcomings but is extremely powerful. I don't see why its use
1239             should be confined to testing.
1240              
1241             The line between emitting a message and catching it is blurred in many
1242             related modules. I did not want a jack-in-the-box object that phoned home if
1243             it was thrown under a full moon. The only clever part of an Error::Base
1244             object is that it stringifies.
1245              
1246             It may be true to say that many error modules seem to I to be caught.
1247             I usually expect my errors to cause all execution to come to a fatal,
1248             non-recoverable crash. Oh, yes; I agree it's sometimes needful to catch such
1249             errors, especially during testing. But if you're regularly throwing and
1250             catching, the term 'exception' may be appropriate but perhaps not 'error'.
1251              
1252             =head1 AUTHOR
1253              
1254             Xiong Changnian C<< >>
1255              
1256             =head1 LICENCE
1257              
1258             Copyright (C) 2011, 2013 Xiong Changnian C<< >>
1259              
1260             This library and its contents are released under Artistic License 2.0:
1261              
1262             L
1263              
1264             =head1 SEE ALSO
1265              
1266             L(3)
1267              
1268             =cut
1269              
1270             ## END MODULE
1271             1;
1272             __END__