File Coverage

blib/lib/Unix/Conf/Err.pm
Criterion Covered Total %
statement 12 59 20.3
branch 0 22 0.0
condition n/a
subroutine 4 14 28.5
pod 7 7 100.0
total 23 102 22.5


line stmt bran cond sub pod time code
1             # Error handling class. To be used by all modules.
2             #
3             # Copyright Karthik Krishnamurthy
4             #
5             =head1 NAME
6              
7             Unix::Conf::Err - This module is an internal module for error handling
8             purposes.
9              
10             =head1 SYNOPSIS
11              
12             Refer to the documentation of Unix::Conf for creating error objects.
13             Accessing the class constructor for Unix::Conf::Err is not preferred
14             as the location of the class and consequently its namespace might
15             change. The preferred way is
16              
17             use Unix::Conf;
18             sub foo ()
19             {
20             return (Unix::Conf::->_err ('chdir'))
21             unless (chdir ('/etc'));
22             }
23              
24             # or
25              
26             sub foo ()
27             {
28             return (
29             Unix::Conf::->_err (
30             'object_method',
31             'argument not an object of class BLAH'
32             )
33             ) unless (ref ($obj) eq 'BLAH');
34             }
35              
36             In the calling function, save the return value, test it for
37             truth, print error message on STDERR and continue.
38              
39             $ret->warn ("Error executing foo ()")
40             unless (($ret = foo ()));
41              
42             Increase debugging information to print the cause of error
43             and a full stacktrace and die.
44              
45             unless (($ret = foo ())) {
46             $ret->debuglevel (2);
47             $ret->die ("Error executing foo");
48             }
49              
50             Get state information from the error object and use it to
51             print error ourselves instead of using the provided 'warn'
52             and 'die' methods.
53              
54             use CGI;
55             my $q = new CGI;
56             # do stuff
57             unless (($ret = foo ())) {
58             my $stacktrace = $ret->stacktrace ();
59             $stacktrace =~ s/\n/
/g;
60             print $q->header ('text/html'),
61             $q->start_html ( "Error" ),
62             $q->h1 ( "Error" ),
63             $q->p ( "Could not execute foo ()
"),
64             $q->p ( "because
" ),
65             $q->p ( $ret->errmsg () ),
66             $q->p ("at
"),
67             $q->p ( $ret->where () ),
68             $q->p ($stacktrace);
69             $q->end_html;
70             exit;
71             }
72            
73             =head1 DESCRIPTION
74              
75             A Unix::Conf::Err object saves the state of the call stack at the
76             time its creation. The idea behind a Unix::Conf::Err object style
77             error handling is allowing the caller to decide how to handle the
78             error without using eval blocks around all Unix::Conf::* library
79             calls. The error object can be used to throw exceptions too, as the
80             string operator is overloaded to return the error string, depending
81             on the debuglevel.
82              
83             =cut
84              
85             package Unix::Conf::Err;
86              
87 1     1   8 use 5.6.0;
  1         3  
  1         34  
88 1     1   4 use strict;
  1         1  
  1         21  
89 1     1   4 use warnings;
  1         2  
  1         546  
90              
91             =over 4
92              
93             =item new ()
94              
95             Arguments
96             PREFIX,
97             ERRMSG,
98              
99             Unix::Conf::Err class constructor. If ERRMSG is not specified, a
100             stringified version of "$!" is used. Using Unix::Conf::Err->new is
101             deprecated. The preferred way to create a Unix::Conf::Err object is
102             to use the Unix::Conf->_err method. Call Unix::Conf->_err () at the
103             point of error so that it will store error data/stack at the time of
104             error to be used later.
105              
106             =cut
107              
108             sub new
109             {
110 0     0 1   my $class = shift;
111 0           my $errobj = {};
112 0           $errobj->{DEBUGLEVEL} = 0;
113 0           ($errobj->{PREFIX}, $errobj->{ERRMSG}) = @_;
114 0 0         $errobj->{ERRMSG} = "$!" unless ($errobj->{ERRMSG});
115 0           my $ctr = 0;
116             # store the stack context at time of constructor
117 0           while (($errobj->{STACK}[$ctr]{PACKAGE}, $errobj->{STACK}[$ctr]{FILE}, $errobj->{STACK}[$ctr]{LINE}, $errobj->{STACK}[$ctr]{SUB}) = caller ($ctr)) {
118 0           $ctr++;
119             }
120 0           return (bless ($errobj, $class));
121             }
122              
123             =item debuglevel ()
124              
125             Arguments
126             DEBUGLEVEL,
127              
128             This method can be invoked through both a class and object. When
129             invoked through Unix::Conf, it sets the class wide debuglevel to
130             the argument. When invoked through an object, it sets only the
131             object private debuglevel to the argument. In case both debuglevels
132             are set, error message is printed at the maximum of the class wide
133             debuglevel and object specific debuglevel. Valid values for
134             DEBUGLEVEL are 0, 1, and 2. At level 0 only only the string passed
135             to warn ()/die () methods are printed. At 1, the output of
136             errmsg () and where () is added. At level 2, the output of
137             stacktrace () is added to the output.
138              
139             =cut
140              
141             my $Debug_Level = 0;
142             sub debuglevel
143             {
144 0     0 1   my ($self, $d) = @_;
145 0 0         if (defined ($d)) {
146             # sanity check
147 0 0         $d = 2 if ($d > 2);
148 0 0         $d = 0 if ($d < 0);
149 0 0         if (ref ($self)) {
150 0           $self->{DEBUGLEVEL} = $d;
151             }
152             else {
153 0           $Debug_Level = $d;
154             }
155 0           return ($d);
156             }
157             # whichever is greater must have been set. so return that one.
158             return (
159 0 0         $Debug_Level > $self->{DEBUGLEVEL} ? $Debug_Level : $self->{DEBUGLEVEL}
160             );
161             }
162              
163             =item where ()
164              
165             Prints information about the stack frame in which the error occured
166             along with the line number and file.
167              
168             =cut
169              
170             sub where
171             {
172 0     0 1   my $self = $_[0];
173 0           return ("in $self->{STACK}[1]{SUB}() at line $self->{STACK}[0]{LINE} in $self->{STACK}[0]{FILE}\n");
174             }
175              
176             =item why ()
177              
178             Prints "PREFIX: ERRMSG".
179              
180             =cut
181              
182             sub why
183             {
184 0     0 1   my $self = $_[0];
185 0           return ("$self->{PREFIX}: $self->{ERRMSG}\n");
186             }
187              
188             =item stacktrace ()
189              
190             Prints the complete stacktrace information at the time of creation
191             of the object.
192              
193             =cut
194              
195             sub stacktrace
196             {
197 0     0 1   my $self = $_[0];
198 0           my $errmsg;
199             # caller invoked in _err returns 2 extra stack frames. don't know why
200             # need to debug later
201 0           my ($ctr, $stacklength) = (1, scalar (@{$self->{STACK}}) - 2);
  0            
202 0           while ($ctr <= $stacklength) {
203 0           $errmsg .= "$self->{STACK}[$ctr]{SUB}() called at line $self->{STACK}[$ctr]{LINE} in $self->{STACK}[$ctr]{FILE}\n";
204 0           $ctr++;
205             }
206 0           return $errmsg;
207             }
208              
209             =item warn ()
210              
211             Arguments
212             ERRMSG,
213              
214             Prints ERRMSG to STDERR.
215              
216             =cut
217              
218             # Arguments: errstr (optional)
219             sub warn (;$)
220             {
221 0     0 1   warn (&__stringify);
222             }
223              
224             =item die ()
225              
226             Arguments
227             ERRMSG,
228              
229             Prints ERRMSG to STDERR and die's.
230              
231             =cut
232              
233             # Arguments: errstr (optional)
234             sub die (;$)
235             {
236 0     0 1   die (&__stringify);
237             }
238              
239             # Overloaded functions
240 1         8 use overload '""' => \&__interpret_as_string,
241             'bool' => \&__interpret_as_bool,
242 1     1   1508 'eq' => \&__interpret_as_string;
  1         1054  
243              
244             sub __interpret_as_string
245             {
246 0     0     my $self = shift;
247 0           return (__stringify ($self));
248             }
249              
250             # If the PREFIX key exists then the constructor has been called.
251             sub __interpret_as_bool
252             {
253 0     0     my $self = shift;
254             #return (exists ($self->{PREFIX}) ? undef : 1);
255 0 0         return (exists ($self->{PREFIX}) ? 0 : 1);
256             }
257              
258             sub __stringify ($;$)
259             {
260 0     0     my ($self, $errstr) = @_;
261              
262             # The whole error message is constructed in $errmsg and returned
263 0           my $errmsg = "";
264              
265             # if argument is present get it in $errmsg. it is usually present when
266             # called from the die/warn methods
267 0 0         if ($errstr) {
268 0           $errmsg .= "$errstr\n";
269             }
270              
271             # when debuglevel is 1 and above include reason and point of error
272 0 0         $self->debuglevel () >= 1 && do {
273             # $errmsg might be empty because no argument was passed to die/warn
274             # meth or because __stringify was called from the string overload
275             # handler.
276 0 0         $errmsg .= "\nbecause\n"
277             if ($errmsg);
278 0           $errmsg .= &why.&where;
279             };
280 0 0         $self->debuglevel () == 2 && do {
281 0           $errmsg .= "\nPrinting stack backtrace\n";
282 0           $errmsg .= &stacktrace;
283             };
284 0           return ($errmsg);
285             }
286              
287             1;
288             __END__