File Coverage

blib/lib/Venus/Try.pm
Criterion Covered Total %
statement 110 111 99.1
branch 33 36 91.6
condition 7 11 63.6
subroutine 27 27 100.0
pod 15 17 88.2
total 192 202 95.0


line stmt bran cond sub pod time code
1             package Venus::Try;
2              
3 86     86   2356 use 5.018;
  86         331  
4              
5 86     86   517 use strict;
  86         183  
  86         2207  
6 86     86   453 use warnings;
  86         181  
  86         3264  
7              
8 86     86   501 use Venus::Class 'attr', 'base';
  86         176  
  86         784  
9              
10             base 'Venus::Kind::Utility';
11              
12 86     86   648 use Scalar::Util ();
  86         269  
  86         117532  
13              
14             # ATTRIBUTES
15              
16             attr 'invocant';
17             attr 'arguments';
18             attr 'on_try';
19             attr 'on_catch';
20             attr 'on_default';
21             attr 'on_finally';
22              
23             # BUILDERS
24              
25             sub build_arg {
26 26     26 0 77 my ($self, $data) = @_;
27              
28             return {
29 26         98 on_try => $data,
30             };
31             }
32              
33             sub build_self {
34 3133     3133 0 7587 my ($self, $data) = @_;
35              
36 3133 50       10836 $self->on_catch([]) if !defined $self->on_catch;
37              
38 3133         7881 return $self;
39             }
40              
41             # METHODS
42              
43             sub any {
44 2     2 1 3 my ($self) = @_;
45              
46 2     2   11 $self->on_default(sub{(@_)});
  2         5  
47              
48 2         18 return $self;
49             }
50              
51             sub call {
52 3103     3103 1 7479 my ($self, $callback) = @_;
53              
54 3103         10216 $self->on_try($self->callback($callback));
55              
56 3103         16436 return $self;
57             }
58              
59             sub callback {
60 3135     3135 1 6826 my ($self, $callback) = @_;
61              
62 3135 100       19561 if (not(UNIVERSAL::isa($callback, 'CODE'))) {
63 3083         5483 my $method;
64 3083         9505 my $invocant = $self->invocant;
65              
66 3083 100       9412 if (defined($invocant)) {
67 3082         11938 $method = $invocant->can($callback);
68             }
69             else {
70 1         17 $method = $self->can($callback);
71             }
72              
73 3083 100       8722 if (!$method) {
74 1         22 $self->throw('error_on_callback', $invocant, $callback)->error;
75             }
76              
77 3082     3054   16792 $callback = sub {goto $method};
  3054         13896  
78             }
79              
80 3134         12920 return $callback;
81             }
82              
83             sub catch {
84 19     19 1 60 my ($self, $package, $callback) = @_;
85              
86 19   100 14   126 $callback ||= sub{(@_)};
  14         37  
87              
88 19         30 push @{$self->on_catch}, [$package, $self->callback($callback)];
  19         64  
89              
90 19         114 return $self;
91             }
92              
93             sub default {
94 6     6 1 54 my ($self, $callback) = @_;
95              
96 6         18 $self->on_default($self->callback($callback));
97              
98 6         84 return $self;
99             }
100              
101             sub error {
102 394     394 1 1911 my ($self, $variable) = @_;
103              
104 394 100       1479 if ($variable) {
105 148     148   537 $self->on_default(sub{($$variable) = @_})
106 381         2466 }
107             else {
108 13         58 $self->catch('Venus::Error');
109             }
110              
111 394         2697 return $self;
112             }
113              
114             sub execute {
115 3102     3102 1 7608 my ($self, $callback, @args) = @_;
116              
117 2991         8451 unshift @args, @{$self->arguments}
118 3102 100 100     9307 if $self->arguments && @{$self->arguments};
  3058         7886  
119              
120 3102 100       9506 unshift @args, $self->invocant
121             if defined($self->invocant);
122              
123 3102 100       12863 return wantarray ? ($callback->(@args)) : $callback->(@args);
124             }
125              
126             sub finally {
127 4     4 1 19 my ($self, $callback) = @_;
128              
129 4         16 $self->on_finally($self->callback($callback));
130              
131 4         63 return $self;
132             }
133              
134             sub maybe {
135 11     11 1 34 my ($self) = @_;
136              
137 11     1   62 $self->on_default(sub{(undef)});
  1         4  
138              
139 11         60 return $self;
140             }
141              
142             sub no_catch {
143 1     1 1 4 my ($self) = @_;
144              
145 1         5 $self->on_catch([]);
146              
147 1         15 return $self;
148             }
149              
150             sub no_default {
151 2     2 1 5 my ($self) = @_;
152              
153 2         7 $self->on_default(undef);
154              
155 2         23 return $self;
156             }
157              
158             sub no_finally {
159 1     1 1 5 my ($self) = @_;
160              
161 1         5 $self->on_finally(undef);
162              
163 1         15 return $self;
164             }
165              
166             sub no_try {
167 1     1 1 5 my ($self) = @_;
168              
169 1         245 $self->on_try(undef);
170              
171 1         195 return $self;
172             }
173              
174             sub result {
175 3098     3098 1 35428 my ($self, @args) = @_;
176              
177 3098         6046 my $dollarat = $@;
178 3098         5115 my @returned;
179              
180             # try
181 3098         4987 my $error = do {
182 3098         5344 local $@;
183 3098         6526 eval {
184 3098         7482 my $tryer = $self->on_try;
185 3098         11632 @returned = ($self->execute($tryer, @args));
186             };
187 3098         9163 $@;
188             };
189              
190             # catch
191 3098 100       8982 if ($error) {
192 172         500 my $caught = $error;
193 172         704 my $catchers = $self->on_catch;
194 172         769 my $default = $self->on_default;
195              
196 172         707 for my $catcher (@$catchers) {
197 16 50       97 if (UNIVERSAL::isa($caught, $catcher->[0])) {
198 16         104 @returned = ($catcher->[1]->($caught));
199 16         37 last;
200             }
201             }
202              
203             # catchall
204 172 100       548 if(!@returned) {
205 156 100       476 if ($default) {
206 153         468 @returned = ($default->($caught))
207             }
208             }
209              
210             # uncaught
211 172 100       539 if(!@returned) {
212 3 100       15 if (Scalar::Util::blessed($caught)) {
213 2         8 die $caught;
214             }
215             else {
216 1 50       8 if (UNIVERSAL::isa($caught, 'Venus::Error')) {
217 0         0 $caught->throw;
218             }
219             else {
220 1         6 require Venus::Error;
221 1         7 Venus::Error->throw($caught);
222             }
223             }
224             }
225             }
226              
227             # finally
228 3095 100       11645 if (my $finally = $self->on_finally) {
229 3         19 $self->execute($finally, @args);
230             }
231              
232 3095         6793 $@ = $dollarat;
233              
234 3095 100       25483 return wantarray ? (@returned) : $returned[0];
235             }
236              
237             # ERRORS
238              
239             sub error_on_callback {
240 2     2 1 7 my ($self, $invocant, $callback) = @_;
241              
242             return {
243 2 100 33     30 name => 'on.callback',
      33        
244             message => sprintf(qq(Can't locate object method "%s" on package "%s"),
245             ($callback, $invocant ? (ref($invocant) || $invocant) : (ref($self) || $self))),
246             };
247             }
248              
249             1;
250              
251              
252              
253             =head1 NAME
254              
255             Venus::Try - Try Class
256              
257             =cut
258              
259             =head1 ABSTRACT
260              
261             Try Class for Perl 5
262              
263             =cut
264              
265             =head1 SYNOPSIS
266              
267             package main;
268              
269             use Venus::Try;
270              
271             my $try = Venus::Try->new;
272              
273             $try->call(sub {
274             my (@args) = @_;
275              
276             # try something
277              
278             return time;
279             });
280              
281             $try->catch('Example::Error', sub {
282             my ($caught) = @_;
283              
284             # caught an error (exception)
285              
286             return;
287             });
288              
289             $try->default(sub {
290             my ($caught) = @_;
291              
292             # catch the uncaught
293              
294             return;
295             });
296              
297             $try->finally(sub {
298             my (@args) = @_;
299              
300             # always run after try/catch
301              
302             return;
303             });
304              
305             my @args;
306              
307             my $result = $try->result(@args);
308              
309             =cut
310              
311             =head1 DESCRIPTION
312              
313             This package provides an object-oriented interface for performing complex
314             try/catch operations.
315              
316             =cut
317              
318             =head1 ATTRIBUTES
319              
320             This package has the following attributes:
321              
322             =cut
323              
324             =head2 invocant
325              
326             invocant(Object)
327              
328             This attribute is read-only, accepts C<(Object)> values, and is optional.
329              
330             =cut
331              
332             =head2 arguments
333              
334             arguments(ArrayRef)
335              
336             This attribute is read-only, accepts C<(ArrayRef)> values, and is optional.
337              
338             =cut
339              
340             =head2 on_try
341              
342             on_try(CodeRef)
343              
344             This attribute is read-write, accepts C<(CodeRef)> values, and is optional.
345              
346             =cut
347              
348             =head2 on_catch
349              
350             on_catch(ArrayRef[CodeRef])
351              
352             This attribute is read-write, accepts C<(ArrayRef[CodeRef])> values, is optional, and defaults to C<[]>.
353              
354             =cut
355              
356             =head2 on_default
357              
358             on_default(CodeRef)
359              
360             This attribute is read-write, accepts C<(CodeRef)> values, and is optional.
361              
362             =cut
363              
364             =head2 on_finally
365              
366             on_finally(CodeRef)
367              
368             This attribute is read-write, accepts C<(CodeRef)> values, and is optional.
369              
370             =cut
371              
372             =head1 INHERITS
373              
374             This package inherits behaviors from:
375              
376             L
377              
378             =cut
379              
380             =head1 METHODS
381              
382             This package provides the following methods:
383              
384             =cut
385              
386             =head2 any
387              
388             any() (Try)
389              
390             The any method registers a default C condition that returns whatever
391             value was encoutered on error and returns it as a result.
392              
393             I>
394              
395             =over 4
396              
397             =item any example 1
398              
399             package main;
400              
401             use Venus::Try;
402              
403             my $try = Venus::Try->new;
404              
405             $try->call(sub {
406             die 'Oops!';
407             });
408              
409             my $any = $try->any;
410              
411             # bless({ on_catch => ... }, "Venus::Try")
412              
413             =back
414              
415             =over 4
416              
417             =item any example 2
418              
419             package main;
420              
421             use Venus::Try;
422              
423             my $try = Venus::Try->new;
424              
425             $try->call(sub {
426             die $try;
427             });
428              
429             my $any = $try->any;
430              
431             # bless({ on_catch => ... }, "Venus::Try")
432              
433             =back
434              
435             =cut
436              
437             =head2 call
438              
439             call(Str | CodeRef $method) (Try)
440              
441             The call method takes a method name or coderef, registers it as the tryable
442             routine, and returns the object. When invoked, the callback will received an
443             C if one was provided to the constructor, the default C if
444             any were provided to the constructor, and whatever arguments were provided by
445             the invocant.
446              
447              
448              
449              
450             I>
451              
452             =over 4
453              
454             =item call example 1
455              
456             package main;
457              
458             use Venus::Try;
459              
460             my $try = Venus::Try->new;
461              
462             my $call = $try->call(sub {
463             my (@args) = @_;
464              
465             return [@args];
466             });
467              
468             # bless({ on_catch => ... }, "Venus::Try")
469              
470             =back
471              
472             =cut
473              
474             =head2 callback
475              
476             callback(Str | CodeRef $method) (CodeRef)
477              
478             The callback method takes a method name or coderef, and returns a coderef for
479             registration. If a coderef is provided this method is mostly a passthrough.
480              
481             I>
482              
483             =over 4
484              
485             =item callback example 1
486              
487             package main;
488              
489             use Venus::Try;
490              
491             my $try = Venus::Try->new;
492              
493             my $callback = $try->callback(sub {
494             my (@args) = @_;
495              
496             return [@args];
497             });
498              
499             # sub { ... }
500              
501             =back
502              
503             =over 4
504              
505             =item callback example 2
506              
507             package Example1;
508              
509             sub new {
510             bless {};
511             }
512              
513             sub test {
514             my (@args) = @_;
515              
516             return [@args];
517             }
518              
519             package main;
520              
521             use Venus::Try;
522              
523             my $try = Venus::Try->new(
524             invocant => Example1->new,
525             );
526              
527             my $callback = $try->callback('test');
528              
529             # sub { ... }
530              
531             =back
532              
533             =over 4
534              
535             =item callback example 3
536              
537             package main;
538              
539             use Venus::Try;
540              
541             my $try = Venus::Try->new;
542              
543             my $callback = $try->callback('missing_method');
544              
545             # Exception! (isa Venus::Try::Error) (see error_on_callback)
546              
547             =back
548              
549             =cut
550              
551             =head2 catch
552              
553             catch(Str $isa, Str | CodeRef $method) (Try)
554              
555             The catch method takes a package or ref name, and when triggered checks whether
556             the captured exception is of the type specified and if so executes the given
557             callback. If no callback is provided the exception is captured in a L
558             operation and returned as a result.
559              
560             I>
561              
562             =over 4
563              
564             =item catch example 1
565              
566             package main;
567              
568             use Venus::Try;
569              
570             my $try = Venus::Try->new;
571              
572             $try->call(sub {
573             my (@args) = @_;
574              
575             die $try;
576             });
577              
578             my $catch = $try->catch('Venus::Try', sub {
579             my (@args) = @_;
580              
581             return [@args];
582             });
583              
584             # bless({ on_catch => ... }, "Venus::Try")
585              
586             =back
587              
588             =over 4
589              
590             =item catch example 2
591              
592             package main;
593              
594             use Venus::Try;
595              
596             my $try = Venus::Try->new;
597              
598             $try->call(sub {
599             my (@args) = @_;
600              
601             $try->throw->error;
602             });
603              
604             my $catch = $try->catch('Venus::Try::Error', sub {
605              
606             return (@_);
607             });
608              
609             # bless({ on_catch => ... }, "Venus::Try")
610              
611             =back
612              
613             =over 4
614              
615             =item catch example 3
616              
617             package main;
618              
619             use Venus::Try;
620              
621             my $try = Venus::Try->new;
622              
623             $try->call(sub {
624              
625             $try->throw->error;
626             });
627              
628             my $catch = $try->catch('Venus::Try::Error');
629              
630             # bless({ on_catch => ... }, "Venus::Try")
631              
632             =back
633              
634             =cut
635              
636             =head2 default
637              
638             default(Str | CodeRef $method) (Try)
639              
640             The default method takes a method name or coderef and is triggered if no
641             C conditions match the exception thrown.
642              
643             I>
644              
645             =over 4
646              
647             =item default example 1
648              
649             package main;
650              
651             use Venus::Try;
652              
653             my $try = Venus::Try->new;
654              
655             $try->call(sub {
656             my (@args) = @_;
657              
658             die $try;
659             });
660              
661             my $default = $try->default(sub {
662             my (@args) = @_;
663              
664             return [@args];
665             });
666              
667             # bless({ on_catch => ... }, "Venus::Try")
668              
669             =back
670              
671             =cut
672              
673             =head2 error
674              
675             error(Ref $variable) (Try)
676              
677             The error method takes a scalar reference and assigns any uncaught exceptions
678             to it during execution. If no variable is provided a L operation will
679             be registered to capture all L exceptions.
680              
681             I>
682              
683             =over 4
684              
685             =item error example 1
686              
687             package main;
688              
689             use Venus::Try;
690              
691             my $try = Venus::Try->new;
692              
693             $try->call(sub {
694             my (@args) = @_;
695              
696             die $try;
697             });
698              
699             my $error = $try->error(\my $object);
700              
701             # bless({ on_catch => ... }, "Venus::Try")
702              
703             =back
704              
705             =over 4
706              
707             =item error example 2
708              
709             package main;
710              
711             use Venus::Try;
712              
713             my $try = Venus::Try->new;
714              
715             $try->call(sub {
716             my (@args) = @_;
717              
718             $try->throw->error;
719             });
720              
721             my $error = $try->error;
722              
723             # bless({ on_catch => ... }, "Venus::Try")
724              
725             =back
726              
727             =cut
728              
729             =head2 execute
730              
731             execute(CodeRef $code, Any @args) (Any)
732              
733             The execute method takes a coderef and executes it with any given arguments.
734             When invoked, the callback will received an C if one was provided to
735             the constructor, the default C if any were provided to the
736             constructor, and whatever arguments were passed directly to this method. This
737             method can return a list of values in list-context.
738              
739             I>
740              
741             =over 4
742              
743             =item execute example 1
744              
745             package Example2;
746              
747             sub new {
748             bless {};
749             }
750              
751             package main;
752              
753             use Venus::Try;
754              
755             my $try = Venus::Try->new(
756             invocant => Example2->new,
757             arguments => [1,2,3],
758             );
759              
760             my $execute = $try->execute(sub {
761             my (@args) = @_;
762              
763             return [@args];
764             });
765              
766             # [bless({}, "Example2"), 1, 2, 3]
767              
768             =back
769              
770             =cut
771              
772             =head2 finally
773              
774             finally(Str | CodeRef $method) (Try)
775              
776             The finally method takes a package or ref name and always executes the callback
777             after a try/catch operation. The return value is ignored. When invoked, the
778             callback will received an C if one was provided to the constructor,
779             the default C if any were provided to the constructor, and whatever
780             arguments were provided by the invocant.
781              
782             I>
783              
784             =over 4
785              
786             =item finally example 1
787              
788             package Example3;
789              
790             sub new {
791             bless {};
792             }
793              
794             package main;
795              
796             use Venus::Try;
797              
798             my $try = Venus::Try->new(
799             invocant => Example3->new,
800             arguments => [1,2,3],
801             );
802              
803             $try->call(sub {
804             my (@args) = @_;
805              
806             return $try;
807             });
808              
809             my $finally = $try->finally(sub {
810             my (@args) = @_;
811              
812             $try->{args} = [@args];
813             });
814              
815             # bless({ on_catch => ... }, "Venus::Try")
816              
817             =back
818              
819             =cut
820              
821             =head2 maybe
822              
823             maybe() (Try)
824              
825             The maybe method registers a default C condition that returns falsy,
826             i.e. an undefined value, if an exception is encountered.
827              
828             I>
829              
830             =over 4
831              
832             =item maybe example 1
833              
834             package main;
835              
836             use Venus::Try;
837              
838             my $try = Venus::Try->new;
839              
840             $try->call(sub {
841             my (@args) = @_;
842              
843             die $try;
844             });
845              
846             my $maybe = $try->maybe;
847              
848             # bless({ on_catch => ... }, "Venus::Try")
849              
850             =back
851              
852             =cut
853              
854             =head2 no_catch
855              
856             no_catch() (Try)
857              
858             The no_catch method removes any configured catch conditions and returns the
859             object.
860              
861             I>
862              
863             =over 4
864              
865             =item no_catch example 1
866              
867             package main;
868              
869             use Venus::Try;
870              
871             my $try = Venus::Try->new;
872              
873             $try->call(sub {
874             my (@args) = @_;
875              
876             die $try;
877             });
878              
879             $try->catch('Venus::Try', sub {
880             my (@args) = @_;
881              
882             return [@args];
883             });
884              
885              
886             my $no_catch = $try->no_catch;
887              
888             # bless({ on_catch => ... }, "Venus::Try")
889              
890             =back
891              
892             =cut
893              
894             =head2 no_default
895              
896             no_default() (Try)
897              
898             The no_default method removes any configured default condition and returns the
899             object.
900              
901             I>
902              
903             =over 4
904              
905             =item no_default example 1
906              
907             package main;
908              
909             use Venus::Try;
910              
911             my $try = Venus::Try->new;
912              
913             $try->call(sub {
914             my (@args) = @_;
915              
916             die $try;
917             });
918              
919             my $default = $try->default(sub {
920             my (@args) = @_;
921              
922             return [@args];
923             });
924              
925             my $no_default = $try->no_default;
926              
927             # bless({ on_catch => ... }, "Venus::Try")
928              
929             =back
930              
931             =cut
932              
933             =head2 no_finally
934              
935             no_finally() (Try)
936              
937             The no_finally method removes any configured finally condition and returns the
938             object.
939              
940             I>
941              
942             =over 4
943              
944             =item no_finally example 1
945              
946             package Example4;
947              
948             sub new {
949             bless {};
950             }
951              
952             package main;
953              
954             use Venus::Try;
955              
956             my $try = Venus::Try->new(
957             invocant => Example4->new,
958             arguments => [1,2,3],
959             );
960              
961             $try->call(sub {
962             my (@args) = @_;
963              
964             return $try;
965             });
966              
967             $try->finally(sub {
968             my (@args) = @_;
969              
970             $try->{args} = [@args];
971             });
972              
973             my $no_finally = $try->no_finally;
974              
975             # bless({ on_catch => ... }, "Venus::Try")
976              
977             =back
978              
979             =cut
980              
981             =head2 no_try
982              
983             no_try() (Try)
984              
985             The no_try method removes any configured C operation and returns the
986             object.
987              
988             I>
989              
990             =over 4
991              
992             =item no_try example 1
993              
994             package main;
995              
996             use Venus::Try;
997              
998             my $try = Venus::Try->new;
999              
1000             $try->call(sub {
1001             my (@args) = @_;
1002              
1003             return [@args];
1004             });
1005              
1006             my $no_try = $try->no_try;
1007              
1008             # bless({ on_catch => ... }, "Venus::Try")
1009              
1010             =back
1011              
1012             =cut
1013              
1014             =head2 result
1015              
1016             result(Any @args) (Any)
1017              
1018             The result method executes the try/catch/default/finally logic and returns
1019             either 1) the return value from the successfully tried operation 2) the return
1020             value from the successfully matched catch condition if an exception was thrown
1021             3) the return value from the default catch condition if an exception was thrown
1022             and no catch condition matched. When invoked, the C and C
1023             callbacks will received an C if one was provided to the constructor,
1024             the default C if any were provided to the constructor, and whatever
1025             arguments were passed directly to this method. This method can return a list of
1026             values in list-context.
1027              
1028             I>
1029              
1030             =over 4
1031              
1032             =item result example 1
1033              
1034             package main;
1035              
1036             use Venus::Try;
1037              
1038             my $try = Venus::Try->new;
1039              
1040             $try->call(sub {
1041             my (@args) = @_;
1042              
1043             return [@args];
1044             });
1045              
1046             my $result = $try->result;
1047              
1048             # []
1049              
1050             =back
1051              
1052             =over 4
1053              
1054             =item result example 2
1055              
1056             package main;
1057              
1058             use Venus::Try;
1059              
1060             my $try = Venus::Try->new;
1061              
1062             $try->call(sub {
1063             my (@args) = @_;
1064              
1065             return [@args];
1066             });
1067              
1068             my $result = $try->result(1..5);
1069              
1070             # [1..5]
1071              
1072             =back
1073              
1074             =over 4
1075              
1076             =item result example 3
1077              
1078             package main;
1079              
1080             use Venus::Try;
1081              
1082             my $try = Venus::Try->new;
1083              
1084             $try->call(sub {die});
1085              
1086             my $result = $try->result;
1087              
1088             # Exception! Venus::Error
1089              
1090             =back
1091              
1092             =cut
1093              
1094             =head1 ERRORS
1095              
1096             This package may raise the following errors:
1097              
1098             =cut
1099              
1100             =over 4
1101              
1102             =item error: C
1103              
1104             This package may raise an error_on_callback exception.
1105              
1106             B
1107              
1108             # given: synopsis;
1109              
1110             @args = ("Example", "execute");
1111              
1112             my $error = $try->throw('error_on_callback', @args)->catch('error');
1113              
1114             # my $name = $error->name;
1115              
1116             # "on_callback"
1117              
1118             # my $message = $error->message;
1119              
1120             # "Can't locate object method \"execute\" on package \"Example\""
1121              
1122             =back
1123              
1124             =head1 AUTHORS
1125              
1126             Awncorp, C
1127              
1128             =cut
1129              
1130             =head1 LICENSE
1131              
1132             Copyright (C) 2000, Al Newkirk.
1133              
1134             This program is free software, you can redistribute it and/or modify it under
1135             the terms of the Apache license version 2.0.
1136              
1137             =cut