File Coverage

blib/lib/Config/Model/Exception.pm
Criterion Covered Total %
statement 213 260 81.9
branch 36 68 52.9
condition 8 23 34.7
subroutine 61 74 82.4
pod 1 19 5.2
total 319 444 71.8


line stmt bran cond sub pod time code
1             #
2             # This file is part of Config-Model
3             #
4             # This software is Copyright (c) 2005-2022 by Dominique Dumont.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10             package Config::Model::Exception 2.153; # TRIAL
11              
12 59     59   445 use warnings;
  59         135  
  59         2073  
13 59     59   363 use strict;
  59         159  
  59         1226  
14 59     59   317 use Data::Dumper;
  59         116  
  59         3357  
15 59     59   406 use Mouse;
  59         165  
  59         387  
16 59     59   22909 use v5.20;
  59         265  
17 59     59   370 use Carp;
  59         200  
  59         4451  
18              
19 59     59   506 use feature qw/postderef signatures/;
  59         144  
  59         11055  
20 59     59   455 no warnings qw/experimental::postderef experimental::signatures/;
  59         173  
  59         4848  
21              
22             @Carp::CARP_NOT=qw/Config::Model::Exception Config::Model::Exception::Any/;
23              
24             our $trace = 0;
25              
26 59     59   446 use Carp qw/longmess shortmess croak/;
  59         158  
  59         5375  
27              
28             use overload
29 59         661 '""' => \&full_msg_and_trace,
30 59     59   454 'bool' => \&is_error;
  59         139  
31              
32             has description => (
33             is => 'ro',
34             isa => 'Str',
35             lazy_build => 1
36             );
37              
38             sub _build_description {
39 32     32   91 my $self = shift;
40 32         137 return $self->_desc;
41             }
42              
43 0     0   0 sub _desc { 'config error' }
44              
45             has object => ( is => 'rw', isa => 'Ref') ;
46             has info => (is => 'rw', isa =>'Str', default => '');
47             has message => (is => 'rw', isa =>'Str', default => '');
48             has error => (is => 'rw', isa =>'Str', default => '');
49             has trace => (is => 'rw', isa =>'Str', default => '');
50              
51             # need to keep these objects around: in some tests the error() method is
52             # called after the instance is garbage collected. Instances are kept
53             # as weak ref in node (and othe tree objects). When instance is
54             # garbage collected, it's destroyed so error() can no longer be invoked.
55             # Solution: keep instance as error attributes.
56             has instance => ( is => 'rw', isa => 'Ref') ;
57              
58 143     143 1 8760 sub BUILD ($self, $) {
  143         241  
  143         249  
59 143 100       1439 $self->instance($self->object->instance) if defined $self->object;
60             }
61              
62             # without this overload, a test like if ($@) invokes '""' overload
63 46     46 0 4465 sub is_error { return ref ($_[0])}
64              
65              
66             sub Trace {
67 1     1 0 3239 $trace = shift;
68             }
69              
70             sub error_or_msg {
71 11     11 0 32 my $self = shift;
72 11   66     73 return $self->error || $self->message;
73             }
74              
75             sub throw {
76 143     143 0 339 my $class = shift;
77 143         2463 my $self = $class->new(@_);
78             # when an exception is thrown, caught and rethrown, the first full
79             # trace (provided by longmess) is clobbered by a second, shorter
80             # trace (also provided by longmess). To avoid that, the first
81             # trace must be stored.
82 143 100       990 $self->trace($trace ? longmess : '') ;
83 143         3370 die $self;
84             }
85              
86             sub rethrow {
87 2     2 0 5 my $self = shift;
88 2         13 die $self;
89             }
90              
91             sub full_msg_and_trace {
92 29     29 0 2147 my $self = shift;
93 29         229 my $msg = $self->full_message;
94 29         99 $msg .= $self->trace;
95 29         227 return $msg;
96             }
97              
98             sub as_string {
99 1     1 0 4 goto &full_msg_and_trace;
100             }
101              
102             sub full_message {
103 13     13 0 39 my $self = shift;
104              
105 13         57 my $obj = $self->object;
106 13 100       112 my $location = defined $obj ? $obj->name : '';
107 13         41 my $msg = "Configuration item ";
108 13 100       85 $msg .= "'$location' " if $location;
109 13         150 $msg .= "has a " . $self->description;
110 13   66     150 $msg .= ":\n\t" . ($self->error || $self->message) . "\n";
111 13 100       109 $msg .= $self->info . "\n" if $self->info;
112 13         50 return $msg;
113             }
114              
115             package Config::Model::Exception::Any 2.153; # TRIAL
116              
117 59     59   46750 use Mouse;
  59         168  
  59         371  
118             extends 'Config::Model::Exception';
119              
120             package Config::Model::Exception::ModelDeclaration 2.153; # TRIAL
121              
122 59     59   24710 use Mouse;
  59         210  
  59         278  
123             extends 'Config::Model::Exception::Fatal';
124              
125 2     2   15 sub _desc {'configuration model declaration error' }
126              
127             package Config::Model::Exception::User 2.153; # TRIAL
128              
129 59     59   25957 use Mouse;
  59         185  
  59         361  
130             extends 'Config::Model::Exception::Any';
131 1     1   7 sub _desc {'user error' }
132              
133              
134             ## old classes below
135             package Config::Model::Exception::Syntax 2.153; # TRIAL
136              
137 59     59   25289 use Mouse;
  59         202  
  59         322  
138             extends 'Config::Model::Exception::Any';
139              
140 0     0   0 sub _desc { 'syntax error' }
141              
142             has [qw/parsed_file parsed_line/] => (is => 'rw');
143              
144             sub full_message {
145 0     0 0 0 my $self = shift;
146              
147 0   0     0 my $fn = $self->parsed_file || '?';
148 0   0     0 my $line = $self->parsed_line || '?';
149 0         0 my $msg = "File $fn line $line ";
150 0         0 $msg .= "has a " . $self->description;
151 0         0 $msg .= ":\n\t" . $self->error_or_msg . "\n";
152              
153 0         0 return $msg;
154             }
155              
156             package Config::Model::Exception::LoadData 2.153; # TRIAL
157              
158 59     59   33508 use Mouse;
  59         210  
  59         353  
159             extends 'Config::Model::Exception::User';
160              
161 0     0   0 sub _desc { 'Load data structure (perl) error' };
162              
163             has wrong_data => (is => 'rw');
164              
165             sub full_message {
166 0     0 0 0 my $self = shift;
167              
168 0         0 my $obj = $self->object;
169 0 0       0 my $location = defined $obj ? $obj->name : '';
170 0         0 my $msg = "Configuration item ";
171 0         0 my $d = Data::Dumper->new( [ $self->wrong_data ], ['wrong data'] );
172 0         0 $d->Sortkeys(1);
173 0 0       0 $msg .= "'$location' " if $location;
174 0 0       0 $msg .= "(class " . $obj->config_class_name . ") " if $obj->get_type eq 'node';
175 0         0 $msg .= "has a " . $self->description;
176 0         0 $msg .= ":\n\t" . $self->error_or_msg . "\n";
177 0         0 $msg .= $d->Dump;
178              
179 0         0 return $msg;
180             }
181              
182             package Config::Model::Exception::Model 2.153; # TRIAL
183              
184 59     59   37602 use Carp;
  59         202  
  59         3805  
185 59     59   487 use Mouse;
  59         148  
  59         352  
186             extends 'Config::Model::Exception::Fatal';
187              
188 2     2   10 sub _desc { 'configuration model error'}
189              
190              
191             sub full_message {
192 2     2 0 6 my $self = shift;
193              
194 2   33     12 my $obj = $self->object
195             || croak "Internal error: no object parameter passed while throwing exception";
196 2         6 my $msg;
197 2 50       18 if ( $obj->isa('Config::Model::Node') ) {
198 0         0 $msg = "Node '" . $obj->name . "' of class " . $obj->config_class_name . ' ';
199             }
200             else {
201 2         11 my $element = $obj->element_name;
202 2         10 my $level = $obj->parent->get_element_property(
203             element => $element,
204             property => 'level'
205             );
206 2         19 my $location = $obj->location;
207 2         24 $msg = "In config class '" . $obj->parent->config_class_name. "',";
208 2 50       12 $msg .= " (location: $location)" if $location;
209 2         11 $msg .= " element '$element' (level $level) ";
210             }
211 2         19 $msg .= "has a " . $self->description;
212 2         11 $msg .= ":\n\t" . $self->error_or_msg . "\n";
213              
214 2         9 return $msg;
215             }
216              
217             package Config::Model::Exception::Load 2.153; # TRIAL
218              
219 59     59   39254 use Mouse;
  59         134  
  59         308  
220             extends 'Config::Model::Exception::User';
221              
222 9     9   44 sub _desc { 'Load command error'}
223              
224             has command => (is => 'rw', isa => 'ArrayRef|Str');
225              
226             sub full_message {
227 9     9 0 19 my $self = shift;
228              
229 9 100       49 my $location = defined $self->object ? $self->object->name : '';
230 9         58 my $msg = $self->description;
231 9         26 my $cmd = $self->command;
232 59     59   28190 no warnings 'uninitialized';
  59         204  
  59         10701  
233 9 0       40 my $cmd_str =
    50          
    100          
234             ref($cmd) ? join('',@$cmd)
235             : $cmd ? "'$cmd'"
236             : defined $cmd ? '<empty>'
237             : '<undef>';
238 9 100       33 $msg .= " in node '$location' " if $location;
239 9         19 $msg .= ':';
240 9         19 $msg .= "\n\tcommand: $cmd_str";
241 9         29 $msg .= "\n\t" . $self->error_or_msg . "\n";
242              
243 9         22 return $msg;
244             }
245              
246             package Config::Model::Exception::UnavailableElement 2.153; # TRIAL
247              
248 59     59   448 use Mouse;
  59         155  
  59         395  
249             extends 'Config::Model::Exception::User';
250              
251 2     2   12 sub _desc { 'unavailable element'}
252              
253             has [qw/element function/] => (is => 'rw', isa => 'Str');
254              
255              
256             sub full_message {
257 2     2 0 6 my $self = shift;
258              
259 2         7 my $obj = $self->object;
260 2         10 my $location = $obj->name;
261 2         31 my $msg = $self->description;
262 2         11 my $element = $self->element;
263 2         8 my $function = $self->function;
264 2         12 my $unavail = $obj->fetch_element(
265             name => $element,
266             check => 'no',
267             accept_hidden => 1
268             );
269 2         16 $msg .= " '$element' in node '$location'.\n";
270 2 50       8 $msg .= "\tError occurred when calling $function.\n" if defined $function;
271 2 50       33 $msg .= "\t" . $unavail->warp_error if $unavail->can('warp_error');
272              
273 2 50       21 $msg .= "\t" . $self->info . "\n" if defined $self->info;
274 2         6 return $msg;
275             }
276              
277             package Config::Model::Exception::AncestorClass 2.153; # TRIAL
278              
279 59     59   38626 use Mouse;
  59         159  
  59         298  
280             extends 'Config::Model::Exception::User';
281              
282 0     0   0 sub _desc { 'unknown ancestor class'}
283              
284              
285             package Config::Model::Exception::ObsoleteElement 2.153; # TRIAL
286              
287 59     59   25038 use Mouse;
  59         169  
  59         335  
288             extends 'Config::Model::Exception::User';
289              
290 0     0   0 sub _desc { 'Obsolete element' }
291              
292             has element => (is => 'rw', isa => 'Str');
293              
294             sub full_message {
295 0     0 0 0 my $self = shift;
296              
297 0         0 my $obj = $self->object;
298 0         0 my $element = $self->element;
299 0         0 my $msg = $self->description;
300              
301 0         0 my $location = $obj->name;
302 0   0     0 my $help = $obj->get_help_as_text($element) || '';
303              
304 0         0 $msg .= " '$element' in node '$location'.\n";
305 0         0 $msg .= "\t$help\n";
306 0 0       0 $msg .= "\t" . $self->info . "\n" if defined $self->info;
307 0         0 return $msg;
308             }
309              
310             package Config::Model::Exception::UnknownElement 2.153; # TRIAL
311              
312 59     59   33377 use Carp;
  59         206  
  59         3820  
313              
314 59     59   497 use Mouse;
  59         157  
  59         393  
315             extends 'Config::Model::Exception::User';
316              
317 4     4   46 sub _desc { 'unknown element' }
318              
319             has [qw/element function where/] => (is => 'rw');
320              
321             sub full_message {
322 2     2 0 7 my $self = shift;
323              
324 2         9 my $obj = $self->object;
325              
326 2 50 33     67 confess "Exception::UnknownElement: object is ", ref($obj), ". Expected a node"
      33        
327             unless ref($obj) && ($obj->isa('Config::Model::Node')
328             || $obj->isa('Config::Model::WarpedNode'));
329              
330 2         12 my $class_name = $obj->config_class_name;
331              
332             # class_name is undef if the warped_node is warped out
333 2         6 my @elements;
334 2 50       16 @elements = $obj->get_element_name(
335             class => $class_name,
336             ) if defined $class_name;
337              
338 2         9 my $msg = '';
339 2 50       15 $msg .= "Configuration path '" . $self->where . "': "
340             if defined $self->where;
341              
342 2 50       18 $msg .= "(function '" . $self->function . "') "
343             if defined $self->function;
344              
345 2 50       9 $msg = "object '" . $obj->name . "' error: " unless $msg;
346              
347 2         45 $msg .= $self->description . " '" . $self->element . "'.";
348              
349             # retrieve a support url from application info to guide user toward the right bug tracker
350 2   50     20 my $info = $obj->instance->get_support_info // 'to https://github.com/dod38fr/config-model/issues';
351 2         78 $msg .=
352             " Either your file has an error or $class_name model is lagging behind. "
353             . "In the latter case, please submit a bug report $info. See cme man "
354             . "page for details.\n";
355              
356 2 50       9 if (@elements) {
357 2         14 $msg .= "\tExpected elements: '" . join( "','", @elements ) . "'\n";
358             }
359             else {
360 0         0 $msg .= " (node is warped out)\n";
361             }
362              
363 2 50       23 my @match_keys = $obj->can('accept_regexp') ? $obj->accept_regexp() : ();
364 2 50       10 if (@match_keys) {
365 0         0 $msg .= "\tor an acceptable parameter matching '" . join( "','", @match_keys ) . "'\n";
366             }
367              
368             # inform about available elements after a change of warp master value
369 2 50       13 if ( defined $obj->parent ) {
370 0         0 my $parent = $obj->parent;
371 0         0 my $element_name = $obj->element_name;
372              
373 0 0       0 if ( $parent->element_type($element_name) eq 'warped_node' ) {
374 0         0 $msg .= "\t"
375             . $parent->fetch_element(
376             name => $element_name,
377             qw/check no accept_hidden 1/
378             )->warp_error;
379             }
380             }
381              
382 2 50       28 $msg .= "\t" . $self->info . "\n" if ( defined $self->info );
383              
384 2         10 return $msg;
385             }
386              
387             package Config::Model::Exception::WarpError 2.153; # TRIAL
388              
389 59     59   55587 use Mouse;
  59         160  
  59         325  
390             extends 'Config::Model::Exception::User';
391              
392 0     0   0 sub _desc { 'warp error'}
393              
394             package Config::Model::Exception::Fatal 2.153; # TRIAL
395              
396 59     59   24285 use Mouse;
  59         164  
  59         349  
397             extends 'Config::Model::Exception::Any';
398              
399 1     1   5 sub _desc { 'fatal error' }
400              
401              
402             package Config::Model::Exception::UnknownId 2.153; # TRIAL
403              
404 59     59   24748 use Mouse;
  59         163  
  59         998  
405             extends 'Config::Model::Exception::User';
406              
407 1     1   15 sub _desc { 'unknown identifier'}
408              
409             has [qw/element id function where/] => (is => 'rw', isa => 'Str');
410              
411             sub full_message {
412 1     1 0 4 my $self = shift;
413              
414 1         4 my $obj = $self->object;
415              
416 1         5 my $element = $self->element;
417 1         4 my $id_str = "'" . join( "','", $obj->fetch_all_indexes() ) . "'";
418              
419 1         5 my $msg = '';
420 1 50       11 $msg .= "In function " . $self->function . ": "
421             if defined $self->function;
422              
423 1 50       7 $msg .= "In " . $self->where . ": "
424             if defined $self->where;
425              
426 1         10 $msg .=
427             $self->description . " '"
428             . $self->id() . "'"
429             . " for element '"
430             . $obj->location
431             . "'\n\texpected: $id_str\n";
432              
433 1         3 return $msg;
434             }
435              
436             package Config::Model::Exception::WrongValue 2.153; # TRIAL
437              
438 59     59   35808 use Mouse;
  59         185  
  59         369  
439             extends 'Config::Model::Exception::User';
440              
441 8     8   45 sub _desc { 'wrong value'};
442              
443              
444             package Config::Model::Exception::WrongType 2.153; # TRIAL
445              
446 59     59   25363 use Mouse;
  59         164  
  59         326  
447             extends 'Config::Model::Exception::User';
448              
449 1     1   18 sub _desc { 'wrong element type' };
450              
451             has [qw/function got_type/] => (is => 'rw', isa => 'Str');
452             has [qw/expected_type/] => (is => 'rw');
453              
454             sub full_message {
455 1     1 0 7 my $self = shift;
456              
457 1         6 my $obj = $self->object;
458              
459 1         6 my $msg = '';
460 1 50       12 $msg .= "In function " . $self->function . ": "
461             if defined $self->function;
462              
463 1         6 my $type = $self->expected_type;
464              
465 1 50       9 $msg .=
466             $self->description
467             . " for element '"
468             . $obj->location
469             . "'\n\tgot type '"
470             . $self->got_type
471             . "', expected '"
472             . (ref $type ? join("' or '",@$type) : $type) . "' "
473             . $self->info . "\n";
474              
475 1         3 return $msg;
476             }
477              
478             package Config::Model::Exception::ConfigFile 2.153; # TRIAL
479              
480 59     59   34287 use Mouse;
  59         184  
  59         332  
481             extends 'Config::Model::Exception::User';
482              
483 0     0   0 sub _desc { 'error in configuration file' }
484              
485             package Config::Model::Exception::ConfigFile::Missing 2.153; # TRIAL
486              
487 59     59   24997 use Mouse;
  59         185  
  59         289  
488 59     59   22252 use Mouse::Util::TypeConstraints;
  59         173  
  59         501  
489              
490             extends 'Config::Model::Exception::ConfigFile';
491              
492 0     0   0 sub _desc { 'missing configuration file'}
493              
494             subtype 'ExcpPathTiny', as 'Object', where {$_->isa('Path::Tiny')} ;
495              
496             has file => (is => 'rw', isa => 'Str | ExcpPathTiny' );
497              
498             sub full_message {
499 0     0 0 0 my $self = shift;
500              
501 0         0 return "Error: cannot find configuration file " . $self->file . "\n";
502             }
503              
504             package Config::Model::Exception::Formula 2.153; # TRIAL
505              
506 59     59   16088 use Mouse;
  59         146  
  59         333  
507             extends 'Config::Model::Exception::Model';
508              
509 0     0   0 sub _desc { 'error in computation formula of the configuration model'}
510              
511             package Config::Model::Exception::Internal 2.153; # TRIAL
512              
513 59     59   25300 use Mouse;
  59         156  
  59         312  
514             extends 'Config::Model::Exception::Fatal';
515              
516 1     1   9 sub _desc { 'internal error' }
517              
518             1;
519              
520             # ABSTRACT: Exception mechanism for configuration model
521              
522             __END__
523              
524             =pod
525              
526             =encoding UTF-8
527              
528             =head1 NAME
529              
530             Config::Model::Exception - Exception mechanism for configuration model
531              
532             =head1 VERSION
533              
534             version 2.153
535              
536             =head1 SYNOPSIS
537              
538             use Config::Model::Exception;
539              
540             # later
541             my $kaboom = 1;
542             Config::Model::Exception::Model->throw(
543             error => "Went kaboom",
544             object => $self
545             ) if $kaboom;
546              
547             =head1 DESCRIPTION
548              
549             This module creates exception classes used by L<Config::Model>.
550              
551             All exception class name begins with C<Config::Model::Exception>
552              
553             The exception classes are:
554              
555             =over
556              
557             =item C<Config::Model::Exception>
558              
559             Base class. It accepts an C<object> argument. The user must pass the
560             reference of the object where the exception occurred. The object name
561             is used to generate the error message.
562              
563             =back
564              
565             TODO: list all exception classes and hierarchy.
566              
567             =head1 How to get trace
568              
569             By default, most of the exceptions do not print out the stack
570             trace. For debug purpose, you can force a stack trace for all
571             exception classes:
572              
573             Config::Model::Exception->Trace(1) ;
574              
575             =head1 AUTHOR
576              
577             Dominique Dumont, (ddumont at cpan dot org)
578              
579             =head1 SEE ALSO
580              
581             L<Config::Model>,
582             L<Config::Model::Instance>,
583             L<Config::Model::Node>,
584             L<Config::Model::Value>
585              
586             =head1 AUTHOR
587              
588             Dominique Dumont
589              
590             =head1 COPYRIGHT AND LICENSE
591              
592             This software is Copyright (c) 2005-2022 by Dominique Dumont.
593              
594             This is free software, licensed under:
595              
596             The GNU Lesser General Public License, Version 2.1, February 1999
597              
598             =cut