File Coverage

blib/lib/Ouch.pm
Criterion Covered Total %
statement 91 94 96.8
branch 27 30 90.0
condition 10 14 71.4
subroutine 30 32 93.7
pod 18 18 100.0
total 176 188 93.6


line stmt bran cond sub pod time code
1 3     3   351293 use strict;
  3         34  
  3         91  
2 3     3   17 use warnings;
  3         5  
  3         198  
3             package Ouch;
4             $Ouch::VERSION = '0.0501';
5 3     3   19 use Carp qw(longmess shortmess);
  3         6  
  3         202  
6 3     3   19 use parent 'Exporter';
  3         13  
  3         32  
7 3     3   308 use overload bool => sub {1}, q{""} => 'scalar', fallback => 1;
  3     16   6  
  3         31  
  16         47  
8 3     3   301 use Scalar::Util qw(blessed);
  3         6  
  3         563  
9              
10             our @EXPORT = qw(bleep ouch kiss hug barf);
11             our @EXPORT_OK = qw(try throw catch catch_all caught caught_all);
12             our %EXPORT_TAGS = ( traditional => [qw(try throw catch catch_all)], trytiny => [qw( throw caught caught_all )] );
13              
14             sub import {
15 6     6   1106 my $package = shift;
16              
17             # filter out :trytiny_var from list of imports
18 6         14 my ($alt, @imports);
19             ($_ eq ':trytiny_var' ? ($alt = 1) : (push @imports, $_))
20 6 100       24 for @_;
21              
22             # leave early if the modified interface is not of interest
23 6   50     36 local $Exporter::ExportLevel = 1 + ($Exporter::ExportLevel || 0);
24 6 100       4462 return $package->SUPER::import(@imports) unless $alt;
25              
26             # we will default to $_ instead of $@ in the following, the real
27             # import will still be done by Exporter but based on wrapped subs
28 3     3   22 no warnings 'redefine';
  3         5  
  3         698  
29              
30 1         2 my $barf = \&barf;
31 1 50   3   5 local *barf = sub { push @_, $_ if @_ < 1; goto $barf };
  3         2426  
  3         9  
32              
33 1         2 my $bleep = \&bleep;
34 1 100   4   4 local *bleep = sub { push @_, $_ if @_ < 1; goto $bleep };
  4         345  
  4         11  
35              
36 1         2 my $hug = \&hug;
37 1 100   8   17 local *hug = sub { push @_, $_ if @_ < 1; goto $hug };
  8         379  
  8         22  
38              
39 1         5 my $kiss = \&kiss;
40 1 50   7   3 local *kiss = sub { push @_, $_ if @_ < 2; goto $kiss };
  7         38  
  7         19  
41              
42 3     3   21 use warnings 'redefine';
  3         4  
  3         2738  
43              
44             # leave the stage to Exporter now
45 1         267 $package->SUPER::import(@imports);
46             }
47              
48             sub new {
49 15     15 1 519 my ($class, $code, $message, $data) = @_;
50 15         4708 bless {code => $code, message => $message, data => $data, shortmess => shortmess($message), trace => longmess($message) }, $class;
51             }
52              
53             sub try (&) {
54 8     8 1 17 my $try = shift;
55 8         13 eval { $try->() };
  8         18  
56 8         37 return $@;
57             }
58              
59             sub ouch {
60 14     14 1 9124 my ($code, $message, $data) = @_;
61 14         52 my $self = __PACKAGE__->new($code, $message, $data);
62 14         2834 die $self;
63             }
64              
65             sub throw { # alias
66 4     4 1 18 ouch @_;
67             }
68              
69             sub kiss {
70 19     19 1 56 my ($code, $e) = @_;
71 19 100       63 $e = $@ if @_ < 2;
72 19 100 66     163 if (blessed $e && $e->isa('Ouch') && $e->code eq $code) {
      100        
73 9         48 return 1;
74             }
75 10         45 return 0;
76             }
77              
78             sub catch {
79 4     4 1 12 kiss @_;
80             }
81              
82             sub caught {
83 0     0 1 0 kiss @_;
84             }
85              
86             sub hug {
87 20 100   20 1 403 my $e = @_ ? $_[0] : $@;
88 20 100       74 return $e ? 1 : 0;
89             }
90              
91             sub catch_all {
92 4     4 1 13 hug @_;
93             }
94              
95             sub caught_all {
96 0     0 1 0 hug @_;
97             }
98              
99             sub bleep {
100 14 100   14 1 325 my $e = @_ ? $_[0] : $@;
101 14 100 66     78 if (blessed $e && $e->isa('Ouch')) {
102 8         23 return $e->message;
103             }
104             else {
105 6         16 my $message = "$e"; # force to string anyway
106 6 50       56 if ($message =~ m{^(.*)\s+at\s.*line\s\d+.}xms) {
107 6         112 return $1;
108             }
109             else {
110 0         0 return $message;
111             }
112             }
113             }
114              
115             sub barf {
116 6 100   6 1 2578 my $e = @_ ? $_[0] : $@;
117 6         11 my $code;
118 6 100 66     48 if (blessed $e && $e->isa('Ouch')) {
119 4         14 $code = $e->code;
120             }
121             else {
122 2         6 $code = 1;
123             }
124              
125 6         28 print STDERR bleep($e)."\n";
126 6         48 exit $code;
127             }
128              
129             sub scalar {
130 2     2 1 517 my $self = shift;
131 2         12 return $self->{shortmess};
132             }
133              
134             sub trace {
135 2     2 1 6 my $self = shift;
136 2         9 return $self->{trace};
137             }
138              
139             sub hashref {
140 2     2 1 6 my $self = shift;
141             return {
142             code => $self->{code},
143             message => $self->{message},
144             data => $self->{data},
145 2         13 };
146             }
147              
148             sub code {
149 23     23 1 988 my $self = shift;
150 23         204 return $self->{code};
151             }
152              
153             sub message {
154 10     10 1 54 my $self = shift;
155 10         228 return $self->{message};
156             }
157              
158             sub data {
159 2     2 1 5 my $self = shift;
160 2         9 return $self->{data};
161             }
162              
163             =head1 NAME
164              
165             Ouch - Exceptions that don't hurt.
166              
167             =head1 VERSION
168              
169             version 0.0501
170              
171             =head1 SYNOPSIS
172              
173             use Ouch;
174              
175             eval { ouch(404, 'File not found.'); };
176              
177             if (kiss 404) {
178             check_elsewhere();
179             }
180              
181             say $@; # These two lines do the
182             say $@->scalar; # same thing.
183              
184             =head1 DESCRIPTION
185              
186             Ouch provides a class for exception handling that doesn't require a lot of boilerplate, nor any up front definition. If L
187             is working for you, great! But if you want something that is faster, easier to use, requires less typing, and has no prereqs, but still gives
188             you much of that same functionality, then Ouch is for you.
189              
190             =head2 Why another exception handling module?
191              
192             It really comes down to L isn't enough for me, and L does what I want but makes me type way too much. Also, I tend to work on a lot of protocol-based systems that use error codes (HTTP, FTP, SMTP, JSON-RPC) rather than error classes, so that feels more natural to me. Consider the difference between these:
193              
194             B
195              
196             use Ouch;
197             ouch 404, 'File not found.', 'file';
198              
199             B
200              
201             use Exception::Class (
202             'FileNotFound' => {
203             fields => [ 'code', 'field' ],
204             },
205             );
206             FileNotFound->throw( error => 'File not found.', code => 404, field => 'file' );
207              
208             And if you want to catch the exception you're looking at:
209              
210             B
211              
212             if (kiss 404) {
213             # do something
214             }
215              
216             B
217              
218             my $e;
219             if ($e = Exception::Class->caught('FileNotFound')) {
220             # do something
221             }
222              
223             Those differences may not seem like a lot, but over any substantial program with lots of exceptions it can become a big deal.
224              
225             =head2 Usage
226              
227             Most of the time, all you need to do is:
228              
229             ouch $code, $message, $data;
230             ouch -32700, 'Parse error.', $request; # JSON-RPC 2.0 error
231             ouch 441, 'You need to specify an email address.', 'email'; # form processing error
232             ouch 'missing_param', 'You need to specify an email address.', 'email';
233              
234             You can also go long form if you prefer:
235              
236             die Ouch->new($code, $message, $data);
237              
238             If you want to rethrow an Ouch, you can simply C it.
239              
240             eval { ouch(404, 'File not found.'); } ;
241             die $@;
242              
243             =head2 Functional Interface
244              
245             =head3 ouch
246              
247             Some nice sugar instead of using the object oriented interface.
248              
249             ouch 2121, 'Did not do the big thing.';
250              
251             =over
252              
253             =item code
254              
255             An error code. An integer or string representing error type. Try to stick to codes used in whatever domain you happen to be working in. HTTP Status codes. JSON-RPC error codes, etc.
256              
257             =item message
258              
259             A human readable error message.
260              
261             =item data
262              
263             Optional. Anything you want to attach to the exception to help a developer catching it decide what to do. For example, if you're doing form processing, you might want this to be the name of the field that caused the exception.
264              
265             B Do not include objects or code refs in your data. This should only be stuff that is easily serializable like scalars, array refs, and hash refs.
266              
267             =back
268              
269             =head3 kiss
270              
271             Some nice sugar to trap an Ouch.
272              
273             if (kiss $code) {
274             # make it go
275             }
276              
277             =over
278              
279             =item code
280              
281             The code you're looking for.
282              
283             =item exception
284              
285             Optional. If you like you can pass the exception into C. If not, it will just use whatever is in C<$@>. You might want to do this if you've saved the exception before running another C, for example.
286              
287             =back
288              
289              
290             =head3 hug
291              
292             Some nice sugar to trap any exception.
293              
294             if (hug) {
295             # make it stop
296             }
297              
298             =over
299              
300             =item exception
301              
302             Optional. If you like you can pass the exception into C. If not, it will just use whatever is in C<$@>.
303              
304             =back
305              
306              
307             =head3 bleep
308              
309             A little sugar to make exceptions human friendly. Returns a clean error message from any exception, including an Ouch.
310              
311             File not found.
312              
313             Rather than:
314              
315             File not found. at /Some/File.pm line 63.
316              
317             =over
318              
319             =item exception
320              
321             Optional. If you like you can pass the exception into C. If not, it will just use whatever is in C<$@>.
322              
323             =back
324              
325             =head3 barf
326              
327             Calls C, and then exits with error code
328              
329             =over
330              
331             =item exception
332              
333             Optional. You can pass an exception into C which then gets passed to C otherwise it will use whatever's in C<$@>
334              
335             =back
336              
337              
338             =head2 Object-Oriented Interface
339              
340             =head3 new
341              
342             Constructor for the object-oriented interface. Takes the same parameters as C.
343              
344             Ouch->new($code, $message, $data);
345              
346             =head3 scalar
347              
348             Returns the scalar form of the error message:
349              
350             Crap! at /Some/File.pm line 43.
351              
352             Just as if you had done:
353              
354             die 'Crap!';
355              
356             Rather than:
357              
358             ouch $code, 'Crap!';
359              
360             =head3 trace
361              
362             Call this if you want the full stack trace that lead up to the ouch.
363              
364             =head3 hashref
365              
366             Returns a formatted hash reference of the exception, which can be useful for handing off to a serializer like L.
367              
368             {
369             code => $code,
370             message => $message,
371             data => $data,
372             }
373              
374             =head3 code
375              
376             Returns the C passed into the constructor.
377              
378             =head3 message
379              
380             Returns the C passed into the constructor.
381              
382             =head3 data
383              
384             Returns the C passed into the constructor.
385              
386             =head2 Try::Tiny
387              
388             Many Ouch users like to use Ouch with L.
389              
390             use Try::Tiny;
391             use Ouch;
392              
393             try {
394             ouch 404, 'File not found!';
395             }
396             catch {
397             if (kiss(401, $_)) {
398             # do something
399             }
400             else {
401             die $_; # rethrow
402             }
403             };
404              
405             Some users are sticks in the mud who can't bring themselves to C and
406             C. For them, there is the C<:trytiny> interface. Here's how it works:
407              
408             use Try::Tiny;
409             use Ouch qw(:trytiny);
410              
411             try {
412             throw 404, 'File not found!';
413             }
414             catch {
415             if (caught(401, $_)) {
416             # do something
417             }
418             else {
419             die $_; # rethrow
420             }
421             };
422              
423             Using L has some impedence mismatch in that the exception is
424             propagated through C<$_> instead of C<$@> (the default used by Ouch). This
425             forces to always include C<$_> when calling functions in Ouch, which is
426             suboptimal. It's possible to do this:
427              
428             use Try::Tiny;
429             use Ouch qw(:trytiny_var); # use Try::Tiny's variable $_
430              
431             try {
432             throw 404, 'File not found!';
433             }
434             catch {
435             if (kiss 401) {
436             # do something
437             }
438             else {
439             die $_; # rethrow
440             }
441             };
442              
443             i.e. you can use the regular Ouch syntax.
444              
445             This behaviour is localized to the import, i.e. if Ouch is then imported
446             in another place it is possible to decide again which is the default
447             exception variable in that specific import:
448              
449             package I::Want::Try::Tiny;
450             use Try::Tiny;
451             use Ouch qw(:trytiny_var);
452             # ... $_ is the default exception for kiss, hug, barf, and bleep
453              
454             package Gimme::Regular::Ouch;
455             use Ouch;
456             # ... $@ is the default exception object here
457              
458             It's also possible to mix the two approaches, i.e. use both C<:trytiny>
459             and C<:trytiny_var>.
460              
461             =head3 throw
462              
463             See C for details.
464              
465             =head3 caught
466              
467             See C for details.
468              
469             =head3 caught_all
470              
471             See C for details.
472              
473             =head1 DEPRECATED
474              
475             This functionality is deprecated and will be removed in a future release. Use Try::Tiny instead.
476              
477             =head2 Traditional Interface
478              
479             Some people just can't bring themselves to use the sugary cuteness of Ouch. For them there is the C<:traditional> interface. Here's how it works:
480              
481             use Ouch qw(:traditional);
482              
483             my $e = try {
484             throw 404, 'File not found.';
485             };
486              
487             if ( catch 404, $e ) {
488             # do the big thing
489             }
490             elsif ( catch_all $e ) {
491             # make it stop
492             }
493             else {
494             # make it go
495             }
496              
497             B C also populates C<$@>, and C and C will also use C<$@> if you don't specify an exception.
498              
499             =head3 try
500              
501             Returns an exception. Is basically just a nice wrapper around C.
502              
503             =over
504              
505             =item block
506              
507             Try accepts a code ref, anonymous subroutine, or a block.
508              
509             B You need a semi-colon at the end of a C block.
510              
511             =back
512              
513             =head3 throw
514              
515             Works exactly like C. See C for details.
516              
517             =head3 catch
518              
519             Works exactly like C. See C for details.
520              
521             =head3 catch_all
522              
523             Works exactly like C. See C for details.
524              
525              
526             =head1 REQUIREMENTS
527              
528             Requires Perl 5.12 or higher.
529              
530              
531              
532             =head1 SUPPORT
533              
534             =over
535              
536             =item Repository
537              
538             L
539              
540             =item Bug Reports
541              
542             L
543              
544             =back
545              
546              
547             =head1 SEE ALSO
548              
549             If you're looking for something lighter, check out L that ships with Perl. Or if you're looking for something heavier check out L.
550              
551             =head1 AUTHOR
552              
553             JT Smith
554              
555             =head1 LEGAL
556              
557             Ouch is Copyright 2011 Plain Black Corporation (L) and is licensed under the same terms as Perl itself.
558              
559             =cut
560              
561             1;