File Coverage

blib/lib/qbit/Exceptions.pm
Criterion Covered Total %
statement 39 99 39.3
branch 0 38 0.0
condition 0 33 0.0
subroutine 13 25 52.0
pod 0 5 0.0
total 52 200 26.0


line stmt bran cond sub pod time code
1              
2             =head1 Name
3              
4             qbit::Exceptions - qbit exceptions
5              
6             =cut
7              
8             package qbit::Exceptions;
9             $qbit::Exceptions::VERSION = '2.2';
10             =head1 Synopsis
11              
12             Usage:
13              
14             package Exception::Sample;
15             use base qw(Exception);
16              
17             package Sample;
18             use qbit;
19              
20             sub ttt {
21             throw 'Fatal error';
22              
23             # or
24             # throw Exception::Sample;
25              
26             # or
27             # throw Exception::Sample 'Some text describing problem';
28             };
29              
30             1;
31              
32             One more sample. Here we are not catching proper exception, and the program
33             stops. Finally blocks are always executed.
34              
35             package Exception::Sample;
36             use base qw(Exception);
37              
38             package Exception::OtherSample;
39             use base qw(Exception);
40              
41             package Sample;
42             use qbit;
43              
44             sub ttt {
45             my ($self) = @_;
46              
47             try {
48             print "try\n";
49             throw Exception::Sample 'Exception message';
50             }
51             catch Exception::OtherSample with {
52             print "catch\n";
53             }
54             finally {
55             print "finally\n";
56             };
57              
58             print "end\n";
59             }
60              
61             1;
62              
63             And one more code example. Here we have exception hierarchy. We are throwing
64             a complex exception but we can catch it with it's parents.
65              
66             package Exception::Basic;
67             use base qw(Exception);
68              
69             package Exception::Complex;
70             use base qw(Exception::Basic);
71              
72             package Sample;
73             use qbit;
74              
75             sub ttt {
76             my ($self) = @_;
77              
78             try {
79             print "try\n";
80             throw Exception::Complex 'Exception message';
81             }
82             catch Exception::Basic with {
83             print "catch\n";
84             }
85             finally {
86             print "finally\n";
87             };
88              
89             print "end\n";
90             }
91              
92             1;
93              
94             In catch and finally blocks you can access $@ that stores exception object.
95              
96             =cut
97              
98 8     8   30 use strict;
  8         10  
  8         208  
99 8     8   30 use warnings;
  8         9  
  8         291  
100              
101 8     8   28 use base qw(Exporter);
  8         10  
  8         1084  
102              
103             BEGIN {
104 8     8   12 our (@EXPORT, @EXPORT_OK);
105              
106 8         23 @EXPORT = qw(try catch with finally throw);
107 8         3524 @EXPORT_OK = @EXPORT;
108             }
109              
110             sub try(&;$) {
111 0     0 0   my ($sub, $catch) = @_;
112              
113 0           eval {$sub->()};
  0            
114              
115 0           my $cur_catch = $catch;
116 0   0       my $find_catch = !defined($catch) || $catch->[0] eq '::FINALLY::';
117              
118 0           my $first_exception = '';
119 0 0         if ($@) {
120 0 0 0       $@ = Exception::SysDie->new($@)
121             unless ref($@) && $@->isa('Exception');
122              
123 0           $first_exception = $@;
124              
125 0           while (defined($cur_catch)) {
126 0 0         last if $cur_catch->[0] eq '::FINALLY::';
127 0 0 0       if ($find_catch || $@->isa($cur_catch->[0])) {
128 0           $find_catch = 1;
129 0 0         if (ref($cur_catch->[1]) eq 'CODE') {
130 0           eval {$cur_catch->[1]($first_exception)};
  0            
131              
132 0 0         if ($@) {
133 0           $find_catch = 0;
134              
135 0 0 0       $@ = Exception::SysDie->new($@)
136             unless ref($@) && $@->isa('Exception');
137             }
138              
139 0           last;
140             } else {
141 0           $cur_catch = $cur_catch->[1];
142             }
143             } else {
144 0 0         $cur_catch = $cur_catch->[ref($cur_catch->[1]) eq 'CODE' ? 2 : 1];
145             }
146             }
147             }
148              
149 0 0 0       $cur_catch = $cur_catch->[ref($cur_catch->[1]) eq 'CODE' ? 2 : 1]
      0        
150             while ref($cur_catch) && defined($cur_catch) && $cur_catch->[0] ne '::FINALLY::';
151              
152 0 0 0       die("Expected semicolon after catch block (" . join(", ", (caller())[1, 2]) . ")\n")
153             if defined($cur_catch) && ref($cur_catch) ne 'ARRAY';
154              
155 0 0         $cur_catch->[1]($first_exception) if defined($cur_catch);
156              
157 0 0 0       die $@ if $@ && !$find_catch;
158             }
159              
160             sub catch(&;$) {
161 0     0 0   return [Exception => @_];
162             }
163              
164             sub with(&;$) {
165 0     0 0   return @_;
166             }
167              
168             sub finally(&;$) {
169 0 0   0 0   if (defined($_[1])) {die("Expected semicolon after finally block (" . join(", ", (caller())[1, 2]) . ")\n");}
  0            
170 0           return ['::FINALLY::' => @_];
171             }
172              
173             sub throw($) {
174 0     0 0   my ($exception) = @_;
175 0 0         $exception = Exception->new($exception) unless ref($exception);
176 0           die $exception;
177             }
178              
179             package Exception;
180             $Exception::VERSION = '2.2';
181 8     8   39 use strict;
  8         9  
  8         161  
182 8     8   24 use warnings;
  8         10  
  8         410  
183 8     8   44 use overload '""' => sub {shift->as_string()};
  8     0   12  
  8         77  
  0         0  
184              
185 8     8   479 use Scalar::Util qw(blessed);
  8         17  
  8         3844  
186              
187             sub new {
188 0     0     my ($this, $text, %data) = @_;
189 0   0       my $class = ref($this) || $this;
190              
191 0 0         $text = '' if !defined $text;
192              
193 0           my @call_stack = ();
194 0           my $i = 0;
195              
196 0           while (1) {
197              
198             package DB;
199 0           $DB::VERSION = '2.2';
200 0           my ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask) =
201             caller($i);
202              
203 0 0         last if !defined($package);
204              
205 0 0 0       push(
206             @call_stack,
207             {
208             package => $package,
209             filename => $filename,
210             line => $line,
211             subroutine => $subroutine,
212             args => [@DB::args],
213             }
214             )
215             if $package ne 'qbit::Exceptions'
216             && $subroutine ne 'qbit::Exceptions::try';
217              
218 0           ++$i;
219             }
220              
221 0           my $caller = shift(@call_stack);
222             my $self = {
223             %data,
224             (
225             blessed($text) && $text->isa('Exception')
226             ? (text => $text->{'text'}, parent => $text)
227             : (text => $text)
228             ),
229             filename => $caller->{'filename'},
230             package => $caller->{'package'},
231 0 0 0       line => $caller->{'line'},
232             callstack => \@call_stack,
233             };
234              
235 0           bless $self, $class;
236 0           return $self;
237             }
238              
239             sub catch {
240 0     0     return \@_;
241             }
242              
243             sub throw {
244 0     0     qbit::Exceptions::throw(shift->new(@_));
245             }
246              
247             sub message {
248 0     0     return shift->{'text'};
249             }
250              
251             sub as_string {
252 0     0     my ($self) = @_;
253              
254             return
255             ref($self)
256             . ": $self->{'text'}\n"
257             . " Package: $self->{'package'}\n"
258             . " Filename: $self->{'filename'} (line $self->{'line'})\n"
259             . " CallStack:\n"
260             . ' '
261             . join("\n ",
262 0           map {$_->{'subroutine'} . "() called at '$_->{'filename'}' line $_->{'line'}"} @{$self->{'callstack'}})
  0            
263             . "\n"
264 0 0         . ($self->{'parent'} ? "\n$self->{'parent'}\n" : '');
265             }
266              
267             package Exception::SysDie;
268             $Exception::SysDie::VERSION = '2.2';
269 8     8   37 use base qw(Exception);
  8         9  
  8         2413  
270              
271 8     8   37 use strict;
  8         10  
  8         150  
272 8     8   26 use warnings;
  8         11  
  8         677  
273              
274             sub new {
275 0     0     my ($self, $text) = @_;
276              
277 0           chomp($text);
278              
279 0           return $self->SUPER::new($text);
280             }
281              
282             package Exception::BadArguments;
283             $Exception::BadArguments::VERSION = '2.2';
284 8     8   34 use base qw(Exception);
  8         17  
  8         1950  
285              
286             package Exception::Denied;
287             $Exception::Denied::VERSION = '2.2';
288 8     8   32 use base qw(Exception);
  8         11  
  8         1689  
289              
290             1;