File Coverage

blib/lib/Error/Base.pm
Criterion Covered Total %
statement 81 250 32.4
branch 10 74 13.5
condition 0 2 0.0
subroutine 18 41 43.9
pod 22 22 100.0
total 131 389 33.6


line stmt bran cond sub pod time code
1             package Error::Base;
2             #=========# MODULE USAGE
3             #~ use Error::Base; # Simple structured errors with full backtrace
4             #~
5              
6             #=========# PACKAGE BLOCK
7             { #=====# Entire package inside bare block, not indented....
8              
9 1     1   26210 use 5.008008;
  1         4  
  1         38  
10 1     1   6 use strict;
  1         2  
  1         35  
11 1     1   4 use warnings;
  1         13  
  1         29  
12 1     1   841 use version; our $VERSION = qv('v1.0.2');
  1         2350  
  1         6  
13              
14             # Core modules
15             use overload # Overload Perl operations
16 1         8 '""' => \&_stringify,
17 1     1   2064 ;
  1         1334  
18 1     1   242 use Scalar::Util; # General-utility scalar subroutines
  1         2  
  1         164  
19              
20             # CPAN modules
21              
22             # Alternate uses
23             #~ use Devel::Comments '###', ({ -file => 'debug.log' }); #~
24              
25             ## use
26             #============================================================================#
27              
28             # Pseudo-globals
29              
30             # Compiled regexes
31             our $QRFALSE = qr/\A0?\z/ ;
32             our $QRTRUE = qr/\A(?!$QRFALSE)/ ;
33              
34             our $BASETOP = 2; # number of stack frames generated internally
35             # see also global defaults set in accessors
36              
37             #----------------------------------------------------------------------------#
38              
39             #=========# OPERATOR OVERLOADING
40             #
41             # _stringify(); # short
42             #
43             # Purpose : Overloads stringification.
44             # Parms : ____
45             # Reads : ____
46             # Returns : ____
47             # Writes : ____
48             # Throws : ____
49             # See also : ____
50             #
51             # ____
52             #
53             sub _stringify {
54             # my ($self, $other, $swap) = @_;
55 0     0   0 my ($self, undef, undef) = @_;
56            
57 1     1   5 no warnings 'uninitialized';
  1         5  
  1         2330  
58 0 0       0 if ( defined $self->{-lines} ) {
59 0         0 return join qq{\n}, @{ $self->{-lines} }, q{};
  0         0  
60             }
61             else {
62 0         0 return 'Error::Base internal error: stringifying unthrown object';
63             };
64            
65             }; ## _stringify
66              
67             #=========# INTERNAL ROUTINE
68             #
69             # @lines = $self->_trace( # dump full backtrace
70             # -top => 2, # starting stack frame
71             # );
72             #
73             # Purpose : Full backtrace dump.
74             # Parms : -top : integer : usually set at init-time
75             # Returns : ____
76             # Writes : $self->{-frames} : unformatted backtrace
77             # Throws : 'excessive backtrace'
78             # See also : _fuss(), _paired()
79             #
80             # ____
81             #
82             sub _trace {
83 0     0   0 my $self = shift;
84 0         0 my %args = _paired(@_);
85 0 0       0 my $i = defined $args{-top} ? $args{-top} : 1;
86            
87 0         0 my $bottomed ;
88 0         0 my @maxlen = ( 1, 1, 1 ); # starting length of each field
89 0         0 my @f = ( # order in which keys will be dumped
90             '-sub',
91             '-line',
92             '-file',
93             );
94 0         0 my $pad = q{ }; # padding for better formatting
95 0         0 my $in ; # usually 'in '
96            
97             my @frames ; # unformatted AoH
98 0         0 my @lines ; # formatted array of strings
99            
100             # Get each stack frame.
101 0         0 while ( not $bottomed ) {
102 0         0 my $frame ;
103            
104             # Get info for current frame.
105             (
106 0         0 $frame->{-package},
107             $frame->{-file},
108             $frame->{-line},
109             undef,
110             undef,
111             undef,
112             $frame->{-eval}
113             ) = caller( $i );
114            
115             # caller returns this from the "wrong" viewpoint
116             (
117             undef,
118             undef,
119             undef,
120 0         0 $frame->{-sub},
121             undef,
122             undef,
123             undef,
124             ) = caller( $i + 1 );
125            
126             # Normal exit from while loop.
127 0 0       0 if ( not $frame->{-package} ) {
128 0         0 $bottomed++;
129 0         0 last;
130             };
131            
132             # Clean up bottom frame.
133 0 0       0 if ( not $frame->{-sub} ) {
134 0         0 $frame->{-sub} = q{};
135 0         0 $frame->{-bottom} = 1;
136             };
137            
138             # Get maximum length of each field.
139 0         0 for my $fc ( 0..$#f ) {
140 0 0       0 $maxlen[$fc] = $maxlen[$fc] > length $frame->{$f[$fc]}
141             ? $maxlen[$fc]
142             : length $frame->{$f[$fc]}
143             ;
144             };
145            
146             # Clean up any eval text.
147 0 0       0 if ($frame->{-eval}) {
148             # fake newlines for hard newlines
149 0         0 $frame->{-eval} =~ s/\n/\\n/g;
150             };
151 0         0 push @frames, $frame;
152            
153             # Safety exit from while loop.
154 0         0 $i++;
155 0 0       0 die 'Error::Base internal error: excessive backtrace'
156             if $i > 99;
157             #~ last if $i > 9; # DEBUG ONLY #~
158            
159             }; ## while not bottomed
160            
161             # Stash unformatted stack frames.
162 0         0 $self->{-frames} = \@frames;
163            
164             # Format each stack frame.
165 0         0 for my $frame (@frames) {
166            
167             # Pad each field to maximum length (found in while)
168 0         0 for my $fc ( 0..$#f ) {
169 0         0 my $diff = $maxlen[$fc] - length $frame->{$f[$fc]};
170 0         0 $frame->{$f[$fc]} = $frame->{$f[$fc]} . ($pad x $diff);
171             };
172            
173             # Fix up bottom.
174 0 0       0 if ( $frame->{-bottom} ) {
175 0         0 $frame->{-sub} =~ s/ /_/g; # all underbars
176 0         0 $in = q{___}; # *THREE* underbars
177             }
178             else {
179 0         0 $in = q{in }; # a three-char string
180             };
181            
182             # Format printable line.
183 0         0 my $line = qq*$in$frame->{-sub} at line $frame->{-line}*
184             . qq* [$frame->{-file}]*
185             ;
186            
187             # Append any eval text.
188 0 0       0 if ($frame->{-eval}) {
189             # hard newlines so number of frames doesn't change
190 0         0 $line = $line
191             . qq{\n}
192             . qq* string eval: "$frame->{-eval}"*
193             . qq{\n}
194             ;
195             };
196            
197 0         0 push @lines, $line;
198             }; ## for each frame
199            
200 0         0 return @lines;
201             }; ## _trace
202              
203             #=========# CLASS OR OBJECT METHOD
204             #
205             # Error::Base->crash( $text ); # class method; error text required
206             # $err->crash; # object method
207             # $err->crash( $text ); # object method; error text optional
208             # $err->crash( -base => $base ); # named argument okay
209             # $err->crash( -foo => 'bar' ); # set Error::Base options now
210             # $err->crash( mybit => 'baz' ); # set your private stuff now
211             #
212             # Purpose : Fatal out of your program's errors
213             # Parms : $text : string : final part of error message [odd arg]
214             # : -type : string : middle part of error message
215             # : -base : string : initial part of error message
216             # : -top : integer : starting backtrace frame
217             # : -quiet : boolean : TRUE for no backtrace at all
218             # Returns : never
219             # Throws : $self : die will stringify
220             # See also : _fuss(), crank(), cuss(), init()
221             #
222             # The first arg is tested to see if it's a class or object reference.
223             # Then the next test is to see if an odd number of args remain.
224             # If so, then the next arg is shifted off and considered -base.
225             # All remaining args are considered key/value pairs and passed to new().
226             #
227             sub crash{
228 0     0 1 0 my $self = _fuss(@_);
229            
230 0         0 die $self;
231             }; ## crash
232              
233             #=========# INTERNAL FUNCTION
234             #
235             # This does all the work for crash(), crank(), and cuss().
236             # See crash() for more info.
237             #
238             sub _fuss {
239 0     0   0 my $self = shift;
240 0 0       0 if ( Scalar::Util::blessed $self ) { # called on existing object
241 0         0 $self->init(@_); # initialize or overwrite
242             }
243             else { # called as class method
244 0         0 $self = $self->new(@_);
245             };
246            
247 0         0 my $max = 78; # maximum line length
248 0         0 my $message ; # user-defined error message
249             my @lines ; # to stringify $self
250            
251             # Deal with array values.
252 0         0 $self->{-mesg} = _expand_ref( $self->{-mesg} );
253            
254             # Collect all the texts into one message.
255 0         0 $message = _join_local(
256             $self->{-base},
257             $self->{-type},
258             $self->{-mesg},
259             );
260             #~ ### $self
261            
262             # Late interpolate.
263 0         0 $message = $self->_late( $message );
264            
265             # If still no text in there, finally default.
266 0 0       0 if ( not $message ) {
267 0         0 $message = 'Undefined error.';
268             };
269 0         0 $self->{-all} = $message; # keep for possible inspection
270              
271             # Accumulate.
272 0         0 @lines = ( $message );
273            
274             # Stack backtrace by default.
275 0 0       0 if ( not $self->{-quiet} ) {
276 0         0 my @trace = $self->_trace( -top => $self->{-top} );
277 0         0 push @lines, @trace;
278             };
279            
280             # Optionally prepend some stuff.
281 0 0       0 if ( defined $self->{-prepend} ) { # prepended to first line
282 0         0 @{ $self->{-lines} }
  0         0  
283             = _join_local( $self->{-prepend}, shift @lines );
284             }
285             else {
286 0         0 @{ $self->{-lines} } = shift @lines;
  0         0  
287             };
288 0 0       0 if ( defined $self->{-indent} ) { # prepended to all others
289 0         0 push @{ $self->{-lines} },
  0         0  
290 0         0 map { _join_local( $self->{-indent}, $_ ) } @lines;
291             }
292             else {
293 0         0 push @{ $self->{-lines} }, @lines;
  0         0  
294             };
295            
296             ### @lines
297 0         0 return $self;
298            
299             #~ # Do something to control line length and deal with multi-line $all.
300             #~ my @temp = split /\n/, $all; # in case it's multi-line
301             #~ my $limit = $max - length $prepend;
302             #~ @temp = map { s//\n/ if length > $limit }
303             #~ @temp; # avoid excessive line length
304             #~ my $infix = qq{\n} . $indent;
305             #~ $all = join $infix, @temp;
306            
307             }; ## _fuss
308              
309             #=========# CLASS OR OBJECT METHOD
310             #
311             # Just like crash() except it warn()-s and does not die().
312             # See crash() for more info.
313             sub crank{
314 0     0 1 0 my $self = _fuss(@_);
315            
316 0         0 warn $self;
317             }; ## crank
318              
319             #=========# CLASS OR OBJECT METHOD
320             #
321             # Just like crash() except it just returns $self (after expansion).
322             # See crash() for more info.
323             sub cuss{
324 0     0 1 0 my $self = _fuss(@_);
325            
326 0         0 return $self;
327             }; ## crank
328              
329             #=========# INTERNAL FUNCTION
330             #
331             # $string =_expand_ref( $var ); # expand reference if any
332             #
333             # Purpose : ____
334             # Parms : ____
335             # Reads : ____
336             # Returns : ____
337             # Invokes : ____
338             # Writes : ____
339             # Throws : ____
340             # See also : ____
341             #
342             # ____
343             #
344             sub _expand_ref {
345 0     0   0 my $in = shift;
346 0         0 my $rt = Scalar::Util::reftype $in; # returns no class
347            
348 0 0       0 if ( not $rt ) { # simple scalar...
    0          
    0          
349             # ... don't deref
350 0         0 return $in # unchanged
351             }
352             elsif ( $rt eq 'SCALAR' ) { # scalar ref
353 0         0 return $$in # dereference
354             }
355             elsif ( $rt eq 'ARRAY' ) { # array ref
356 0         0 return _join_local(@$in); # deref and join
357             }
358             #~ elsif ( $rt eq 'HASH' ) { # hash ref
359             #~ my @sorted = map { $_, $in->{$_} } sort keys %$in;
360             #~ return _join_local(@sorted); # deref, sort, and join
361             #~ }
362             else {
363 0         0 die 'Error::Base internal error: bad reftype in _expand_ref';
364             };
365            
366             }; ## _expand_ref
367              
368             #=========# INTERNAL FUNCTION
369             #
370             # $string = _join_local(@_); # short
371             #
372             # Purpose : Like builtin join() but with local list separator.
373             # Parms : @_ : strings to join
374             # Returns : $string : joined strings
375             # Throws : ____
376             # See also : init()
377             #
378             # Buitin join() does not take $" (or anything else) by default.
379             # We splice out empty strings to avoid useless runs of spaces.
380             #
381             sub _join_local {
382 0     0   0 my @parts = @_;
383            
384             # Splice out empty strings.
385 0         0 @parts = grep { $_ ne q** } @parts;
  0         0  
386            
387 0         0 return join $", @parts;
388             }; ## _join_local
389              
390             #=========# INTERNAL FUNCTION
391             #
392             # my %args = _paired(@_); # check for unpaired arguments
393             #
394             # Purpose : ____
395             # Parms : ____
396             # Reads : ____
397             # Returns : ____
398             # Writes : ____
399             # Throws : ____
400             # See also : ____
401             #
402             # ____
403             #
404             sub _paired {
405 0 0   0   0 if ( scalar @_ % 2 ) { # an odd number modulo 2 is one: true
406 0         0 die 'Error::Base internal error: unpaired args';
407             };
408 0         0 return @_;
409             }; ## _paired
410              
411             #=========# CLASS METHOD
412             #
413             # my $obj = $class->new();
414             # my $obj = $class->new({ -a => 'x' });
415             #
416             # Purpose : Object constructor
417             # Parms : $class : Any subclass of this class
418             # anything else will be passed to init()
419             # Returns : $self
420             # Invokes : init()
421             #
422             # Good old-fashioned hashref-based object constructor.
423             #
424             sub new {
425 13     13 1 18 my $class = shift;
426 13         20 my $self = {}; # always hashref
427            
428 13         27 bless ($self => $class);
429 13         29 $self->init(@_); # init remaining args
430            
431 13         36 return $self;
432             }; ## new
433              
434             #=========# OBJECT METHOD
435             #
436             # $err->init( k => 'v', f => $b );
437             # $err->init( $text, k => 'v', f => $b );
438             #
439             # An object can be init()-ed more than once; all new values overwrite the old.
440             # This non-standard init() allows an unnamed initial arg.
441             #
442             # See: crash()
443             #
444             sub init {
445 13     13 1 22 my $self = shift;
446 13 50       32 if ( scalar @_ % 2 ) { # an odd number modulo 2 is one: true
447 0         0 $self->{-mesg} = shift; # and now it's even
448             };
449            
450             # Merge all values. Newer values always overwrite.
451 13         17 %{$self} = ( %{$self}, @_ );
  13         30  
  13         65  
452            
453             # Set some default values, mostly to avoid 'uninitialized' warnings.
454 13         45 $self->put_base( $self->{-base} );
455 13         48 $self->put_type( $self->{-type} );
456 13         41 $self->put_mesg( $self->{-mesg} );
457 13         40 $self->put_quiet( $self->{-quiet} );
458 13         42 $self->put_nest( $self->{-nest} );
459 13         29 $self->_fix_pre_ind();
460            
461 13         16 return $self;
462             }; ## init
463              
464             #----------------------------------------------------------------------------#
465             # ACCSESSORS
466              
467             my $Default = {
468             -base => q{},
469             -type => q{},
470             -mesg => q{},
471             -quiet => 0,
472             -nest => 0,
473             -prepend => undef,
474             -indent => undef,
475             };
476              
477              
478              
479             # put
480             sub put_base {
481 13     13 1 14 my $self = shift;
482 13         29 $self->{-base} = shift;
483 13 100       36 if ( not defined $self->{-base} ) {
484 9         17 $self->{-base} = $Default->{-base};
485             };
486 13         18 return $self;
487             };
488             sub put_type {
489 13     13 1 15 my $self = shift;
490 13         32 $self->{-type} = shift;
491 13 50       31 if ( not defined $self->{-type} ) {
492 13         24 $self->{-type} = $Default->{-type};
493             };
494 13         22 return $self;
495             };
496             sub put_mesg {
497 13     13 1 15 my $self = shift;
498 13         66 $self->{-mesg} = shift;
499 13 50       32 if ( not defined $self->{-mesg} ) {
500 13         40 $self->{-mesg} = $Default->{-mesg};
501             };
502 13         21 return $self;
503             };
504             sub put_quiet {
505 13     13 1 19 my $self = shift;
506 13         30 $self->{-quiet} = shift;
507 13 50       31 if ( not defined $self->{-quiet} ) {
508 13         23 $self->{-quiet} = $Default->{-quiet};
509             };
510 13         17 return $self;
511             };
512             sub put_nest {
513 13     13 1 15 my $self = shift;
514 13         27 $self->{-nest} = shift;
515 13 50       29 if ( not defined $self->{-nest} ) {
516 13         25 $self->{-nest} = $Default->{-nest};
517             };
518             # -top is now deprecated from the API
519 13         62 $self->{-top} = $self->{-nest} + $BASETOP;
520 13         15 return $self;
521             };
522             sub put_prepend {
523 0     0 1 0 my $self = shift;
524 0         0 $self->{-prepend} = shift;
525 0         0 $self->_fix_pre_ind();
526 0         0 return $self;
527             };
528             sub put_indent {
529 0     0 1 0 my $self = shift;
530 0         0 $self->{-indent} = shift;
531 0         0 $self->_fix_pre_ind();
532 0         0 return $self;
533             };
534             # For internal use only
535             sub _fix_pre_ind {
536 13     13   21 my $self = shift;
537 13         14 my $indent ;
538             my $case ;
539            
540 13 50       166 $case = $case . ( defined $self->{-prepend} ? 'P' : '-' );
541 13 50       30 $case = $case . ( defined $self->{-indent} ? 'I' : '-' );
542            
543             # four cases cover all needs
544 13 50       22 if ( $case eq '--' ) {
    0          
    0          
545 13         25 $self->{-prepend} = $Default->{-prepend};
546 13         58 $self->{-indent} = $Default->{-indent};
547             }
548             elsif ( $case eq '-I' ) {
549 0         0 $self->{-prepend} = $self->{-indent};
550             }
551             elsif ( $case eq 'P-' ) {
552 0         0 my $prepend = $self->{-prepend};
553 0         0 $self->{-indent} = ( substr $prepend, 0, 1 )
554             . ( q{ } x ((length $prepend) - 1) )
555             ;
556             }
557             else {
558             # ( $case eq 'PI' ) # do nothing
559             };
560            
561 13         16 return $self;
562             };
563              
564             # get
565             sub get_base {
566 0     0 1   my $self = shift;
567 0           return $self->{-base};
568             };
569             sub get_type {
570 0     0 1   my $self = shift;
571 0           return $self->{-type};
572             };
573             sub get_mesg {
574 0     0 1   my $self = shift;
575 0           return $self->{-mesg};
576             };
577             sub get_quiet {
578 0     0 1   my $self = shift;
579 0           return $self->{-quiet};
580             };
581             sub get_nest {
582 0     0 1   my $self = shift;
583 0           return $self->{-nest};
584             };
585             sub get_prepend {
586 0     0 1   my $self = shift;
587 0           return $self->{-prepend};
588             };
589             sub get_indent {
590 0     0 1   my $self = shift;
591 0           return $self->{-indent};
592             };
593             sub get_all {
594 0     0 1   my $self = shift;
595 0           return $self->{-all};
596             };
597             sub get_lines {
598 0     0 1   my $self = shift;
599 0           return $self->{-lines};
600             };
601             sub get_frames {
602 0     0 1   my $self = shift;
603 0           return $self->{-frames};
604             };
605              
606             ## accessors
607             #----------------------------------------------------------------------------#
608              
609             #=========# INTERNAL OBJECT METHOD
610             #
611             # $out = $self->_late( $in ); # late interpolate
612             #
613             # Wrapper method; see Error::Base::Late::_late().
614 0     0     sub _late { return Error::Base::Late::_late(@_) };
615             ##
616              
617             } #=====# ... Entire package inside bare block, not indented.
618             #=========# END PACKAGE BLOCK
619              
620             package Error::Base::Late; # switch package to avoid pseudo-global lexicals
621             {
622              
623             #=========# INTERNAL FUNCTION IN FOREIGN PACKAGE
624             #
625             # $out = _late( $self, $in ); # late interpolate
626             #
627             # Purpose : ____
628             # Parms : $in : scalar string
629             # Reads : every key in $self starting with a $, @, or % sigil
630             # : $self : available as '$self'
631             # Returns : $out : scalar string
632             # Writes : ____
633             # Throws : ____
634             # See also : ____
635             #
636             # I hope this is the worst possible implementation of late().
637             # Late interpolation is accomplished by multiple immediate interpolations,
638             # inside and outside of a string eval.
639             # Non-core PadWalker is not used to derive interpolation context;
640             # caller is required to pass context inside the $self object.
641             # To avoid collision and unintended interpolation, I make housekeeping
642             # variables internal to this routine, package variables.
643             # These are fully qualified to a "foreign" package; caller cannot
644             # accidentally access them (although I cannot stop you from doing stupid).
645             # Some work is done in a bare "setup" block with lexical variables.
646             # But package variables are used to pass values within the routine,
647             # from block to block, inside to outside, within and without the eval.
648             # Quoting is a major concern. Heredocs are used in three places for
649             # double-quoted interpolation; they may not conflict with each other
650             # or with any string that may exist within any of:
651             # - the string to be interpolated, $in
652             # - values passed in $self against @keys (keys with leading sigils)
653             # Rather than attempt to exclude all of these from a generic q//,
654             # I chose heredocs and three long, arbitrary strings.
655             #
656             sub _late {
657 1     1   7 use strict;
  1         3  
  1         81  
658 1     1   5 use warnings;
  1         1  
  1         28  
659 1     1   4 no warnings 'uninitialized'; # too many to count
  1         2  
  1         598  
660             #~ ##### CASE:
661             #~ ##### @_
662             # No lexical variables loose in the outer block of the subroutine.
663 0     0     $Error::Base::Late::self = shift;
664 0 0         if ( not ref $Error::Base::Late::self ) {
665 0           die 'Error::Base internal error: no $self';
666             };
667 0   0       $Error::Base::Late::in = shift || undef;
668 0 0         return $Error::Base::Late::in
669             unless $Error::Base::Late::in =~ /[\$\@%]/; # no sigil, don't bother
670            
671             # Y0uMaYFiReWHeNReaDYGRiDLeY # quite unlikely to collide
672            
673 0           @Error::Base::Late::code = undef; # to be eval-ed
674 0           $Error::Base::Late::out = undef; # interpolated
675            
676             #--------------------------------------------------------------------#
677             { # setup block
678            
679             # Some preamble.
680 0           push @Error::Base::Late::code,
  0            
681             q**,
682             q*#--------------------------------------------------------#*,
683             q*# START EVAL *,
684             q**,
685             q*my $self = $Error::Base::Late::self;*,
686             q**,
687             ;
688            
689             # Unpack all appropriate k/v pairs into their own lexical variables...
690            
691             # Each key includes leading sigil.
692 0           my @keys = grep { /^[\$\@%]/ } keys %$Error::Base::Late::self;
  0            
693 0 0         return $Error::Base::Late::in # abort if not interpolating today
694             #~ unless ( @keys or $Error::Base::Late::in =~ /\$self/ );
695             unless ( @keys );
696 0           my $key ; # placeholder includes sigil!
697             my $val ; # value to be interpolated
698 0           my $rt ; # builtin 'ref' returns (unwanted) class of blessed ref
699            
700             # my $key = $sigil?$Error::Base::Late::self->{'$key'}?;
701 0           my $ch1 = q*my * ;
702 0           my $ch2 = q* = * ;
703 0           my $ch3 = q*Error::Base::Late::self->{'* ;
704 0           my $ch4 = q*'}* ;
705 0           my $ch5 = q*;* ;
706            
707 0           for my $key (@keys) {
708 0           $val = $Error::Base::Late::self->{$key};
709 0           $rt = Scalar::Util::reftype $val; # returns no class
710            
711 0           my $sigil ; # sigil (if any) to deref
712 0           my $lbc = q*{$*; # left brace if sigil . '$' for $self
713 0           my $rbc = q*}*; # right brace if sigil
714            
715 0 0         if ( not $rt ) { # simple scalar...
    0          
    0          
    0          
716             # ... don't deref
717 0           $lbc = q{$}; # only '$' for $self
718 0           $rbc = q{};
719             }
720             elsif ( $rt eq 'SCALAR' ) { # scalar ref
721 0           $sigil = q{$};
722             }
723             elsif ( $rt eq 'ARRAY' ) { # array ref
724 0           $sigil = q{@};
725             }
726             elsif ( $rt eq 'HASH' ) { # hash ref
727 0           $sigil = q{%};
728             }
729             else {
730 0           die 'Error::Base internal error: bad reftype in _late';
731             };
732            
733             # my $key = $sigil?$Error::Base::Late::self->{'$key'}?;
734 0           push @Error::Base::Late::code,
735             ( join q{},
736             $ch1, $key, $ch2,
737             $sigil, $lbc, $ch3, $key, $ch4, $rbc, $ch5,
738             );
739            
740             }; ## for keys
741             # ... done unpacking.
742            
743             # Do the late interpolation phase.
744 0           push @Error::Base::Late::code,
745             q**,
746             q*<
747             <
748             $Error::Base::Late::in
749             Heredoc02_Y0uMaYFiReWHeNReaDYGRiDLeY
750             q*Heredoc01_Y0uMaYFiReWHeNReaDYGRiDLeY*,
751             q*#--------------------------------------------------------#*,
752             q**,
753             ;
754            
755             # Code is now fully assembled.
756 0           $Error::Base::Late::eval_code =
757             join qq{\n}, @Error::Base::Late::code;
758            
759             } ## setup
760             #--------------------------------------------------------------------#
761             { # eval string
762            
763 0           $Error::Base::Late::out = eval
  0            
764             <
765             $Error::Base::Late::eval_code
766             Heredoc03_Y0uMaYFiReWHeNReaDYGRiDLeY
767            
768 0 0         if ($@) {
769 0           warn "Error::Base internal warning: in _late eval: $@";
770 0           return $Error::Base::Late::in; # best we can do
771             };
772            
773             #~ ##### CASE
774             #~ ##### $Error::Base::Late::self
775             #~ ##### $Error::Base::Late::in
776             #~ ##### @Error::Base::Late::code
777             #~ ##### $Error::Base::Late::eval_code
778             #~ ##### $@
779            
780             } ## eval string
781             #--------------------------------------------------------------------#
782            
783             # Heredocs add spurious newlines.
784 0           chomp $Error::Base::Late::out;
785 0           chomp $Error::Base::Late::out;
786             #~ my $out = $Error::Base::Late::out;
787             #~ ##### $out;
788 0           return $Error::Base::Late::out;
789             }; ## _late
790              
791             } ## package Error::Base::Late
792              
793             ## END MODULE
794             1;
795             #============================================================================#
796             __END__