File Coverage

blib/lib/CAS/Messaging.pm
Criterion Covered Total %
statement 18 89 20.2
branch 0 56 0.0
condition 0 29 0.0
subroutine 6 15 40.0
pod 4 7 57.1
total 28 196 14.2


line stmt bran cond sub pod time code
1             package CAS::Messaging;
2              
3             =head1 NAME
4              
5             CAS::Messaging - Base class for class message & error handling. Not intended
6             for external use.
7              
8             =head1 SYNOPSIS
9              
10             use CAS::Constants;
11              
12             =head1 DESCRIPTION
13              
14             Exports the following constants into callers namespace:
15             CONTINUE => 100
16             OK => 200
17             CREATED => 201
18             ACCEPTED => 202
19             NOT_MODIFIED => 304
20             BAD_REQUEST => 400
21             UNAUTHORIZED => 401
22             AUTH_REQUIRED => 401
23             FORBIDDEN => 403
24             NOT_FOUND => 404
25             METHOD_NOT_ALLOWED => 405
26             NOT_ACCEPTABLE => 406
27             REQUEST_TIME_OUT => 408
28             TIME_EXPIRED => 408
29             CONFLICT => 409
30             GONE => 410
31             ERROR => 500
32             INTERNAL_SERVER_ERROR => 500
33             NOT_IMPLEMENTED => 501
34              
35             Definitions of response codes:
36              
37             =over 4
38              
39             =item B
40              
41             The client may continue with its request. Generally only used inside
42             methods where multiple steps may be required.
43              
44             =item B
45              
46             The request has succeeded. Accept for certain special circumstances where
47             another code is defined as expected, this is the code that should be set
48             when any method completes its task sucessfully (as far as we know).
49              
50             =item B
51              
52             The is the code set when a new object was succesfully created.
53              
54             =item B
55              
56             Indicates the request has been accepted for processing, but the processing has
57             not been completed.
58              
59             =item B
60              
61             A request was made to save or change something that resulted in no actual
62             change, but no system error occured. Such as when setting an attribute to a
63             value that is not allowed.
64              
65             =item B
66              
67             The request could not be understood by the server due to malformed syntax or
68             missing required arguments.
69              
70             =item B
71              
72             The request requires user authentication.
73              
74             =item B
75              
76             As L.
77              
78             =item B
79              
80             The server understood the request, but is refusing to fulfill it because the
81             user or requesting client lacks the required authorization.
82              
83             =item B
84              
85             The server understood the request, but the requested resource (such as a user
86             or client) was not found.
87              
88             =item B
89              
90             The requested method is not allowed in the current context or by the
91             calling object.
92              
93             =item B
94              
95             The client did not produce a request within the time that the server was
96             prepared to wait. Or, in the more common context of the user, their log-in
97             period has timed out and they need to re-authenticate.
98              
99             =item B
100              
101             As L.
102              
103             =item B
104              
105             The request could not be completed due to a conflict with the current state of
106             the resource.
107              
108             =item B
109              
110             The server encountered some condition which prevented it from
111             fulfilling the request. Serious internal problems, such as malformed SQL
112             statements will also die. This condition is more commonly set when a request
113             appeared valid but was impossible to complete, such as a well formed new
114             user request, but where the username was already taken. All methods initially
115             set the response code to ERROR and then change it when appropriate.
116              
117             =item B
118              
119             As L.
120              
121             =item B
122              
123             The server does not support the functionality required to fulfill the request.
124              
125             =back
126              
127             These values are drawn from Apache's response codes, since this system is
128             intended to be generally accessed via an Apache server. While error text
129             will be stored in B, the RESPONSE_CODE can be checked to see the
130             reason for failure.
131              
132             =cut
133              
134 6     6   24554 use strict;
  6         13  
  6         278  
135 6     6   37 use Scalar::Util qw(blessed);
  6         22  
  6         424  
136 6     6   35 use Carp qw(cluck confess croak carp);
  6         27  
  6         430  
137 6     6   37 use base qw(Exporter);
  6         11  
  6         1370  
138              
139             our $VERSION = '0.08';
140             our $AUTOLOAD = '';
141              
142             our %codes = (
143             CONTINUE => 100,
144             OK => 200,
145             CREATED => 201,
146             ACCEPTED => 202,
147             NOT_MODIFIED => 304,
148             BAD_REQUEST => 400,
149             UNAUTHORIZED => 401,
150             AUTH_REQUIRED => 401,
151             FORBIDDEN => 403,
152             NOT_FOUND => 404,
153             METHOD_NOT_ALLOWED => 405,
154             NOT_ACCEPTABLE => 406,
155             REQUEST_TIME_OUT => 408,
156             TIME_EXPIRED => 408,
157             CONFLICT => 409,
158             GONE => 410,
159             ERROR => 500,
160             INTERNAL_SERVER_ERROR => 500,
161             NOT_IMPLEMENTED => 501,
162             );
163 6     6   42 use constant \%codes;
  6         11  
  6         497  
164             use constant {
165 6         9397 CONTINUE => 100,
166             OK => 200,
167             CREATED => 201,
168             ACCEPTED => 202,
169             NOT_MODIFIED => 304,
170             BAD_REQUEST => 400,
171             UNAUTHORIZED => 401,
172             AUTH_REQUIRED => 401,
173             FORBIDDEN => 403,
174             NOT_FOUND => 404,
175             METHOD_NOT_ALLOWED => 405,
176             NOT_ACCEPTABLE => 406,
177             REQUEST_TIME_OUT => 408,
178             TIME_EXPIRED => 408,
179             CONFLICT => 409,
180             GONE => 410,
181             ERROR => 500,
182             INTERNAL_SERVER_ERROR => 500,
183             NOT_IMPLEMENTED => 501,
184 6     6   35 };
  6         12  
185              
186             our $Errmsg = '';
187             our @EXPORT = (keys %codes,qw($Errmsg));
188              
189             # we need to be able to get the string by value sometimes
190             # it doesn't matter here if an alias gets lost
191             our %code_name_by_val = reverse %codes;
192              
193             # set the result information in self
194             sub _set_result {
195 0     0     my $self = shift;
196 0 0         $self->error("Not a method call") unless blessed($self);
197 0   0       my $debug = $self->{debug} || 0;
198            
199 0   0       my $code = shift || ERROR; # no code == bad ;)
200 0 0         $self->error("Unknown result code $code") unless $code_name_by_val{$code};
201 0           $self->{response_code} = $code;
202            
203 0           my @call = caller;
204 0           my $msg = shift;
205 0 0         unless ($msg) {
206 0           $msg = 'No message provided by ' . $call[0];
207             } # no message, blame caller
208            
209 0 0         if ($debug) {
210 0           $msg = "($call[0]:" . "[$call[2]]) $msg";
211             } # if debugging make sure we know where from
212            
213 0           push(@{$self->{messages}}, $msg);
  0            
214            
215             # If debugging is at 2 or more, we're generating very noisy output as well
216 0 0         $self->gripe("_set_result ($code): $msg") if $self->{debug} >= 2;
217             } # _set_result
218              
219              
220             sub _clear_result {
221 0     0     my $self = shift;
222 0 0         $self->error("Not a method call") unless blessed($self);
223            
224             # we set the code to error as any call to _clear_result should be
225             # internal, and anything happening before a different result is set that
226             # stops processing is almost certainly an error
227 0           $self->{response_code} = ERROR;
228 0           $self->{messages} = [];
229             } # _sclear_result
230              
231              
232             # Checks to see if the provided code matches the current response_code
233             # accept either value or text
234             sub response_is {
235 0     0 0   my $self = shift;
236 0 0         $self->error("Not a method call") unless blessed($self);
237 0   0       my $code = shift || $self->error("No response code specified");
238            
239 0 0         if ($codes{$code}) { $code = $codes{$code} }
  0            
240            
241 0 0         $self->error("Unknown code $code") unless exists $code_name_by_val{$code};
242            
243 0 0         return 1 if $self->{response_code} == $code;
244 0           return undef;
245             } # response_is
246              
247             # returns the text version of the code, useful mostly in error reporting
248             sub response_code {
249 0     0 0   my $self = shift;
250 0 0         $self->error("Not a method call") unless blessed($self);
251            
252             # return the key string for the current code
253 0           return $code_name_by_val{$self->{response_code}};
254             } # response_code
255              
256             # get the numerical value from the code name
257             sub code_value {
258 0     0 0   my $self = shift;
259 0 0         $self->error("Not a method call") unless blessed($self);
260            
261 0           my $name = shift;
262 0 0         $self->gripe("Unknown code $name") unless exists $codes{$name};
263 0 0         return $codes{$name} if exists $codes{$name};
264 0           return undef;
265             } # response_code
266              
267              
268             =head2 messages
269              
270             Messages return any processing messages. While sometimes useful information
271             can be found here for debugging, generally the only reason to call this method
272             is to see what happened that caused an error or other invalid response.
273              
274             unless ($user->validate_Password($HR_params)) {
275             die "Password not validated: $user->messages";
276             } # unless valid password provided
277              
278             Note that in scalar context messages will return a scalar of all messages
279             generated seperated with '; '. In list context it returns a list of the
280             messages allowing the caller to format for other display, such as HTML. As
281             such, the results of the die above would be very different if written as:
282             die "Password not validated: ", $user->messages;
283              
284             When the last method call worked as expected, then the last message in the list
285             should be the message generated when the result_code was set.
286              
287             =cut
288             sub messages {
289 0     0 1   my $self = shift;
290 0           my $class = blessed($self);
291 0 0         $self->error("Not a method call") unless $class;
292            
293 0           return wantarray ? @{$self->{messages}}
  0            
294 0 0         : join('; ', $class, @{$self->{messages}});
295             } # messages
296              
297              
298             =head2 errstr
299              
300             Presumes that there was an error, and that the last message generated most
301             directly relates to the cause of the error and returns only that message. Be
302             warned however that this might always be correct, or enough information.
303             Generally the whole message list is prefered.
304              
305             =cut
306             sub errstr {
307 0     0 1   my $self = shift;
308 0 0         $self->error("Not a method call") unless blessed($self);
309            
310 0           return $self->{messages}[-1];
311             } # errstr
312              
313              
314             =head2 error
315              
316             Throw a fatal exeption. Returns a stack trace (confess) if called when
317             DEBUG is true. L actually does all the work, error just tells
318             gripe to die.
319              
320             =cut
321             sub error {
322 0     0 1   my $self = shift;
323 0 0         confess("Not a method call") unless blessed($self);
324            
325 0           $self->gripe(@_,1); # @_ should only contain the message
326             } # error
327              
328             =head2 gripe
329              
330             Generate debug sensitive warnings and exceptions. gripe also writes warnings
331             to a scratch pad in the calling object so that warning_notes method can
332             return all warnings generated. This behavior mirrors that of
333             L for objects rather than CGI's.
334              
335             Suggested debug level usage (as level goes up messages from earlier levels
336             should continue to be sent):
337              
338             0: Production. Perls warnings should _not_ be turned on and no debug
339             messages should be generated.
340              
341             1: Basic development level. Perls warnings are turned on. Basic debug
342             messages should be generated. L dies with stack trace (confess) and
343             outputs all stored messages.
344              
345             2: Shotgun debugging. Code should now be generating debug messages when
346             entering and/or exiting important blocks so that program flow can be
347             observed.
348              
349             3: Turns on Perls diagnostics. At this level messages should be generated for
350             every pass through loops. This would also be the appropriate level to dump
351             data structures at critical points. Gripe now includes stack trace with every
352             invocation. It is realistic to expect hundreds of lines of output at _least_ at
353             this level. This would be the most verbose debug level.
354              
355             4: Autodie - gripe will now throw a fatal exception with confess.*
356              
357             * Currently this happens the first time called. However it realy should only
358             die the first time a message intended to be sent only at debug levels >= 1.
359              
360             =cut
361             sub gripe {
362 0     0 1   my $self = shift;
363 0           my $class = blessed($self);
364 0 0         croak("Not a method call") unless $class;
365 0   0       my $msg = shift || confess("Class $class threw warning without message");
366 0   0       my $die = shift || 0;
367            
368 0           my @call = caller;
369 0 0         @call = caller(1) if $die;
370            
371             # determine debug level, & set to die if told to be extremely verbose
372 0   0       my $debug = $self->{debug} || 0;
373 0 0         $die = 1 if $debug > 3;
374            
375             # just to be paranoid, we'll unlock tables on fatal error
376             # tables left locked can block future operations and would require
377             # root to unlock by hand
378 0 0 0       if ($die && ref $self->{dbh} && $self->{dbh}->ping) {
      0        
379 0           $self->{dbh}->do("UNLOCK TABLES");
380             } # if dieing and DBH
381            
382 0 0         if ($debug) {
383 0           $msg = "($call[0]" . "[$call[2]]) $msg";
384             } # if debugging
385            
386             # to make sure we know what class the object that called us belongs to
387 0           $msg = "$class: $msg";
388 0 0 0       if (exists $self->{ERRORLOG} && openhandle($self->{ERRORLOG})) {
389 0 0 0       my $logmsg = ($die && $debug) || $debug >= 2
390             ? Carp::longmess($msg) : Carp::shortmess($msg);
391 0           my $fh = $self->{ERRORLOG};
392 0           print $fh $logmsg;
393             } # if user wants errors loged
394            
395             # if we're dying and debug is on
396 0 0 0       if ($die && $debug) { confess("$msg\n" . $self->messages) }
  0 0          
    0          
397 0           elsif ($die) { croak($msg) } # or die with just the message
398 0           elsif ($debug >= 2) { cluck("$msg\n") } # verbose warn
399 0           else { carp("$msg\n") } # just let em know the basics
400             } # gripe
401              
402              
403             =head1 AUTHOR
404              
405             Sean P. Quinlan, C<< >>
406              
407             =head1 TO DO / development notes
408              
409             Gripe should have a way to output to a filehandle (provided when object
410             created) so that output can be optionally logged. Should _set_result also
411             record each invocation to the log if debugging?
412              
413             =head1 BUGS
414              
415             Please report any bugs or feature requests to
416             C, or through the web interface at
417             L.
418             I will be notified, and then you'll automatically be notified of progress on
419             your bug as I make changes.
420              
421             =head1 HISTORY
422              
423             =over 8
424              
425             =item 0.01
426              
427             Original version; created by module-starter
428              
429              
430             =back
431              
432              
433             =head1 SUPPORT
434              
435             You can find documentation for this module with the perldoc command.
436              
437             perldoc CAS
438              
439              
440             Please join the CAS mailing list and suggest a final release name for
441             the package.
442             http://mail.grendels-den.org/mailman/listinfo/CAS_grendels-den.org
443              
444             You can also look for information at:
445              
446             =over 4
447              
448             =item * AnnoCPAN: Annotated CPAN documentation
449              
450             L
451              
452             =item * CPAN Ratings
453              
454             L
455              
456             =item * RT: CPAN's request tracker
457              
458             L
459              
460             =item * Search CPAN
461              
462             L
463              
464             =back
465              
466             =head1 ACKNOWLEDGEMENTS
467              
468             The Bioinformatics Group at Massachusetts General Hospital during my
469             tenure there for development assistance and advice, particularly the QA team
470             for banging on the project code.
471              
472              
473             =head1 COPYRIGHT & LICENSE
474              
475             Copyright 2006 Sean P. Quinlan, all rights reserved.
476              
477             This program is free software; you can redistribute it and/or modify it
478             under the same terms as Perl itself.
479              
480             =cut
481              
482             1; # End of CAS::Messaging