File Coverage

blib/lib/Language/FormulaEngine/Error.pm
Criterion Covered Total %
statement 48 66 72.7
branch 10 18 55.5
condition 7 12 58.3
subroutine 15 22 68.1
pod 2 5 40.0
total 82 123 66.6


line stmt bran cond sub pod time code
1             package Language::FormulaEngine::Error;
2 8     8   2409 use Moo;
  8         58  
  8         45  
3             my @subclasses;
4             BEGIN {
5 8     8   47 @subclasses= qw( ErrInval ErrNA ErrREF ErrNUM ErrNAME );
6             # For each subclass name, export a function which can be used as the package
7             # name or as the package constructor.
8 8         26 for (@subclasses) {
9 40         138 my $pkg= __PACKAGE__.'::'.$_;
10 8     8   2981 no strict 'refs';
  8         45  
  8         573  
11 14 100   14   14615 *$_= sub { @_? $pkg->new(@_) : $pkg }
12 40         448 }
13             }
14 8     8   62 use Exporter 'import';
  8         25  
  8         5167  
15             our @EXPORT_OK= ( @subclasses, qw( auto_wrap_error ) );
16             our %EXPORT_TAGS= ( all => \@EXPORT_OK );
17              
18             # ABSTRACT: Exception objects for formula functions
19             our $VERSION = '0.08'; # VERSION
20              
21              
22             has message => ( is => 'rw', required => 1 );
23              
24             sub mine {
25 0     0 1 0 return Language::FormulaEngine::ErrorMine->new($_[0]);
26             }
27              
28             sub BUILDARGS {
29 12     12 0 9005 my $pkg= shift;
30 12 100 100     85 return $_[0] if @_ == 1 && ref $_[0] eq 'HASH';
31             # If odd number of arguments, and first is a scalar, then treat it as the message
32 11 100 66     57 unshift @_, 'message' if @_ & 1 and !ref $_[0];
33 11         207 return { @_ };
34             }
35              
36             our %err_patterns= (
37             ErrNA => qr/uninitialized|undefined/,
38             ErrNUM => qr/numeric/,
39             );
40             sub auto_wrap_error {
41             # allow to be called as package method, or not
42 1 50 33 1 1 1206 shift if @_ && !ref $_[0] && $_[0]->isa(__PACKAGE__);
      33        
43 1         5 my $msg= shift;
44             # return things which are already Error objects (or mines)
45 1 50       3 if (ref $msg) {
46 0 0       0 return $msg->disarm if ref($msg)->can('disarm');
47 0 0       0 return $msg if ref($msg)->can('message');
48             }
49             # Match message against patterns that Perl might have generated
50 1 50       3 if (defined $msg) {
51 1         7 $msg =~ s/ at \(eval.*//; # isn't useful
52 1         4 for (keys %err_patterns) {
53             return (__PACKAGE__.'::'.$_)->new(message => $msg)
54 1 50       44 if $msg =~ $err_patterns{$_};
55             }
56             } else {
57 0         0 $msg= '<undef>';
58             }
59 0         0 return __PACKAGE__->new(message => $msg);
60             }
61              
62             sub _fake_inc {
63 48     48   274 (my $pkg= caller) =~ s,::,/,g;
64 48         170 $INC{$pkg.'.pm'}= $INC{'Language/FormulaEngine/Error.pm'};
65             }
66              
67             sub _stringify {
68 0     0     my $self= shift;
69 0           my $cls= ref $self;
70 0           $cls =~ s/^Language::FormulaEngine::Error:://;
71 0           $cls . ': ' . $self->message;
72             }
73              
74 8     8   69 use overload '""' => \&_stringify;
  8         19  
  8         68  
75              
76             package Language::FormulaEngine::ErrorMine;
77             Language::FormulaEngine::Error::_fake_inc();
78             use overload # SOMEONE SET US UP THE BOMB!
79 0     0   0 '0+' => sub { die ${$_[0]} },
  0         0  
80 0     0   0 '""' => sub { die ${$_[0]} },
  0         0  
81 8     8   1450 bool => sub { die ${$_[0]} };
  8     0   20  
  8         108  
  0         0  
  0         0  
82              
83 0     0 0   sub new { bless $_[0], shift }
84 0     0 0   sub disarm { ${$_[0]} }
  0            
85              
86             package Language::FormulaEngine::Error::ErrInval;
87             Language::FormulaEngine::Error::_fake_inc();
88 8     8   1217 use Moo;
  8         16  
  8         54  
89             extends 'Language::FormulaEngine::Error';
90              
91             package Language::FormulaEngine::Error::ErrNA;
92             Language::FormulaEngine::Error::_fake_inc();
93 8     8   2898 use Moo;
  8         30  
  8         67  
94             extends 'Language::FormulaEngine::Error';
95              
96             package Language::FormulaEngine::Error::ErrREF;
97             Language::FormulaEngine::Error::_fake_inc();
98 8     8   2716 use Moo;
  8         21  
  8         39  
99             extends 'Language::FormulaEngine::Error';
100              
101             package Language::FormulaEngine::Error::ErrNUM;
102             Language::FormulaEngine::Error::_fake_inc();
103 8     8   2808 use Moo;
  8         38  
  8         55  
104             extends 'Language::FormulaEngine::Error::ErrInval';
105              
106             package Language::FormulaEngine::Error::ErrNAME;
107             Language::FormulaEngine::Error::_fake_inc();
108 8     8   2754 use Moo;
  8         33  
  8         64  
109             extends 'Language::FormulaEngine::Error';
110              
111             1;
112              
113             __END__
114              
115             =pod
116              
117             =encoding UTF-8
118              
119             =head1 NAME
120              
121             Language::FormulaEngine::Error - Exception objects for formula functions
122              
123             =head1 VERSION
124              
125             version 0.08
126              
127             =head1 DESCRIPTION
128              
129             In keeping with the theme of spreadsheet formulas, this module collection provides exception
130             objects that can be used for similar exception handling. These objects are intended to be
131             thrown using "die", but they can also be wrapped with a "trap" object so that they don't die
132             until used. For example, in a spreadsheet the error is I<returned> from a function, but
133             anything that uses that value needs to generate a similar error. That would require a lot of
134             argument checking and prevent using native Perl operations, but by returning an error wrapped
135             in a trap, any perl operation that attempts to use the trap will instead throw the exception
136             object.
137              
138             =head1 ATTRIBUTES
139              
140             All error objects have:
141              
142             =head2 message
143              
144             A text description of the error
145              
146             =head1 METHODS
147              
148             =head2 mine
149              
150             return $error->mine;
151              
152             This wraps the error in a "landmine". Any perl code that attempts to operate on the value of
153             the object will instead die with C<$error>. Call C<disarm> on the mine to return the original
154             C<$error> reference.
155              
156             =head1 EXPORTABLE FUNCTIONS
157              
158             Each of the sub-classes of error has a constructor function which you can export from this
159             module. You can also take a perl-generated exception and automatically wrap it with an
160             appropriate Error object using L</auto_wrap_error>.
161              
162             =head2 ErrInval
163              
164             The formula was given invalid inputs
165              
166             =head2 ErrNA
167              
168             The function encountered a condition where no value could be returned. i.e. the function is
169             not defined for the supplied parameters, such as accessing elements beyond the end of an array.
170              
171             =head2 ErrREF
172              
173             The formula referenced a non-existent or nonsensical variable.
174              
175             =head2 ErrNUM
176              
177             The function expected a number (or specific range of number, like positive, integer, etc) but
178             was given something it couldn't convert.
179              
180             =head2 ErrNAME
181              
182             The formula uses an unknown function name. This is thrown during compilation, or during
183             evaluation if the compile step is omitted.
184              
185             =head2 auto_wrap_error
186              
187             my $err_obj= auto_wrap_error( $perl_error_text );
188              
189             Look at the perl error to see if it is a known type of error, and wrap it with the appropriate
190             type of error object.
191              
192             =head1 AUTHOR
193              
194             Michael Conrad <mconrad@intellitree.com>
195              
196             =head1 COPYRIGHT AND LICENSE
197              
198             This software is copyright (c) 2023 by Michael Conrad, IntelliTree Solutions llc.
199              
200             This is free software; you can redistribute it and/or modify it under
201             the same terms as the Perl 5 programming language system itself.
202              
203             =cut