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   185 use strict;
  53         61  
  53         1190  
4 53     53   163 use warnings;
  53         51  
  53         5508  
5              
6             our $VERSION = '4.03';
7             $VERSION = eval $VERSION;
8              
9             # Exceptions generated by this module
10             use Exception::Class 1.29 (
11 53         731 '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   22664 );
  53         407003  
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 130 my $class = shift;
75 97         284 my %args = @_;
76              
77             # Report on ourself?
78 97         159 my $report_self = delete($args{'self'});
79              
80             # Ignore ourselves in stack trace, unless told not to
81 97 50       176 if (! $report_self) {
82 97         178 my @ignore = ('Object::InsideOut::Exception', 'Object::InsideOut');
83 97 50       204 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         234 $args{'ignore_package'} = \@ignore;
91             }
92              
93             # Remove any location information
94 97         128 my $location = delete($args{'location'});
95              
96             # Create exception object
97 97         493 my $e = $class->new(%args);
98              
99             # Override location information, if applicable
100 97 100       12593 if ($location) {
    50          
101 40         76 $e->{'package'} = $$location[0];
102 40         51 $e->{'file'} = $$location[1];
103 40         62 $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   106469 no strict 'refs';
  53         729  
  53         1985  
116 53     53   179 no warnings 'once';
  53         63  
  53         28877  
117 97 50       86 if (${$class.'::WARN_ONLY'}) {
  97         542  
118 0         0 warn $e->OIO::full_message();
119             } else {
120 97         379 $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 5721 my $self = shift;
129              
130             # Start with error class and message
131 54         193 my $msg = ref($self) . ' error: ' . $self->message();
132 54         237 chomp($msg);
133              
134             # Add fields, if any
135 54         1050 my @fields = $self->Fields();
136 54         992 foreach my $field (@fields) {
137 218 100       620 next if ($field eq 'Chain');
138 164 100       261 if (exists($self->{$field})) {
139 27         64 $msg .= "\n$field: " . $self->{$field};
140 27         37 chomp($msg);
141             }
142             }
143              
144             # Add location
145 54         146 $msg .= "\nPackage: " . $self->package()
146             . "\nFile: " . $self->file()
147             . "\nLine: " . $self->line();
148              
149             # Chained error messages
150 54 50       6872 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         148 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 83 if (Object::InsideOut::Util::is_it($_[0], 'Exception::Class::Base')) {
168 1         8 die($_[0]);
169             }
170              
171             # Package the error into an object
172             OIO->die(
173 5         24 '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 98 my ($err1, $err2) = @_;
183              
184             # Massage second error, if needed
185 60 50 66     136 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       120 if ($err1) {
201 53 100       212 if (! ref($err1)) {
202 2         23 my $e = OIO->new(
203             'message' => "$err1",
204             'ignore_package' => [ 'Object::InsideOut::Exception' ]
205             );
206              
207 2         147 my $frame = $e->trace->frame(1);
208 2         210 $e->{'package'} = $frame->package();
209 2         10 $e->{'line'} = $frame->line();
210 2         10 $e->{'file'} = $frame->filename();
211              
212 2         7 $err1 = $e;
213             }
214              
215             # Combine errors, if possible
216 53 100       102 if ($err2) {
217 2 100       8 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         8 $err1 = $err2;
226 7         5 undef($err2);
227             }
228              
229 60         117 return ($err1);
230             }
231              
232             } # End of package's lexical scope
233              
234             1;