File Coverage

blib/lib/Exception/Class.pm
Criterion Covered Total %
statement 152 158 96.2
branch 30 40 75.0
condition 13 25 52.0
subroutine 38 40 95.0
pod 5 12 41.6
total 238 275 86.5


line stmt bran cond sub pod time code
1             package Exception::Class;
2              
3 4     4   68599 use 5.008001;
  4         18  
4              
5 4     4   19 use strict;
  4         10  
  4         87  
6 4     4   16 use warnings;
  4         10  
  4         172  
7              
8             our $VERSION = '1.43';
9              
10 4     4   1488 use Exception::Class::Base;
  4         12  
  4         121  
11 4     4   27 use Scalar::Util qw( blessed reftype );
  4         648  
  4         294  
12              
13             our $BASE_EXC_CLASS;
14 4   50 4   564 BEGIN { $BASE_EXC_CLASS ||= 'Exception::Class::Base'; }
15              
16             our %CLASSES;
17              
18             sub import {
19 9     9   209 my $class = shift;
20              
21             ## no critic (Variables::ProhibitPackageVars)
22 9         20 local $Exception::Class::Caller = caller();
23              
24 9         21 my %c;
25              
26             my %needs_parent;
27 9         30 while ( my $subclass = shift ) {
28 19 100       49 my $def = ref $_[0] ? shift : {};
29             $def->{isa}
30             = $def->{isa}
31 19 50       66 ? ( ref $def->{isa} ? $def->{isa} : [ $def->{isa} ] )
    100          
32             : [];
33              
34 19         60 $c{$subclass} = $def;
35             }
36              
37             # We need to sort by length because if we check for keys in the
38             # Foo::Bar:: stash, this creates a "Bar::" key in the Foo:: stash!
39             MAKE_CLASSES:
40 9         62 foreach my $subclass ( sort { length $a <=> length $b } keys %c ) {
  32         55  
41 19         49 my $def = $c{$subclass};
42              
43             # We already made this one.
44 19 50       77 next if $CLASSES{$subclass};
45              
46             {
47             ## no critic (TestingAndDebugging::ProhibitNoStrict)
48 4     4   29 no strict 'refs';
  4         9  
  4         3117  
  19         77  
49 19         29 foreach my $parent ( @{ $def->{isa} } ) {
  19         55  
50 7 100       11 unless ( keys %{"$parent\::"} ) {
  7         42  
51             $needs_parent{$subclass} = {
52             parents => $def->{isa},
53 3         12 def => $def
54             };
55 3         11 next MAKE_CLASSES;
56             }
57             }
58             }
59              
60             $class->_make_subclass(
61 16   50     64 subclass => $subclass,
62             def => $def || {},
63             );
64             }
65              
66 9         4702 foreach my $subclass ( keys %needs_parent ) {
67              
68             # This will be used to spot circular references.
69 3         6 my %seen;
70 3         8 $class->_make_parents( \%needs_parent, $subclass, \%seen );
71             }
72             }
73              
74             sub _make_parents {
75 4     4   8 my $class = shift;
76 4         5 my $needs = shift;
77 4         8 my $subclass = shift;
78 4         4 my $seen = shift;
79 4         5 my $child = shift; # Just for error messages.
80              
81             ## no critic (TestingAndDebugging::ProhibitNoStrict, TestingAndDebugging::ProhibitProlongedStrictureOverride)
82 4     4   32 no strict 'refs';
  4         8  
  4         1383  
83              
84             # What if someone makes a typo in specifying their 'isa' param?
85             # This should catch it. Either it's been made because it didn't
86             # have missing parents OR it's in our hash as needing a parent.
87             # If neither of these is true then the _only_ place it is
88             # mentioned is in the 'isa' param for some other class, which is
89             # not a good enough reason to make a new class.
90             die
91             "Class $subclass appears to be a typo as it is only specified in the 'isa' param for $child\n"
92             unless exists $needs->{$subclass}
93             || $CLASSES{$subclass}
94 4 0 33     14 || keys %{"$subclass\::"};
  0   0     0  
95              
96 4         4 foreach my $c ( @{ $needs->{$subclass}{parents} } ) {
  4         11  
97              
98             # It's been made
99 4 100 66     13 next if $CLASSES{$c} || keys %{"$c\::"};
  1         6  
100              
101             die "There appears to be some circularity involving $subclass\n"
102 1 50       3 if $seen->{$subclass};
103              
104 1         2 $seen->{$subclass} = 1;
105              
106 1         8 $class->_make_parents( $needs, $c, $seen, $subclass );
107             }
108              
109 4 100 66     703 return if $CLASSES{$subclass} || keys %{"$subclass\::"};
  3         17  
110              
111             $class->_make_subclass(
112             subclass => $subclass,
113             def => $needs->{$subclass}{def}
114 3         10 );
115             }
116              
117             sub _make_subclass {
118 19     19   37 my $class = shift;
119 19         66 my %p = @_;
120              
121 19         40 my $subclass = $p{subclass};
122 19         31 my $def = $p{def};
123              
124 19         31 my $isa;
125 19 50       53 if ( $def->{isa} ) {
126 19 50       54 $isa = ref $def->{isa} ? join q{ }, @{ $def->{isa} } : $def->{isa};
  19         55  
127             }
128 19   66     83 $isa ||= $BASE_EXC_CLASS;
129              
130 19         31 my $version_name = 'VERSION';
131              
132 19         58 my $code = <<"EOPERL";
133             package $subclass;
134              
135             use base qw($isa);
136              
137             our \$$version_name = '1.1';
138              
139             1;
140              
141             EOPERL
142              
143 19 100       45 if ( $def->{description} ) {
144 1         14 ( my $desc = $def->{description} ) =~ s/([\\\'])/\\$1/g;
145 1         4 $code .= <<"EOPERL";
146             sub description
147             {
148             return '$desc';
149             }
150             EOPERL
151             }
152              
153 19         28 my @fields;
154 19 100       51 if ( my $fields = $def->{fields} ) {
155             @fields
156 4 100 66     33 = ref $fields && reftype $fields eq 'ARRAY' ? @$fields : $fields;
157              
158             $code
159             .= 'sub Fields { return ($_[0]->SUPER::Fields, '
160 4         10 . join( ', ', map {"'$_'"} @fields )
  5         24  
161             . ") }\n\n";
162              
163 4         11 foreach my $field (@fields) {
164 5         24 $code .= sprintf( "sub %s { \$_[0]->{%s} }\n", $field, $field );
165             }
166             }
167              
168 19 100       54 if ( my $alias = $def->{alias} ) {
169             ## no critic (Variables::ProhibitPackageVars)
170 2 50       6 die 'Cannot make alias without caller'
171             unless defined $Exception::Class::Caller;
172              
173             ## no critic (TestingAndDebugging::ProhibitNoStrict)
174 4     4   26 no strict 'refs';
  4         9  
  4         396  
175 2         12 *{"$Exception::Class::Caller\::$alias"}
176 2     2   13 = sub { $subclass->throw(@_) };
  2         671  
177             }
178              
179 19 50       43 if ( my $defaults = $def->{defaults} ) {
180 0         0 $code
181             .= "sub _defaults { return shift->SUPER::_defaults, our \%_DEFAULTS }\n";
182             ## no critic (TestingAndDebugging::ProhibitNoStrict)
183 4     4   27 no strict 'refs';
  4         10  
  4         874  
184 0         0 *{"$subclass\::_DEFAULTS"} = {%$defaults};
  0         0  
185             }
186              
187             ## no critic (BuiltinFunctions::ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval)
188 4     4 1 22 eval $code;
  4     3 1 16  
  4     1 1 336  
  3     1 1 17  
  3     1 0 6  
  3     1 1 159  
  1     1 0 5  
  1     1 0 2  
  1     1 0 53  
  1     1 0 5  
  1     1   2  
  1     1   47  
  1     1   5  
  1     1   2  
  1     4   69  
  1     0   5  
  1     2   1  
  1     1   153  
  1     1   6  
  1     1   2  
  1     4   659  
  1     0   5  
  1     1   2  
  1     1   63  
  1         6  
  1         1  
  1         58  
  1         9  
  1         3  
  1         162  
  1         8  
  1         2  
  1         122  
  1         7  
  1         2  
  1         53  
  1         11  
  1         2  
  1         170  
  1         7  
  1         2  
  1         283  
  19         1329  
  4         26  
  0         0  
  2         12  
  1         22  
  1         5  
  1         1699  
  4         1172  
  0         0  
  1         5  
  1         6  
189 19 50       65 die $@ if $@;
190              
191 19         64 ( my $filename = "$subclass.pm" ) =~ s{::}{/}g;
192 19         55 $INC{$filename} = __FILE__;
193              
194 19         80 $CLASSES{$subclass} = 1;
195             }
196              
197             sub caught {
198 5     5 0 33 my $e = $@;
199              
200 5 100       18 return $e unless $_[1];
201              
202 2 100 66     20 return unless blessed($e) && $e->isa( $_[1] );
203 1         3 return $e;
204             }
205              
206 1     1 0 699 sub Classes { sort keys %Exception::Class::CLASSES }
207              
208             1;
209              
210             # ABSTRACT: A module that allows you to declare real exception classes in Perl
211              
212             __END__