File Coverage

blib/lib/Object/InsideOut/Exception.pm
Criterion Covered Total %
statement 63 82 76.8
branch 22 30 73.3
condition 2 3 66.6
subroutine 9 9 100.0
pod 1 4 25.0
total 97 128 75.7


line stmt bran cond sub pod time code
1             package Object::InsideOut::Exception; {
2              
3 53     53   172 use strict;
  53         56  
  53         1148  
4 53     53   144 use warnings;
  53         55  
  53         5294  
5              
6             our $VERSION = '4.04';
7             $VERSION = eval $VERSION;
8              
9             # Exceptions generated by this module
10             use Exception::Class 1.29 (
11 53         661 'OIO' => {
12             'description' => 'Generic Object::InsideOut exception',
13             # First 3 fields must be: 'Package', 'File', 'Line'
14             'fields' => ['Error', 'Chain'],
15             },
16              
17             'OIO::Code' => {
18             'isa' => 'OIO',
19             'description' =>
20             'Object::InsideOut exception that indicates a coding error',
21             'fields' => ['Info', 'Code'],
22             },
23              
24             'OIO::Internal' => {
25             'isa' => 'OIO::Code',
26             'description' =>
27             'Object::InsideOut exception that indicates a internal problem',
28             'fields' => ['Code', 'Declaration'],
29             },
30              
31             'OIO::Attribute' => {
32             'isa' => 'OIO::Code',
33             'description' =>
34             'Object::InsideOut exception that indicates a coding error',
35             'fields' => ['Attribute'],
36             },
37              
38             'OIO::Method' => {
39             'isa' => 'OIO',
40             'description' =>
41             'Object::InsideOut exception that indicates an method calling error',
42             },
43              
44             'OIO::Args' => {
45             'isa' => 'OIO::Method',
46             'description' =>
47             'Object::InsideOut exception that indicates an argument error',
48             'fields' => ['Usage', 'Arg'],
49             },
50              
51             'OIO::Args::Unhandled' => {
52             'isa' => 'OIO::Args',
53             'description' =>
54             'Object::InsideOut exception that indicates an unhandled argument',
55             'fields' => ['Usage', 'Arg'],
56             },
57              
58             'OIO::Runtime' => {
59             'isa' => 'OIO::Code',
60             'description' =>
61             'Object::InsideOut exception that indicates a runtime error',
62             'fields' => ['Class1', 'Class2'],
63             },
64 53     53   21457 );
  53         395848  
65              
66              
67             # Turn on stack trace by default
68             OIO->Trace(1);
69              
70              
71             # A 'throw' method that adds location information to the exception object
72             sub OIO::die
73             {
74 97     97 0 123 my $class = shift;
75 97         219 my %args = @_;
76              
77             # Report on ourself?
78 97         129 my $report_self = delete($args{'self'});
79              
80             # Ignore ourselves in stack trace, unless told not to
81 97 50       204 if (! $report_self) {
82 97         156 my @ignore = ('Object::InsideOut::Exception', 'Object::InsideOut');
83 97 50       176 if (exists($args{'ignore_package'})) {
84 0 0       0 if (ref($args{'ignore_package'})) {
85 0         0 push(@ignore, @{$args{'ignore_package'}});
  0         0  
86             } else {
87 0         0 push(@ignore, $args{'ignore_package'});
88             }
89             }
90 97         166 $args{'ignore_package'} = \@ignore;
91             }
92              
93             # Remove any location information
94 97         112 my $location = delete($args{'location'});
95              
96             # Create exception object
97 97         465 my $e = $class->new(%args);
98              
99             # Override location information, if applicable
100 97 100       12438 if ($location) {
    50          
101 40         63 $e->{'package'} = $$location[0];
102 40         49 $e->{'file'} = $$location[1];
103 40         52 $e->{'line'} = $$location[2];
104             }
105              
106             # If reporting on ourself, then correct location info
107             elsif ($report_self) {
108 0         0 my $frame = $e->trace->frame(1);
109 0         0 $e->{'package'} = $frame->package();
110 0         0 $e->{'line'} = $frame->line();
111 0         0 $e->{'file'} = $frame->filename();
112             }
113              
114             # Throw error
115 53     53   100960 no strict 'refs';
  53         609  
  53         1835  
116 53     53   734 no warnings 'once';
  53         58  
  53         28902  
117 97 50       177 if (${$class.'::WARN_ONLY'}) {
  97         370  
118 0         0 warn $e->OIO::full_message();
119             } else {
120 97         354 $e->throw(%args);
121             }
122             }
123              
124              
125             # Provides a fully formatted error message for the exception object
126             sub OIO::full_message
127             {
128 54     54 1 5174 my $self = shift;
129              
130             # Start with error class and message
131 54         174 my $msg = ref($self) . ' error: ' . $self->message();
132 54         214 chomp($msg);
133              
134             # Add fields, if any
135 54         1027 my @fields = $self->Fields();
136 54         977 foreach my $field (@fields) {
137 218 100       308 next if ($field eq 'Chain');
138 164 100       266 if (exists($self->{$field})) {
139 27         63 $msg .= "\n$field: " . $self->{$field};
140 27         33 chomp($msg);
141             }
142             }
143              
144             # Add location
145 54         138 $msg .= "\nPackage: " . $self->package()
146             . "\nFile: " . $self->file()
147             . "\nLine: " . $self->line();
148              
149             # Chained error messages
150 54 50       7124 if (exists($self->{'Chain'})) {
151 0         0 my $chain = OIO::full_message($self->{'Chain'});
152 0         0 chomp($chain);
153 0         0 $chain =~ s/^/ /mg;
154 0         0 $msg .= "\n\nSubsequent to the above, the following error also occurred:\n"
155             . $chain;
156             }
157              
158 54         143 return ($msg . "\n");
159             }
160              
161              
162             # Catch untrapped errors
163             # Usage: local $SIG{'__DIE__'} = 'OIO::trap';
164             sub OIO::trap
165             {
166             # Just rethrow if already an exception object
167 6 100   6 0 109 if (Object::InsideOut::Util::is_it($_[0], 'Exception::Class::Base')) {
168 1         15 die($_[0]);
169             }
170              
171             # Package the error into an object
172             OIO->die(
173 5         29 'location' => [ caller() ],
174             'message' => 'Trapped uncaught error',
175             'Error' => join('', @_));
176             }
177              
178              
179             # Combine errors into a single error object
180             sub OIO::combine
181             {
182 60     60 0 96 my ($err1, $err2) = @_;
183              
184             # Massage second error, if needed
185 60 50 66     143 if ($err2 && ! ref($err2)) {
186 0         0 my $e = OIO->new(
187             'message' => "$err2",
188             'ignore_package' => [ 'Object::InsideOut::Exception' ]
189             );
190              
191 0         0 my $frame = $e->trace->frame(1);
192 0         0 $e->{'package'} = $frame->package();
193 0         0 $e->{'line'} = $frame->line();
194 0         0 $e->{'file'} = $frame->filename();
195              
196 0         0 $err2 = $e;
197             }
198              
199             # Massage first error, if needed
200 60 100       114 if ($err1) {
201 53 100       207 if (! ref($err1)) {
202 2         19 my $e = OIO->new(
203             'message' => "$err1",
204             'ignore_package' => [ 'Object::InsideOut::Exception' ]
205             );
206              
207 2         123 my $frame = $e->trace->frame(1);
208 2         217 $e->{'package'} = $frame->package();
209 2         10 $e->{'line'} = $frame->line();
210 2         8 $e->{'file'} = $frame->filename();
211              
212 2         7 $err1 = $e;
213             }
214              
215             # Combine errors, if possible
216 53 100       86 if ($err2) {
217 2 100       10 if (Object::InsideOut::Util::is_it($err1, 'OIO')) {
218 1         2 $err1->{'Chain'} = $err2;
219             } else {
220 1         7 warn($err2); # Can't combine
221             }
222             }
223              
224             } else {
225 7         6 $err1 = $err2;
226 7         7 undef($err2);
227             }
228              
229 60         138 return ($err1);
230             }
231              
232             } # End of package's lexical scope
233              
234             1;