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   362 use strict;
  53         110  
  53         1451  
4 53     53   250 use warnings;
  53         106  
  53         7685  
5              
6             our $VERSION = '4.05';
7             $VERSION = eval $VERSION;
8              
9             # Exceptions generated by this module
10             use Exception::Class 1.29 (
11 53         824 '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   26405 );
  53         556894  
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 308 my $class = shift;
75 97         352 my %args = @_;
76              
77             # Report on ourself?
78 97         251 my $report_self = delete($args{'self'});
79              
80             # Ignore ourselves in stack trace, unless told not to
81 97 50       268 if (! $report_self) {
82 97         262 my @ignore = ('Object::InsideOut::Exception', 'Object::InsideOut');
83 97 50       311 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         264 $args{'ignore_package'} = \@ignore;
91             }
92              
93             # Remove any location information
94 97         210 my $location = delete($args{'location'});
95              
96             # Create exception object
97 97         647 my $e = $class->new(%args);
98              
99             # Override location information, if applicable
100 97 100       20159 if ($location) {
    50          
101 40         108 $e->{'package'} = $$location[0];
102 40         83 $e->{'file'} = $$location[1];
103 40         91 $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   156879 no strict 'refs';
  53         136  
  53         1810  
116 53     53   307 no warnings 'once';
  53         117  
  53         36553  
117 97 50       308 if (${$class.'::WARN_ONLY'}) {
  97         597  
118 0         0 warn $e->OIO::full_message();
119             } else {
120 97         550 $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 16812 my $self = shift;
129              
130             # Start with error class and message
131 54         260 my $msg = ref($self) . ' error: ' . $self->message();
132 54         378 chomp($msg);
133              
134             # Add fields, if any
135 54         1346 my @fields = $self->Fields();
136 54         1342 foreach my $field (@fields) {
137 218 100       500 next if ($field eq 'Chain');
138 164 100       412 if (exists($self->{$field})) {
139 27         91 $msg .= "\n$field: " . $self->{$field};
140 27         58 chomp($msg);
141             }
142             }
143              
144             # Add location
145 54         194 $msg .= "\nPackage: " . $self->package()
146             . "\nFile: " . $self->file()
147             . "\nLine: " . $self->line();
148              
149             # Chained error messages
150 54 50       12031 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         202 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 123 if (Object::InsideOut::Util::is_it($_[0], 'Exception::Class::Base')) {
168 1         20 die($_[0]);
169             }
170              
171             # Package the error into an object
172             OIO->die(
173 5         36 '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 65     65 0 162 my ($err1, $err2) = @_;
183              
184             # Massage second error, if needed
185 65 50 66     242 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 65 100       189 if ($err1) {
201 58 100       354 if (! ref($err1)) {
202 3         70 my $e = OIO->new(
203             'message' => "$err1",
204             'ignore_package' => [ 'Object::InsideOut::Exception' ]
205             );
206              
207 3         329 my $frame = $e->trace->frame(1);
208 3         505 $e->{'package'} = $frame->package();
209 3         27 $e->{'line'} = $frame->line();
210 3         20 $e->{'file'} = $frame->filename();
211              
212 3         20 $err1 = $e;
213             }
214              
215             # Combine errors, if possible
216 58 100       165 if ($err2) {
217 2 100       12 if (Object::InsideOut::Util::is_it($err1, 'OIO')) {
218 1         3 $err1->{'Chain'} = $err2;
219             } else {
220 1         8 warn($err2); # Can't combine
221             }
222             }
223              
224             } else {
225 7         14 $err1 = $err2;
226 7         10 undef($err2);
227             }
228              
229 65         196 return ($err1);
230             }
231              
232             } # End of package's lexical scope
233              
234             1;