File Coverage

blib/lib/Throwable/Factory/Try.pm
Criterion Covered Total %
statement 61 63 96.8
branch 31 42 73.8
condition 23 38 60.5
subroutine 11 12 91.6
pod 1 1 100.0
total 127 156 81.4


line stmt bran cond sub pod time code
1             package Throwable::Factory::Try;
2              
3 3     3   316843 use 5.10.0;
  3         13  
  3         200  
4 3     3   19 use strict;
  3         5  
  3         112  
5 3     3   13 use warnings FATAL => 'all';
  3         18  
  3         135  
6              
7 3     3   23 use base qw(Exporter);
  3         4  
  3         277  
8 3     3   15 use Scalar::Util qw(blessed);
  3         6  
  3         333  
9              
10 3         32 use Sub::Import 'Try::Tiny' => (
11             catch => { -as => '_catch' },
12             try => undef,
13             finally => undef,
14 3     3   2294 );
  3         36670  
15              
16             =head1 NAME
17              
18             Throwable::Factory::Try - exception handling for Throwable::Factory
19              
20             =head1 VERSION
21              
22             Version 0.03
23              
24             =cut
25              
26             our $VERSION = '0.03';
27              
28              
29             =head1 SYNOPSIS
30              
31             This module provides a try/catch/finally mechanism to be used with C, based off C and C.
32             The goal is to provide a simple but powerful exception framework.
33              
34             use Throwable::Factory
35             FooBarException => ['-notimplemented'],
36             FooException => ['-notimplemented'],
37             ;
38             use Throwable::Factory::Try;
39              
40             try {
41             FooBarException->throw('it happened again')
42             }
43             catch [
44             'LWP::UserAgent' => sub { print 'Why are you throwing that at me' },
45             ['LWP::UserAgent', 'HTTP::Tiny'] => sub { print 'Why are you throwing those at me' },
46             'FooBarException' => sub { print shift },
47             qr/^Foo/ => sub { FooException->throw },
48             ['FooBarException','FooException'] => sub { print "One of these two" },
49             '-notimplemented' => sub { print 'One of these' },
50             [':str', qr/^Foo/] => sub { print 'String starting with Foo: ' . shift },
51             ':str' => sub { print 'Just a string: ' . shift },
52             '*' => sub { print 'default case' },
53             ],
54             finally {
55             do_it_anyway()
56             };
57            
58              
59             =head1 FUNCTIONS
60              
61             =head2 catch
62              
63             Replacement for L|Try::Tiny/catch->. It has to be right after the L|Try::Tiny/try-> code block, and can handle the L|Try::Tiny/finally-> statement as an argument.
64             It takes an array reference of C<< CONDITION => CODE BLOCK >>, which will be treated in the same order it was passed to C. C can be one of the following:
65              
66             =over
67              
68             =item *
69              
70             C<< '' >> - matches objects which L|UNIVERSAL/obj-DOES-ROLE-> or L|UNIVERSAL/obj-isa-TYPE-> C<< >>
71              
72             try {
73             My::Exception::Class->throw('my own exception')
74             }
75             catch [
76             'My::Exception::Class' => sub { print 'Here it is' }
77             ];
78              
79             =item *
80              
81             C<< [''] >> - matches objects whose classname is in the array
82              
83             try {
84             My::Exception::Class->throw('my own exception')
85             }
86             catch [
87             ['My::Exception::Class', 'My::Exception::SecondClass'] => sub { print 'Here it is' }
88             ];
89              
90             =item *
91              
92             C<< '' >> - matches Throwable::Factory objects based on their TYPE
93              
94             use Throwable::Factory
95             FooBarException => undef,
96             ;
97            
98             try {
99             FooBarException->throw('I failed')
100             }
101             catch [
102             'FooBarException' => sub { print 'Here it is' }
103             ];
104              
105             =item *
106              
107             C<< [''] >> - same as above, but with multiple choice.
108              
109             use Throwable::Factory
110             FooBarException => undef,
111             ;
112            
113             try {
114             FooBarException->throw('I failed')
115             }
116             catch [
117             ['FooBarException', 'FooException'] => sub { print 'Here it is' }
118             ];
119            
120             =item *
121              
122             C - matches Throwable::Factory objects whose TYPE matches the pattern
123              
124             use Throwable::Factory
125             ConnectionClosedException => undef,
126             ConnectionFailedException => undef,
127             ;
128            
129             try {
130             ConnectionClosedException->throw('Damn')
131             }
132             catch [
133             qr/^Connection/ => sub { print 'Here it is' }
134             ];
135              
136             =item *
137              
138             C<< '' >> - matches Throwable::Factory objects based of their L|Throwable::Factory/Exception-Taxonomy->
139              
140             use Throwable::Factory
141             BadArgumentException => ['-caller'],
142             ;
143            
144             try {
145             BadArgumentException->throw('try again')
146             }
147             catch [
148             '-caller' => sub { print 'Here it is' }
149             ];
150              
151             =item *
152              
153             C<':str'> - matches all strings
154              
155             try {
156             die 'oops'
157             }
158             catch [
159             ':str' => sub { print 'Here it is: ' . shift }
160             ];
161              
162             C<< [':str', ] >> - matches strings with a Regexp
163              
164             try {
165             die 'oops'
166             }
167             catch [
168             [':str', qr/^oops/ ] => sub { print 'Here it is: ' . shift }
169             ];
170              
171             =item *
172              
173             C<'*'> - matches everything. Use this as a 'catch all' case.
174              
175             =back
176              
177             =cut
178              
179             our @EXPORT = qw(try catch finally);
180             our @EXPORT_OK = @EXPORT;
181              
182              
183             sub catch ($@) {
184 12     12 1 5973 my $handlers = shift;
185             my $dispatch = _dispatch(
186             @$handlers,
187 0     0   0 '*' => sub { die shift }
188 12         61 );
189              
190 12         48 &_catch($dispatch, @_);
191             }
192              
193             =head1 INTERNAL METHODS
194              
195             =head2 _class_case
196              
197             Method based on L|Dispatch::Class/class_name->, but with cases specific to Throwable::Factory, i.e. taxonomy cases and exception type.
198              
199             =cut
200              
201             sub _class_case
202             {
203 12     12   29 my @prototable = @_;
204              
205             return sub {
206 12     12   16 my ($x) = @_;
207              
208 12         38 my $blessed = blessed $x;
209 12         20 my $ref = ref $x;
210 12         17 my $scope = 'obj';
211              
212 12         39 my @table = @prototable;
213 12         58 while (my ($key, $value) = splice @table, 0, 2)
214             {
215             # everything undefined
216 13 50       32 unless(defined $key)
217             {
218 0 0       0 return $value
219             unless defined $x
220             }
221            
222             # key is a wildcard
223 13 100       49 return $value
224             if $key eq '*';
225            
226             # prepare array cases
227 11 100 66     54 if(ref $key eq 'ARRAY' && ~~@$key)
228             {
229             # regexp to match against string
230 3 100 66     29 if(~~@$key >= 2 && $key->[0] eq ':str' && ref $key->[1] eq 'Regexp')
    50 66        
231 4         15 {
232 1         3 $scope = 'str';
233 1         2 $key = $key->[1];
234             }
235             # list of class/types
236             elsif(! grep {ref $_} @$key )
237             {
238 2         55 my $re = join('|', map { quotemeta($_) } @$key);
  4         17  
239 2         119 $key = qr/^($re)$/;
240 2         7 $scope = 'obj';
241             }
242             }
243              
244             # key is a regexp and value's ref matches key
245 11 100       32 if(ref $key eq 'Regexp')
246             {
247 6 100 66     88 return $value
      100        
248             if $scope eq 'obj' && $ref && $ref =~ $key;
249              
250 4 50 66     33 return $value
      66        
251             if $scope eq 'str' && !$ref && $x =~ $key;
252             }
253              
254             # value is a string
255 8 100       16 if($key eq ':str')
256             {
257 1 50       6 return $value
258             unless $ref
259             }
260              
261             # value's ref is equal to the key
262 7 100       18 return $value
263             if $key eq $ref;
264              
265             # value DOES key
266             # + taxonomy cases
267 6 50       16 if($blessed)
268             {
269 6   50     43 my $DOES = $x->can('DOES') || 'isa';
270            
271 6 50 33     21 return $value
272             if $key eq '-caller' &&
273             $x->$DOES('Throwable::Taxonomy::Caller');
274              
275 6 50 33     18 return $value
276             if $key eq '-environment' &&
277             $x->$DOES('Throwable::Taxonomy::Environment');
278            
279 6 100 66     33 return $value
280             if $key eq '-notimplemented' &&
281             $x->$DOES('Throwable::Taxonomy::NotImplemented');
282            
283 5 100       35 return $value if
284             $x->$DOES($key);
285             }
286              
287             # value can do TYPE and value's TYPE is key
288 4 100 66     164 if($blessed && $x->can('TYPE'))
289             {
290 3 100       10 if(ref $key eq 'Regexp')
291             {
292 2 50 33     10 return $value
293             if $scope ne 'str' && $x->TYPE =~ $key;
294             }
295              
296 1 50       7 return $value
297             if $x->TYPE eq $key;
298             }
299             }
300             }
301 12         68 }
302              
303             =head2 _dispatch
304              
305             Method based on L|Dispatch::Class/dispatch->.
306              
307             =cut
308              
309             sub _dispatch
310             {
311 12     12   32 my $analyze = _class_case(@_);
312              
313             return sub {
314 12     12   29432 my $e = shift;
315 12         37 my $handler = $analyze->($e);
316 12         114 $handler->($e);
317             }
318 12         49 }
319              
320             =head1 AUTHOR
321              
322             Lucien Coffe, C<< >>
323              
324             =head1 BUGS
325              
326             Please report any bugs or feature requests to C, or through
327             the web interface at L. I will be notified, and then you'll
328             automatically be notified of progress on your bug as I make changes.
329              
330             =head1 SUPPORT
331              
332             You can find documentation for this module with the perldoc command.
333              
334             perldoc Throwable::Factory::Try
335              
336              
337             You can also look for information at:
338              
339             =over 4
340              
341             =item * RT: CPAN's request tracker (report bugs here)
342              
343             L
344              
345             =item * AnnoCPAN: Annotated CPAN documentation
346              
347             L
348              
349             =item * CPAN Ratings
350              
351             L
352              
353             =item * Search CPAN
354              
355             L
356              
357             =back
358              
359              
360             =head1 ACKNOWLEDGEMENTS
361              
362              
363             =head1 LICENSE AND COPYRIGHT
364              
365             Copyright 2014 Lucien Coffe.
366              
367             This program is free software; you can redistribute it and/or modify it
368             under the terms of the the Artistic License (2.0). You may obtain a
369             copy of the full license at:
370              
371             L
372              
373             Any use, modification, and distribution of the Standard or Modified
374             Versions is governed by this Artistic License. By using, modifying or
375             distributing the Package, you accept this license. Do not use, modify,
376             or distribute the Package, if you do not accept this license.
377              
378             If your Modified Version has been derived from a Modified Version made
379             by someone other than you, you are nevertheless required to ensure that
380             your Modified Version complies with the requirements of this license.
381              
382             This license does not grant you the right to use any trademark, service
383             mark, tradename, or logo of the Copyright Holder.
384              
385             This license includes the non-exclusive, worldwide, free-of-charge
386             patent license to make, have made, use, offer to sell, sell, import and
387             otherwise transfer the Package with respect to any patent claims
388             licensable by the Copyright Holder that are necessarily infringed by the
389             Package. If you institute patent litigation (including a cross-claim or
390             counterclaim) against any party alleging that the Package constitutes
391             direct or contributory patent infringement, then this Artistic License
392             to you shall terminate on the date that such litigation is filed.
393              
394             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
395             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
396             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
397             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
398             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
399             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
400             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
401             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
402              
403              
404             =cut
405              
406             0xC0FFE