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