File Coverage

blib/lib/Exception/Class.pm
Criterion Covered Total %
statement 156 162 96.3
branch 32 42 76.1
condition 13 25 52.0
subroutine 39 41 95.1
pod 5 12 41.6
total 245 282 86.8


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