File Coverage

lib/Getopt/Class.pm
Criterion Covered Total %
statement 455 581 78.3
branch 157 326 48.1
condition 107 273 39.1
subroutine 62 77 80.5
pod 17 18 94.4
total 798 1275 62.5


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Getopt::Long with Class - ~/lib/Getopt/Class.pm
3             ## Version v1.1.4
4             ## Copyright(c) 2024 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2020/04/25
7             ## Modified 2025/03/13
8             ## All rights reserved
9             ##
10             ## This program is free software; you can redistribute it and/or modify it
11             ## under the same terms as Perl itself.
12             ##----------------------------------------------------------------------------
13             package Getopt::Class;
14             BEGIN
15             {
16 6     6   734372 use strict;
  6         13  
  6         224  
17 6     6   47 use warnings;
  6         19  
  6         289  
18 6     6   2833 use parent qw( Module::Generic );
  6         1845  
  6         36  
19 6     6   1559119 use vars qw( $VERSION );
  6         25  
  6         338  
20 6     6   39 use Clone;
  6         9  
  6         300  
21 6     6   5835 use DateTime;
  6         3568868  
  6         386  
22 6     6   4363 use DateTime::Format::Strptime;
  6         335521  
  6         27  
23 6     6   5232 use Getopt::Long;
  6         72142  
  6         28  
24 6     6   4760 use Module::Generic::Array;
  6         84956  
  6         296  
25 6     6   9232 use Module::Generic::File qw( file );
  6         561921  
  6         124  
26 6     6   7044 use Module::Generic::Scalar;
  6         261250  
  6         444  
27 6     6   68 use Scalar::Util;
  6         15  
  6         429  
28 6     6   160 our $VERSION = 'v1.1.4';
29             };
30              
31 6     6   54 use strict;
  6         12  
  6         145  
32 6     6   32 use warnings;
  6         10  
  6         10756  
33              
34             sub init
35             {
36 6     6 1 1823470 my $self = shift( @_ );
37 6   50     38 my $param = shift( @_ ) || return( $self->error( "No hash parameter was provided." ) );
38 6 50       68 return( $self->error( "Hash of parameters provided ($param) is not an hash reference." ) ) if( !$self->_is_hash( $param ) );
39 6 50       165 $self->SUPER::init( $param ) || return( $self->pass_error );
40 6         8101 $self->{configured} = 0;
41 6         23 $self->{classes} = {};
42 6         22 $self->{missing} = [];
43 6         21 $self->{colour_open} = '<';
44 6         22 $self->{colour_close} = '>';
45 6   50     27 my $dict = $param->{dictionary} || return( $self->error( "No dictionary was provided to initiate Getopt::Long" ) );
46 6 50       35 return( $self->error( "Dictionary provided is not a hash reference." ) ) if( !$self->_is_hash( $dict ) );
47 6         110 $self->dictionary( $dict );
48            
49             # Set the aliases hash reference used to contain each of the option aliases,e ach pointing to the same dictionary definition
50 6         6118 $self->{aliases} = {};
51            
52             # Tie'ing will make sure that values set for a key or its aliases are populated to other aliases
53             # Getopt::Long already does it, but this takes care of synchronising values for all aliases AFTER Getopt::Long has processed the options
54             # So that if the user change an option value using an alias:, e.g.:
55             # last_name => { type => 'string', alias => [qw( surname )] }
56             # last_name and surname would have the same value set thanks to Getopt::Long
57             # --last-name = 'Einstein';
58             # But if, after, the user does something like:
59             # $opts->{surname} = 'Doe';
60             # $opts->{last_name} would still be 'Einstein'
61             # Getopt::Class::Alias ensures the values for aliases and original key are the same seamlessly
62             # The way tie works means we must tie en empty hash, because we cannot tie an already populated hash sadly enough
63 6         25 my %options = ();
64             my $tie = tie( %options, 'Getopt::Class::Alias',
65             {
66             dict => $dict,
67             aliases => $self->{aliases},
68             # debug => $self->{debug}
69 6   50     75 }) || return( $self->error( "Unable to get a Getopt::Class::Alias tie object: ", Getopt::Class::Alias->error ) );
70            
71 6         100 $self->{configure_options} = [qw( no_ignore_case no_auto_abbrev auto_version auto_help )];
72 6         31 my $opts = \%options;
73 6         28 my $params = [];
74              
75 6         227 foreach my $k ( sort( keys( %$dict ) ) )
76             {
77 114         217 my $k2_dash = $k;
78 114         249 $k2_dash =~ tr/_/-/;
79 114         164 my $k2_under = $k;
80 114         163 $k2_under =~ tr/-/_/;
81            
82 114         203 my $def = $dict->{ $k };
83 114 50       247 next if( $def->{__no_value_assign} );
84              
85             # Do some pre-processing work for booleans
86 114 100 66     368 if( $def->{type} eq 'boolean' && !exists( $def->{mirror} ) )
87             {
88 42         74 my $mirror_opt;
89             # If this is a boolean, add their counterpart, if necessary
90 42 50 33     476 if( substr( $k, 0, 5 ) eq 'with_' &&
    100 66        
    100 66        
    100 66        
91             !exists( $dict->{ 'without_' . substr( $k, 5 ) } ) )
92             {
93 0         0 $mirror_opt = 'without_' . substr( $k, 5 );
94             }
95             elsif( substr( $k, 0, 8 ) eq 'without_' &&
96             !exists( $dict->{ 'with_' . substr( $k, 8 ) } ) )
97             {
98 6         20 $mirror_opt = 'with_' . substr( $k, 8 );
99             }
100             elsif( substr( $k, 0, 7 ) eq 'enable_' &&
101             !exists( $dict->{ 'disable_' . substr( $k, 7 ) } ) )
102             {
103 6         20 $mirror_opt = 'disable_' . substr( $k, 7 );
104             }
105             elsif( substr( $k, 0, 8 ) eq 'disable_' &&
106             !exists( $dict->{ 'enable_' . substr( $k, 8 ) } ) )
107             {
108 6         34 $mirror_opt = 'enable_' . substr( $k, 8 );
109             }
110            
111 42 100       106 if( defined( $mirror_opt ) )
112             {
113 18         33 my $false = 0;
114             my $val = exists( $def->{default} )
115             # ? ( Scalar::Util::reftype( $def->{default} // '' ) eq 'SCALAR' || ref( $def->{default} // '' ) eq 'CODE' )
116             ? ( $self->_is_scalar( $def->{default} ) || $self->_is_code( $def->{default} ) || ref( $def->{default} ) )
117             ? $def->{default}
118             : \$def->{default}
119             : exists( $def->{code} )
120             ? $def->{code}
121 18 50 33     166 : \$false;
    0          
    50          
122 18         745 my $copy = Clone::clone( $def );
123 18         73 $dict->{ $mirror_opt } = $copy;
124             $def->{mirror} = { name => $mirror_opt, toggle => sub
125             {
126 0     0   0 my( $value ) = @_;
127 0         0 $opts->{ $mirror_opt } = int( !$value );
128 18         147 }};
129 18 50 33     122 $def->{mirror}->{default} = delete( $def->{default} ) if( exists( $def->{default} ) && defined( $def->{default} ) );
130             # A code is used for this boolean, so we create an anon sub that call this sub just like Getopt::Long would
131 18 50       54 if( ref( $val ) eq 'CODE' )
132             {
133             $copy->{mirror} = { name => $k, toggle => sub
134             {
135 0     0   0 my( $value ) = @_;
136 0         0 $val->( $k, int( !$value ) );
137 0         0 }};
138             }
139             # Otherwise, we create a sub that set the mirror value
140             else
141             {
142             $copy->{mirror} = { name => $k, toggle => sub
143             {
144 3     3   7 my( $value ) = @_;
145 3         14 $opts->{ $k } = int( !$value );
146 18         157 }};
147             }
148 18 50       102 $copy->{mirror}->{default} = int( !$def->{mirror}->{default} ) if( exists( $def->{mirror}->{default} ) );
149             # We remove it, because they would be assigned by Getopt::Long even if not triggered and this would bother us.
150 18         31 delete( $def->{default} );
151 18         33 delete( $copy->{default} );
152             $def->{default} = sub
153             {
154 0     0   0 my( $option, $value ) = @_;
155 0 0       0 return if( $def->{mirror}->{is_set} );
156 0         0 $def->{mirror}->{value} = $value;
157 0         0 $def->{mirror}->{is_set}++;
158 0         0 $def->{mirror}->{toggle}->( $value );
159 18         498 };
160             $copy->{default} = sub
161             {
162 3     3   593 my( $option, $value ) = @_;
163 3 50       13 return if( $copy->{mirror}->{is_set} );
164 3         8 $copy->{mirror}->{value} = $value;
165 3         8 $copy->{mirror}->{is_set}++;
166 3         12 $copy->{mirror}->{toggle}->( $value );
167 18         107 };
168 18         66 $def->{__no_value_assign} = 1;
169             }
170             }
171             }
172            
173             # Build the options parameters
174 6         93 foreach my $k ( sort( keys( %$dict ) ) )
175             {
176 132         217 my $k2_dash = $k;
177 132         281 $k2_dash =~ tr/_/-/;
178 132         206 my $k2_under = $k;
179 132         195 $k2_under =~ tr/-/_/;
180            
181 132         223 my $def = $dict->{ $k };
182            
183 132         332 my $opt_name = [ $k2_under ];
184             # If the dictionary element is given with dash, e.g. some-thing, we replace it with some_thing, which is our standard
185             # and we set some-thing as an alias
186 132 50 33     405 if( CORE::index( $k, '-' ) != -1 && $k eq $k2_dash )
187             {
188 0         0 $dict->{ $k2_under } = CORE::delete( $dict->{ $k } );
189 0         0 $k = $k2_under;
190             }
191             # Add the dash option as an alias if it is not the same as the underscore one, such as when this is just one word, e.g. version
192 132 100       321 CORE::push( @$opt_name, $k2_dash ) if( $k2_dash ne $k2_under );
193              
194 132 100 66     607 if( !ref( $def->{alias} ) && CORE::length( $def->{alias} ) )
195             {
196 18         49 $def->{alias} = [$def->{alias}];
197             }
198             # Add the given aliases, if any
199 132 100       654 if( $self->_is_array( $def->{alias} ) )
200             {
201 18 50       220 push( @$opt_name, @{$def->{alias}} ) if( scalar( @{$def->{alias}} ) );
  18         88  
  18         56  
202             # push( @$opt_name, $k2_under ) if( !scalar( grep( /^$k2_under$/, @{$def->{alias}} ) ) );
203             }
204             # Now, also add the original key-something and key_something to the alias, so we can find them from one of the aliases
205             # When we do exec, we'll be able to find all the aliases
206 132 100       1248 $def->{alias} = [] if( !CORE::exists( $def->{alias} ) );
207 132 50       237 CORE::push( @{$def->{alias}}, $k2_dash ) if( !scalar( grep( /^$k2_dash$/, @{$def->{alias}} ) ) );
  132         313  
  132         619  
208 132 100       218 CORE::push( @{$def->{alias}}, $k2_under ) if( !scalar( grep( /^$k2_under$/, @{$def->{alias}} ) ) );
  60         161  
  132         2817  
209 132         678 $def->{alias} = Module::Generic::Array->new( $def->{alias} );
210            
211 132         2090 my $opt = join( '|', @$opt_name );
212 132 100 66     618 if( defined( $def->{default} ) && ( ref( $def->{default} ) || length( $def->{default} ) ) )
      66        
213             {
214 84 50       185 if( $def->{type} eq 'file' )
215             {
216 0 0 0     0 if( !ref( $def->{default} ) || $self->_can_overload( $def->{default} => '""' ) )
217             {
218 0         0 $opts->{ $k2_under } = "$def->{default}";
219             }
220             }
221             else
222             {
223 84         409 $opts->{ $k2_under } = $def->{default};
224             }
225             }
226             else
227             {
228 48         272 $opts->{ $k2_under } = '';
229             }
230 132         313 my $suff = '';
231 132 100 66     1339 if( $def->{type} eq 'string' || $def->{type} eq 'scalar' )
    100 66        
    100 66        
    100 66        
    50 33        
    100 33        
    100 33        
    50          
    50          
    0          
    0          
    0          
232             {
233 24         46 $suff = '=s';
234             }
235             elsif( $def->{type} eq 'string-hash' )
236             {
237 6         13 $suff = '=s%';
238             }
239             elsif( $def->{type} eq 'array' ||
240             $def->{type} eq 'file-array' ||
241             $def->{type} eq 'uri-array' )
242             {
243 6         16 $suff = '=s@';
244 6 50       28 $opts->{ $k2_under } = [] unless( length( $def->{default} ) );
245 6 0 33     27 $def->{min} = 1 if( !exists( $def->{min} ) && !exists( $def->{max} ) );
246             }
247             elsif( $def->{type} eq 'boolean' )
248             {
249 60         95 $suff = '!';
250 60 0 33     165 if( exists( $def->{code} ) &&
      0        
      33        
251             ref( $def->{code} ) eq 'CODE' &&
252             # Will not override if a code ref is already assigned
253             ref( $opts->{ $k2_under } // '' ) ne 'CODE' )
254             {
255 0         0 $opts->{ $k2_under } = $def->{code};
256             }
257             }
258             elsif( $def->{type} eq 'hash' )
259             {
260 0         0 $suff = '=s%';
261 0 0       0 $opts->{ $k2_under } = {} unless( length( $def->{default} ) );
262             }
263             elsif( $def->{type} eq 'code' && ref( $def->{code} ) eq 'CODE' )
264             {
265 18         85 $opts->{ $k2_under } = $def->{code};
266             }
267             elsif( $def->{type} eq 'integer' )
268             {
269 12         35 $suff = '=i';
270             }
271             elsif( $def->{type} eq 'decimal' || $def->{type} eq 'float' || $def->{type} eq 'number' )
272             {
273 0         0 $suff .= '=f';
274             }
275             elsif( $def->{type} eq 'date' || $def->{type} eq 'datetime' )
276             {
277 6         17 $suff = '=s';
278             }
279             elsif( $def->{type} eq 'code' )
280             {
281 0 0       0 return( $self->error( "Type is code, but there is no property code for this option \"$k\"." ) ) if( !CORE::exists( $def->{code} ) );
282 0 0       0 return( $self->error( "Type is code, but the property code is not a code reference for this option \"$k\"." ) ) if( ref( $def->{code} ) ne 'CODE' );
283 0         0 $opts->{ $k2_under } = $def->{code};
284             }
285             elsif( $def->{type} eq 'file' )
286             {
287 0         0 $suff = '=s';
288             }
289             elsif( $def->{type} eq 'uri' )
290             {
291 0         0 $suff = '=s';
292             }
293            
294 132 100       334 if( $def->{min} )
295             {
296             # If there is no max, it would be for example s{1,}
297             # 2nd formatter is %s because it could be blank. %d would translate to 0 when blank.
298 6     6   86 no warnings 'uninitialized';
  6         24  
  6         23972  
299 6         82 $suff .= sprintf('{%d,%s}', @$def{ qw( min max ) } );
300             }
301            
302 132 50 66     349 if( $def->{re} && ref( $def->{re} ) ne 'Regexp' )
303             {
304 0         0 return( $self->error( "Regular expression provided for property \"$k\" ($def->{re}) is not a proper regular expression. I was expecting something like qr// and of type 'Regexp'." ) );
305             }
306 132         538 push( @$params, $opt . $suff );
307             }
308 6         75 $self->options( $opts );
309 6         6192 $self->parameters( $params );
310 6         19991 $self->{getopt} = Getopt::Long::Parser->new;
311 6         8905 return( $self );
312             }
313              
314             sub check_class_data
315             {
316 1     1 1 7 my $self = shift( @_ );
317 1   50     6 my $class = shift( @_ ) || return( $self->error( "No class was provided to return its definition" ) );
318 1 50       3 return( $self->error( "Class provided '$class' is not a string." ) ) if( ref( $class ) );
319 1         2 my $p = {};
320 1 50 33     5 $p = shift( @_ ) if( scalar( @_ ) && $self->_is_hash( $_[0] ) );
321 1   50     11 my $dict = $self->class( $class ) || return;
322 1   50     5 my $v = $self->get_class_values( $class ) || return;
323 1         5 my $errors =
324             {
325             missing => {},
326             regexp => {},
327             };
328 1         5 foreach my $f ( sort( keys( %$dict ) ) )
329             {
330 4         8 my $def = $dict->{ $f };
331 4 100       10 my $n = $def->{name} ? $def->{name} : $f;
332 4   50     21 $def->{error} ||= "does not match requirements";
333 4 0 33     11 if( !!$p->{required} && $def->{required} )
334             {
335 0 0 0     0 if( ( $def->{type} =~ /^(?:boolean|decimal|integer|string)/ && !length( $v->{ $f } ) ) ||
      0        
      0        
      0        
      0        
      0        
336 0         0 ( ( $def->{type} eq 'hash' || $def->{type} eq 'string-hash' ) && !scalar( keys( %{$v->{ $f }} ) ) ) ||
337 0         0 ( $def->{type} eq 'array' && !scalar( @{$v->{ $f }} ) ) )
338             {
339 0         0 $errors->{missing}->{ $f } = "$f ($n) is missing";
340 0         0 next;
341             }
342             }
343 4 100 33     17 if( $def->{re} )
    50          
344             {
345 1 50 33     17 if( $def->{type} eq 'string' && length( $v->{ $f } ) && $v->{ $f } !~ /$def->{re}/ )
    50 33        
346             {
347 0         0 $errors->{regexp}->{ $f } = "$f ($n) " . $def->{error};
348             }
349             elsif( $def->{type} eq 'array' )
350             {
351 1         2 my $sub_err = [];
352 1         2 foreach my $this ( @{$v->{ $f }} )
  1         4  
353             {
354 1 50       9 if( $this !~ /$def->{re}/ )
355             {
356 1         3 push( @$sub_err, $this );
357             }
358             }
359 1         7 $errors->{regexp}->{ $f } = join( ', ', @$sub_err ) . ' ' . $def->{error};
360             }
361             }
362             elsif( $def->{type} eq 'decimal' && $v->{ $f } !~ /^\d+(\.\d{1,12})?$/ )
363             {
364 0         0 $errors->{regexp}->{ $f } = "$f ($n) " . $def->{error};
365             }
366             }
367 1         6 return( $errors );
368             }
369              
370             sub class
371             {
372 4     4 1 9 my $self = shift( @_ );
373 4   50     14 my $class = shift( @_ ) || return( $self->error( "No class was provided to return its definition" ) );
374 4 50       13 return( $self->error( "Class provided '$class' is not a string." ) ) if( ref( $class ) );
375 4         14 my $classes = $self->classes;
376 4 50       2775 return( $self->error( "I was expecting an hash reference for the classes dictionaries but got '$classes' instead." ) ) if( !ref( $classes ) );
377 4 50 66     27 return( $self->error( "No class \"$class\" was found." ) ) if( scalar( keys( %$classes ) ) && !exists( $classes->{ $class } ) );
378 4         14 my $dict = $self->dictionary;
379 4 50       2638 return( $self->error( "No dictionary data could be found!" ) ) if( !scalar( keys( %$dict ) ) );
380 4         84 foreach my $k ( sort( keys( %$dict ) ) )
381             {
382 88         111 my $def = $dict->{ $k };
383 88 100       152 next if( !exists( $def->{class} ) );
384 20         20 my $class_names = $def->{class};
385 20         32 my $k2 = $k;
386 20         31 $k2 =~ tr/-/_/;
387 20         31 foreach my $class ( @$class_names )
388             {
389             # Create the class if it doe snot exists yet
390 32 100       62 $classes->{ $class } = {} if( !exists( $classes->{ $class } ) );
391 32         41 my $this = $classes->{ $class };
392             # Then add the property and it definition hash
393 32         46 $this->{ $k2 } = $def;
394             # If there are any alias, we add them too
395 32 50 50     73 if( $def->{alias} && scalar( @{$def->{alias}} ) )
  32         75  
396             {
397 32         32 foreach my $f ( @{$def->{alias}} )
  32         50  
398             {
399 32         33 my $f2 = $f;
400 32         44 $f2 =~ tr/-/_/;
401 32         61 $this->{ $f } = $this->{ $f2 } = $def;
402             }
403             }
404             }
405             }
406 4 50       19 return( $self->error( "No class \"$class\" was found." ) ) if( !exists( $classes->{ $class } ) );
407 4         14 return( $classes->{ $class } );
408             }
409              
410 4     4 1 36 sub classes { return( shift->_set_get_hash( 'classes', @_ ) ); }
411              
412             sub class_properties
413             {
414 2     2 1 4446 my $self = shift( @_ );
415 2         6 my $class = shift( @_ );
416 2 50       7 return( $self->error( "No class was provided to list its properties." ) ) if( !length( $class ) );
417 2         4 my $fields = [];
418 2         10 my $ref = $self->class( $class );
419 2         7 my $props = [ sort( keys( %$ref ) ) ];
420 2         26 return( Module::Generic::Array->new( $props ) );
421             }
422              
423             sub configure
424             {
425 6     6 1 16 my $self = shift( @_ );
426 6 50       30 return( $self ) if( $self->{configured} );
427 6         14 my $conf = [];
428 6 50       25 $conf = shift( @_ ) if( ref( $_[0] ) );
429 6 50       39 $conf = $self->configure_options if( !scalar( @$conf ) );
430 6   50     9986 my $getopt = $self->getopt || return( $self->error( "No Getopt::Long::Parser object found." ) );
431 6         244 local $@;
432             # try-catch
433             eval
434 6         16 {
435 6         42 $getopt->configure( @$conf );
436 6         842 $self->{configured} = 1;
437             };
438 6 50       31 if( $@ )
439             {
440 0         0 return( $self->error( "An error occurred while configuration Getlong::Opt: $@" ) );
441             }
442 6         28 return( $self );
443             }
444              
445 6     6 1 28 sub configure_errors { return( shift->_set_get_array_as_object( 'configure_errors', @_ ) ); }
446              
447 6     6 1 30 sub configure_options { return( shift->_set_get_array_as_object( 'configure_options', @_ ) ); }
448              
449 28     28 1 2722 sub dictionary { return( shift->_set_get_hash( 'dictionary', @_ ) ); }
450              
451             sub exec
452             {
453 6     6 1 3441 my $self = shift( @_ );
454 6 50       27 $self->configure || return;
455 6         16 my $errors = [];
456 6         10 my $missing = [];
457 6         25 my $dict = $self->dictionary;
458 6 50       4983 return( $self->error( "The data returned by dictionary() is not an hash reference." ) ) if( !$self->_is_hash( $dict ) );
459 6 50       145 return( $self->error( "Somehow, the dictionary hash is empty!" ) ) if( !scalar( keys( %$dict ) ) );
460 6         25 my $opts = $self->options;
461 6 50       5172 return( $self->error( "The data returned by options() is not an hash reference." ) ) if( !$self->_is_hash( $opts ) );
462 6 50       169 return( $self->error( "Somehow, the options hash is empty!" ) ) if( !scalar( keys( %$opts ) ) );
463 6         24 my $params = $self->parameters;
464 6 50       5455 return( $self->error( "Data returned by parameters() is not an array reference" ) ) if( !$self->_is_array( $params ) );
465 6 50       102 return( $self->error( "Somehow, the parameters array is empty!" ) ) if( !scalar( @$params ) );
466 6   50     25 my $getopt = $self->getopt || return( $self->error( "No Getopt::Long object found." ) );
467 6         192 my $required = $self->required;
468 6 50       9533 return( $self->error( "Data returned by required() is not an array reference" ) ) if( !$self->_is_array( $required ) );
469            
470 6   50     101 my $tie = tied( %$opts ) || return( $self->error( "Unable to get the tie object for the options value hash." ) );
471            
472             local $Getopt::Long::SIG{ '__DIE__' } = sub
473             {
474 0     0   0 push( @$errors, join( '', @_ ) );
475 6         50 };
476             local $Getopt::Long::SIG{ '__WARN__' } = sub
477             {
478 0     0   0 push( @$errors, join( '', @_ ) );
479 6         52 };
480 6         30 $self->configure_errors( $errors );
481            
482 6         15617 $tie->enable(1);
483             $getopt->getoptions( $opts, @$params ) || do
484 6 50       37 {
485 0         0 my $usage = $self->usage;
486 0 0       0 return( $usage->() ) if( ref( $usage ) eq 'CODE' );
487 0         0 return;
488             };
489            
490 6         1321 foreach my $key ( @$required )
491             {
492 1 0 0     5 if( exists( $opts->{ $key } ) &&
      33        
493             ( !defined( $opts->{ $key } ) ||
494             !length( $opts->{ $key } ) ||
495             $opts->{ $key } =~ /^[[:blank:]]*$/ ||
496             ( ref( $opts->{ $key } ) eq 'SCALAR' &&
497             ( !length( ${$opts->{ $key }} ) || ${$opts->{ $key }} =~ /^[[:blank:]]*$/ )
498             ) ||
499             (
500             ref( $opts->{ $key } ) eq 'ARRAY' &&
501             !scalar( @{$opts->{ $key }} )
502             )
503             )
504             )
505             {
506 1         6 push( @$missing, $key );
507             }
508             }
509 6         54 $self->missing( $missing );
510            
511             # Make sure we can access each of the options dictionary definition not just from the original key, but also from any of it aliases
512 6         26926 my $aliases = $self->{aliases};
513 6         59 foreach my $k ( keys( %$dict ) )
514             {
515 132         266 my $def = $dict->{ $k };
516 132         218 $aliases->{ $k } = $def;
517 132         163 foreach my $a ( @{$def->{alias}} )
  132         301  
518             {
519 210         444 $aliases->{ $a } = $def;
520             }
521             }
522 6         43 $tie->enable(1);
523            
524 6         47 $self->postprocess;
525            
526             # return( $opts );
527             # e return a Getopt::Class::Values object, so we can call the option values hash key as method:
528             # $object->metadata / $object->metadata( $some_hash );
529             # instead of:
530             # $object->{metadata}
531             # return( $opts );
532             my $o = Getopt::Class::Values->new({
533             data => $opts,
534             dict => $dict,
535             aliases => $aliases,
536             debug => $self->{debug},
537 6   50     93 }) || return( $self->pass_error( Getopt::Class::Values->error ) );
538 6         111 return( $o );
539             }
540              
541             sub get_class_values
542             {
543 1     1 1 3 my $self = shift( @_ );
544 1   50     4 my $class = shift( @_ ) || return( $self->error( "No class was provided to return its definition" ) );
545 1 50       3 return( $self->error( "Class provided '$class' is not a string." ) ) if( ref( $class ) );
546 1   50     4 my $this_dict = $self->class( $class ) || return;
547 1         5 my $opts = $self->options;
548 1 50       810 return( $self->error( "The data returned by options() is not an hash reference." ) ) if( !$self->_is_hash( $opts ) );
549 1 50       24 return( $self->error( "Somehow, the options hash is empty!" ) ) if( !scalar( keys( %$opts ) ) );
550 1         4 my $v = {};
551 1 50 33     18 $v = shift( @_ ) if( scalar( @_ ) && $self->_is_hash( $_[0] ) );
552 1         10 foreach my $f ( sort( keys( %$this_dict ) ) )
553             {
554 4   100     16 my $ref = lc( Scalar::Util::reftype( $opts->{ $f } ) // '' );
555 4 50       28 if( $ref eq 'hash' )
    100          
    50          
556             {
557 0 0       0 $v->{ $f } = $opts->{ $f } if( scalar( keys( %{$opts->{ $f }} ) ) > 0 );
  0         0  
558             }
559             elsif( $ref eq 'array' )
560             {
561 1 50       2 $v->{ $f } = $opts->{ $f } if( scalar( @{$opts->{ $f }} ) > 0 );
  1         4  
562             }
563             elsif( !length( $ref ) )
564             {
565 3 50       10 $v->{ $f } = $opts->{ $f } if( length( $opts->{ $f } ) );
566             }
567             }
568 1         5 return( $v );
569             }
570              
571 12     12 1 106 sub getopt { return( shift->_set_get_object( 'getopt', 'Getopt::Long::Parser', @_ ) ); }
572              
573 7     7 1 48 sub missing { return( shift->_set_get_array_as_object( 'missing', @_ ) ); }
574              
575 19     19 1 87 sub options { return( shift->_set_get_hash( 'options', @_ ) ); }
576              
577 12     12 1 192 sub parameters { return( shift->_set_get_array_as_object( 'parameters', @_ ) ); }
578              
579             sub postprocess
580             {
581 6     6 0 17 my $self = shift( @_ );
582 6         25 my $dict = $self->dictionary;
583 6         5054 my $opts = $self->options;
584 6         5264 foreach my $k ( sort( keys( %$dict ) ) )
585             {
586 132         317 my $def = $dict->{ $k };
587 132 50 66     610 next if( !length( $opts->{ $k } ) && !$def->{default} );
588 107 50       345 return( $self->error( "Dictionary is malformed with entry $k value not being an hash reference." ) ) if( ref( $def ) ne 'HASH' );
589            
590 107 100 66     1448 if( ( $def->{type} eq 'date' || $def->{type} eq 'datetime' ) && length( $opts->{ $k } ) )
    100 66        
    100 66        
    100 66        
    100 66        
    100 0        
    50 33        
    50          
    50          
    50          
591             {
592 2         33 my $dt = $self->_parse_timestamp( $opts->{ $k } );
593 2 50       802164 return( $self->pass_error ) if( !defined( $dt ) );
594 2 50       11 $opts->{ $k } = $dt if( $dt );
595             }
596             elsif( $def->{type} eq 'array' )
597             {
598 6         118 $opts->{ $k } = Module::Generic::Array->new( $opts->{ $k } );
599             }
600             elsif( $def->{type} eq 'hash' ||
601             $def->{type} eq 'string-hash' )
602             {
603 6         111 $opts->{ $k } = $self->_set_get_hash_as_object( $k, $opts->{ $k } );
604             }
605             elsif( $def->{type} eq 'boolean' )
606             {
607 54 100 100     247 if( exists( $def->{mirror} ) &&
608             exists( $def->{mirror}->{value} ) )
609             {
610 3         7 $opts->{ $k } = $def->{mirror}->{value};
611             }
612 54 100       224 $opts->{ $k } = ( $opts->{ $k } ? $self->true : $self->false );
613             }
614             elsif( $def->{type} eq 'string' || $def->{type} eq 'scalar' )
615             {
616 14         169 $opts->{ $k } = Module::Generic::Scalar->new( $opts->{ $k } );
617             }
618             elsif( $def->{type} eq 'integer' || $def->{decimal} )
619             {
620             # Even though this is a number, this was set as a scalar reference, so we need to dereference it
621 7 100       97 if( $self->_is_scalar( $opts->{ $k } ) )
622             {
623 6         96 $opts->{ $k } = Module::Generic::Scalar->new( $opts->{ $k } );
624             }
625             else
626             {
627 1         36 $opts->{ $k } = $self->_set_get_number( $k, $opts->{ $k } );
628             }
629             }
630             elsif( $def->{type} eq 'file' && length( $opts->{ $k } // '' ) )
631             {
632 0         0 $opts->{ $k } = file( $opts->{ $k } );
633             }
634             elsif( $def->{type} eq 'file-array' )
635             {
636 0         0 my $arr = Module::Generic::Array->new;
637 0         0 foreach( @{$opts->{ $k }} )
  0         0  
638             {
639 0         0 push( @$arr, file( $_ ) );
640             }
641 0         0 $opts->{ $k } = $arr;
642             }
643             elsif( $def->{type} eq 'uri' )
644             {
645 0 0       0 my $uri_class = exists( $def->{package} ) ? $def->{package} : 'URI';
646 0         0 $opts->{ $k } = $uri_class->new( $opts->{ $k } );
647             }
648             elsif( $def->{type} eq 'uri-array' )
649             {
650 0 0       0 my $uri_class = exists( $def->{package} ) ? $def->{package} : 'URI';
651 0         0 my $arr = Module::Generic::Array->new;
652 0         0 foreach( @{$opts->{ $k }} )
  0         0  
653             {
654 0         0 push( @$arr, $uri_class->new( $_ ) );
655             }
656 0         0 $opts->{ $k } = $arr;
657             }
658             }
659 6         45 return( $self );
660             }
661              
662 7     7 1 44 sub required { return( shift->_set_get_array_as_object( 'required', @_ ) ); }
663              
664 0     0 1 0 sub usage { return( shift->_set_get_code( 'usage', @_ ) ); }
665              
666             # NOTE: Getopt::Class::Values package
667             package Getopt::Class::Values;
668             BEGIN
669 0         0 {
670 6     6   80 use strict;
  6         8  
  6         232  
671 6     6   30 use warnings;
  6         10  
  6         398  
672 6     6   33 use parent qw( Module::Generic );
  6     0   10  
  6         37  
673             };
674              
675 6     6   604 use strict;
  6         38  
  6         310  
676 6     6   37 use warnings;
  6         9  
  6         3901  
677              
678             sub new
679             {
680 6     6   19 my $that = shift( @_ );
681 6         21 my %hash = ();
682 6         53 my $obj = tie( %hash, 'Getopt::Class::Repository' );
683 6   33     62 my $self = bless( \%hash => ( ref( $that ) || $that ) )->init( @_ );
684 6         52 $obj->enable( 1 );
685 6         25 return( $self );
686             }
687              
688 9     9   2492 sub debug { return( shift->_set_get_number( 'debug', @_ ) ); }
689              
690             sub init
691             {
692 6     6   16 my $self = shift( @_ );
693 6   33     61 my $class = ref( $self ) || $self;
694 6         418 $self->{data} = {};
695 6         38 $self->{dict} = {};
696 6         27 $self->{aliases} = {};
697             # Can only set properties that exist
698 6         26 $self->{_init_strict} = 1;
699 6 50       91 $self->SUPER::init( @_ ) || return( $self->pass_error( $self->error ) );
700 6 50       72 return( $self->error( "No dictionary as provided." ) ) if( !$self->{dict} );
701 6 50       25 return( $self->error( "No dictionary as provided." ) ) if( !$self->{aliases} );
702 6 50       73 return( $self->error( "Dictionary provided is not an hash reference." ) ) if( !$self->_is_hash( $self->{dict} ) );
703 6 50       108 return( $self->error( "Aliases provided is not an hash reference." ) ) if( !$self->_is_hash( $self->{aliases} ) );
704 6 50       94 scalar( keys( %{$self->{dict}} ) ) || return( $self->error( "No dictionary data was provided." ) );
  6         23  
705 6 50       183 return( $self->error( "Data provided is not an hash reference." ) ) if( !$self->_is_hash( $self->{data} ) );
706 6         65 my $call_offset = 0;
707 6         61 while( my @call_data = caller( $call_offset ) )
708             {
709 12 50 66     95 unless( $call_offset > 0 && $call_data[0] ne $class && (caller($call_offset-1))[0] eq $class )
      66        
710             {
711 6         13 $call_offset++;
712 6         45 next;
713             }
714 6 50 0     99 last if( $call_data[9] || ( $call_offset > 0 && (caller($call_offset-1))[0] ne $class ) );
      33        
715 0         0 $call_offset++;
716             }
717 6         36 my $bitmask = ( caller( $call_offset ) )[9];
718 6         24 my $offset = $warnings::Offsets{uninitialized};
719 6         17 my $should_display_warning = vec( $bitmask, $offset, 1 );
720 6         29 $self->{warnings} = $should_display_warning;
721 6         21 return( $self );
722             }
723              
724 0     0   0 sub verbose { return( shift->_set_get_number( 'verbose', @_ ) ); }
725              
726             # NOTE: AUTOLOAD
727             AUTOLOAD
728             {
729 10     10   98845 my( $method ) = our $AUTOLOAD =~ /([^:]+)$/;
730 6     6   67 no overloading;
  6         53  
  6         5217  
731 10         29 my $self = shift( @_ );
732 10   33     37 my $class = ref( $self ) || $self;
733             # Options dictionary
734 10         58 my $dict = $self->{dict};
735             # Values provided on command line
736 10         33 my $data = $self->{data};
737             # printf( STDERR "AUTOLOAD: \$data has %d items and property '$method' has value '%s'\n", scalar( keys( %$self ) ), $self->{ $method } );
738             # return if( !CORE::exists( $data->{ $method } ) );
739 10 50       38 return if( !CORE::exists( $self->{ $method } ) );
740 10         28 my $f = $method;
741             # Dictionary definition for this particular option field
742 10         20 my $def = $dict->{ $f };
743 10 50 33     38 if( !exists( $def->{type} ) ||
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
744             !defined( $def->{type} ) )
745             {
746 10 50       51 CORE::warn( "Property \"${f}\" has no defined type. Using scalar.\n" ) if( $self->{warnings} );
747 10         96 return( $self->_set_get_scalar( $f, @_ ) );
748             }
749             elsif( $def->{type} eq 'boolean' || ( $self->_is_object( $self->{ $f } ) && $self->{ $f }->isa( 'Module::Generic::Boolean' ) ) )
750             {
751 0         0 return( $self->_set_get_boolean( $f, @_ ) );
752             }
753             elsif( $def->{type} eq 'string' ||
754             $def->{type} eq 'scalar' ||
755             Scalar::Util::reftype( $self->{ $f } ) eq 'SCALAR' )
756             {
757 0         0 return( $self->_set_get_scalar_as_object( $f, @_ ) );
758             }
759             elsif( $def->{type} eq 'integer' ||
760             $def->{type} eq 'decimal' )
761             {
762 0         0 return( $self->_set_get_number( $f, @_ ) );
763             }
764             elsif( $def->{type} eq 'date' ||
765             $def->{type} eq 'datetime' )
766             {
767 0         0 return( $self->_set_get_datetime( $f, @_ ) );
768             }
769             elsif( $def->{type} eq 'array' )
770             {
771 0         0 return( $self->_set_get_array_as_object( $f, @_ ) );
772             }
773             elsif( $def->{type} eq 'hash' ||
774             $def->{type} eq 'string-hash' )
775             {
776 0         0 return( $self->_set_get_hash_as_object( $f, @_ ) );
777             }
778             elsif( $def->{type} eq 'code' )
779             {
780 0         0 return( $self->_set_get_code( $f, @_ ) );
781             }
782             elsif( $def->{type} eq 'file' )
783             {
784 0         0 return( $self->_set_get_file( $f, @_ ) );
785             }
786             elsif( $def->{type} eq 'file-array' )
787             {
788 0 0       0 if( @_ )
789             {
790 0         0 my $arr = Module::Generic::Array->new;
791 0         0 foreach( @_ )
792             {
793 0         0 push( @$arr, file( $_ ) );
794             }
795 0         0 $self->{ $f } = $arr;
796             }
797 0         0 return( $self->_set_get_array_as_object( $f ) );
798             }
799             elsif( $def->{type} eq 'uri' )
800             {
801 0 0       0 my $uri_class = exists( $def->{package} ) ? $def->{package} : 'URI';
802 0         0 return( $self->_set_get_uri( { field => $f, class => $uri_class }, @_ ) );
803             }
804             elsif( $def->{type} eq 'uri-array' )
805             {
806 0 0       0 my $uri_class = exists( $def->{package} ) ? $def->{package} : 'URI';
807 0 0       0 if( @_ )
808             {
809 0         0 my $arr = Module::Generic::Array->new;
810 0         0 foreach( @_ )
811             {
812 0         0 push( @$arr, $uri_class->new( $_ ) );
813             }
814 0         0 $self->{ $f } = $arr;
815             }
816 0         0 return( $self->_set_get_uri( { field => $f, class => $uri_class } ) );
817             }
818             elsif( $def->{type} eq 'uri' )
819             {
820 0         0 return( $self->_set_get_uri( $f, @_ ) );
821             }
822             elsif( $def->{type} eq 'uri-array' )
823             {
824 0 0       0 if( @_ )
825             {
826 0         0 my $arr = Module::Generic::Array->new;
827 0         0 foreach( @_ )
828             {
829 0         0 push( @$arr, file( $_ ) );
830             }
831 0         0 $self->{ $f } = $arr;
832             }
833 0         0 return( $self->_set_get_array_as_object( $f ) );
834             }
835             else
836             {
837 0 0       0 CORE::warn( "I do not know what to do with this property \"$f\" type \"$def->{type}\". Using scalar.\n" ) if( $self->{warnings} );
838 0         0 return( $self->_set_get_scalar( $f, @_ ) );
839             }
840             };
841              
842             # NOTE: Getopt::Class::Repository package
843             package Getopt::Class::Repository;
844             BEGIN
845 0         0 {
846 6     6   47 use strict;
  6         12  
  6         329  
847 6     6   35 use warnings;
  6         10  
  6         316  
848 6     6   40 use Scalar::Util;
  6         10  
  6         327  
849 6     6   34 use constant VALUES_CLASS => 'Getopt::Class::Value';
  6     0   9  
  6         5399  
850             };
851              
852             # tie( %self, 'Getopt::Class::Repository' );
853             # Used by Getopt::Class::Values to ensure that whether the data are accessed as methods or as hash keys,
854             # in either way it returns the option data
855             # Actually option data are stored in the Getopt::Class::Values object data property
856             sub TIEHASH
857             {
858 6     6   19 my $self = shift( @_ );
859 6   33     68 my $class = ref( $self ) || $self;
860 6         37 return( bless( { data => {} } => $class ) );
861             }
862              
863             sub CLEAR
864             {
865 0     0   0 my $self = shift( @_ );
866 0         0 my $data = $self->{data};
867 0         0 my $caller = caller;
868 0         0 %$data = ();
869             }
870              
871             sub DELETE
872             {
873 0     0   0 my $self = shift( @_ );
874 0         0 my $data = $self->{data};
875 0         0 my $key = shift( @_ );
876 0 0 0     0 if( caller eq VALUES_CLASS || !$self->{enable} )
877             {
878 0         0 CORE::delete( $self->{ $key } );
879             }
880             else
881             {
882 0         0 CORE::delete( $data->{ $key } );
883             }
884             }
885              
886             sub EXISTS
887             {
888 1377     1377   33099 my $self = shift( @_ );
889 1377         2154 my $data = $self->{data};
890 1377         2065 my $key = shift( @_ );
891 1377 100 66     5171 if( caller eq VALUES_CLASS || !$self->{enable} )
892             {
893 78         214 CORE::exists( $self->{ $key } );
894             }
895             else
896             {
897 1299         4682 CORE::exists( $data->{ $key } );
898             }
899             }
900              
901             sub FETCH
902             {
903 1375     1375   44645 my $self = shift( @_ );
904 1375         2093 my $data = $self->{data};
905 1375         1863 my $key = shift( @_ );
906 1375         2205 my $caller = caller;
907             # print( STDERR "FETCH($caller)[enable=$self->{enable}] <- '$key''\n" );
908 1375 100 66     4734 if( caller eq VALUES_CLASS || !$self->{enable} )
909             {
910             # print( STDERR "FETCH($caller)[enable=$self->{enable}] <- '$key' <- '$self->{$key}'\n" );
911 514         1464 return( $self->{ $key } )
912             }
913             else
914             {
915             # print( STDERR "FETCH($caller)[enable=$self->{enable}] <- '$key' <- '$self->{$key}'\n" );
916 861         2738 return( $data->{ $key } );
917             }
918             }
919              
920             sub FIRSTKEY
921             {
922 0     0   0 my $self = shift( @_ );
923 0         0 my $data = $self->{data};
924 0         0 my @keys = ();
925 0 0 0     0 if( caller eq VALUES_CLASS || !$self->{enable} )
926             {
927 0         0 @keys = keys( %$self );
928             }
929             else
930             {
931 0         0 @keys = keys( %$data );
932             }
933 0         0 $self->{ITERATOR} = \@keys;
934 0         0 return( shift( @keys ) );
935             }
936              
937             sub NEXTKEY
938             {
939 0     0   0 my $self = shift( @_ );
940 0         0 my $data = $self->{data};
941 0 0       0 my $keys = ref( $self->{ITERATOR} ) ? $self->{ITERATOR} : [];
942 0         0 return( shift( @$keys ) );
943             }
944              
945             sub SCALAR
946             {
947 7     7   18 my $self = shift( @_ );
948 7         18 my $data = $self->{data};
949 7 100 66     72 if( caller eq VALUES_CLASS || !$self->{enable} )
950             {
951 6         34 return( scalar( keys( %$self ) ) );
952             }
953             else
954             {
955 1         5 return( scalar( keys( %$data ) ) );
956             }
957             }
958              
959             sub STORE
960             {
961 126     126   255743 my $self = shift( @_ );
962 126         202 my $data = $self->{data};
963 126         243 my( $key, $val ) = @_;
964             # print( STDERR "STORE($caller)[enable=$self->{enable}] -> '$key'\n" );
965 126 50 33     496 if( caller eq VALUES_CLASS || !$self->{enable} )
966             {
967             # print( STDERR "STORE($caller)[enable=$self->{enable}] -> '$key' -> '$val'\n" );
968 126         548 $self->{ $key } = $val;
969             }
970             else
971             {
972             # print( STDERR "STORE($caller)[enable=$self->{enable}] -> '$key' -> '$val'\n" );
973 0         0 $data->{ $key } = $val;
974             }
975             }
976              
977             sub enable
978             {
979 18     18   39 my $self = shift( @_ );
980 18 50       80 if( @_ )
981             {
982 18         83 $self->{enable} = shift( @_ );
983             }
984 18         38 return( $self->{enable} );
985             }
986              
987             # NOTE: Getopt::Class::Alias package
988             # This is an alternative to perl feature of refealiasing
989             # https://metacpan.org/pod/perlref#Assigning-to-References
990             package Getopt::Class::Alias;
991             BEGIN
992 0         0 {
993 6     6   47 use strict;
  6         11  
  6         227  
994 6     6   46 use warnings;
  6         35  
  6         424  
995 6     6   33 use parent -norequire, qw( Getopt::Class::Repository Module::Generic );
  6         17  
  6         34  
996 6     6   490 use Scalar::Util;
  6     0   14  
  6         6029  
997             };
998              
999             # tie( %$opts, 'Getopt::Class::Alias', $dictionary );
1000             sub TIEHASH
1001             {
1002             # $this is actually the HASH tied
1003 6     6   19 my $this = shift( @_ );
1004 6   33     65 my $class = ref( $this ) || $this;
1005             # Valid options are:
1006             # dict: options dictionary
1007             # debug
1008 6         18 my $opts = {};
1009 6 50       30 $opts = shift( @_ ) if( @_ );
1010             # print( STDERR __PACKAGE__ . "::TIEHASH() called with following arguments: '", join( ', ', @_ ), "'.\n" );
1011 6         15 my $call_offset = 0;
1012 6         63 while( my @call_data = caller( $call_offset ) )
1013             {
1014             # printf( STDERR "[$call_offset] In file $call_data[1] at line $call_data[2] from subroutine %s has bitmask $call_data[9]\n", (caller($call_offset+1))[3] );
1015 18 50 66     175 unless( $call_offset > 0 && $call_data[0] ne $class && (caller($call_offset-1))[0] eq $class )
      66        
1016             {
1017             # print( STDERR "Skipping package $call_data[0]\n" );
1018 18         29 $call_offset++;
1019 18         173 next;
1020             }
1021 0 0 0     0 last if( $call_data[9] || ( $call_offset > 0 && (caller($call_offset-1))[0] ne $class ) );
      0        
1022 0         0 $call_offset++;
1023             }
1024             # print( STDERR "Using offset $call_offset with bitmask ", ( caller( $call_offset ) )[9], "\n" );
1025 6         27 my $bitmask = ( caller( $call_offset - 1 ) )[9];
1026 6         27 my $offset = $warnings::Offsets{uninitialized};
1027             # print( STDERR "Caller (2)'s bitmask is '$bitmask', warnings offset is '$offset' and vector is '", vec( $bitmask, $offset, 1 ), "'.\n" );
1028 6   50     49 my $should_display_warning = vec( ( $bitmask // 0 ), $offset, 1 );
1029            
1030 6   50     26 my $dict = $opts->{dict} || return( __PACKAGE__->error( "No dictionary was provided to Getopt::Class:Alias" ) );
1031 6 50       47 if( Scalar::Util::reftype( $dict ) ne 'HASH' )
    50          
1032             {
1033             #warn( "Dictionary provided is not an hash reference.\n" ) if( $should_display_warning );
1034             #return;
1035 0         0 return( __PACKAGE__->error({ message => "Dictionary provided is not an hash reference.", no_return_null_object => 1 }) );
1036             }
1037             elsif( !scalar( keys( %$dict ) ) )
1038             {
1039             #warn( "The dictionary hash reference provided is empty.\n" ) if( $should_display_warning );
1040             #return;
1041 0         0 return( __PACKAGE__->error( "The dictionary hash reference provided is empty." ) );
1042             }
1043            
1044             my $aliases = $opts->{aliases} || do
1045 6   33     34 {
1046             #warn( "No aliases map was provided to Getopt::Class:Alias\n" ) if( $should_display_warning );
1047             #return;
1048             return( __PACKAGE__->error( "No aliases map was provided to Getopt::Class:Alias" ) );
1049             };
1050 6 50       27 if( Scalar::Util::reftype( $aliases ) ne 'HASH' )
1051             {
1052             #warn( "Aliases map provided is not an hash reference.\n" ) if( $should_display_warning );
1053             #return;
1054 0         0 return( __PACKAGE__->error( "Aliases map provided is not an hash reference." ) );
1055             }
1056             my $hash =
1057             {
1058             data => {},
1059             dict => $dict,
1060             aliases => $aliases,
1061             warnings => $should_display_warning,
1062 6   50     127 debug => ( $opts->{debug} || 0 ),
1063             # _data_repo => 'data',
1064             colour_open => '<',
1065             colour_close => '>',
1066             };
1067 6         480 return( bless( $hash => $class ) );
1068             }
1069              
1070             sub FETCH
1071             {
1072 1312     1312   24484 my $self = shift( @_ );
1073 1312         2032 my $data = $self->{data};
1074             # my $dict = $self->{dict};
1075 1312         1904 my $key = shift( @_ );
1076             # my $def = $dict->{ $key };
1077 1312         6598 return( $data->{ $key } );
1078             }
1079              
1080             sub STORE
1081             {
1082 252     252   120229 my $self = shift( @_ );
1083 252         460 my $class = ref( $self );
1084 252         427 my $data = $self->{data};
1085             # Aliases contains both the original dictionary key and all its aliases
1086 252         463 my $aliases = $self->{aliases};
1087 252         787 my( $pack, $file, $line ) = caller;
1088 252         578 my( $key, $val ) = @_;
1089 252         448 my $dict = $self->{dict};
1090 252         435 my $enabled = $self->{enable};
1091             my $fallback = sub
1092             {
1093 0     0   0 my( $k, $v ) = @_;
1094 0         0 $data->{ $k } = $v;
1095 252         1268 };
1096 252 100 100     925 if( $enabled && CORE::exists( $aliases->{ $key } ) )
1097             {
1098             my $def = $aliases->{ $key } || do
1099 92   33     270 {
1100             CORE::warn( "No dictionary definition found for \"$key\".\n" ) if( $self->{warnings} );
1101             return( $fallback->( $key, $val ) );
1102             };
1103 92 50       433 if( !$self->_is_array( $def->{alias} ) )
1104             {
1105 0 0       0 CORE::warn( "I was expecting an array reference for this alias, but instead got '$def->{alias}'.\n" ) if( $self->{warnings} );
1106 0         0 return( $fallback->( $key, $val ) );
1107             }
1108             my $alias = $def->{alias} || do
1109 92   33     1277 {
1110             CORE::warn( "No alias property found. This should not happen.\n" ) if( $self->{warnings} );
1111             return( $fallback->( $key, $val ) );
1112             };
1113             # $self->messagef_colour( 3, 'Found alias "{green}' . $alias . '{/}" with %d elements: {green}"%s"{/}', scalar( @$alias ), $alias->join( "', '" ) );
1114 92         774 $self->messagef_colour( 3, "Found alias '<green>$alias</>' with %d elements: <green>'%s'</>", scalar( @$alias ), $alias->join( "', '" ) );
1115 92 50       23992 if( Scalar::Util::reftype( $alias ) ne 'ARRAY' )
1116             {
1117 0 0       0 CORE::warn( "Alias property is not an array reference. This should not happen.\n" ) if( $self->{warnings} );
1118 0         0 return( $fallback->( $key, $val ) );
1119             }
1120 92         14364 $data->{ $key } = $val;
1121 92         292 foreach my $a ( @$alias )
1122             {
1123 161 100       1246 next if( $a eq $key );
1124             # We do not set the value, if for some reason, the user would have removed this key
1125             # $data->{ $a } = $val if( CORE::exists( $data->{ $a } ) );
1126 69         190 $data->{ $a } = $val;
1127             }
1128             }
1129             else
1130             {
1131 160         1040 $data->{ $key } = $val;
1132             }
1133             }
1134              
1135             1;
1136             # NOTE: POD
1137             __END__
1138              
1139             =encoding utf-8
1140              
1141             =head1 NAME
1142              
1143             Getopt::Class - Extended dictionary version of Getopt::Long
1144              
1145             =head1 SYNOPSIS
1146              
1147             use Getopt::Class;
1148             our $DEBUG = 0;
1149             our $VERBOSE = 0;
1150             our $VERSION = '0.1';
1151             my $dict =
1152             {
1153             create_user => { type => 'boolean', alias => [qw(create_person create_customer)], action => 1 },
1154             create_product => { type => 'boolean', action => 1 },
1155             debug => { type => 'integer', default => \$DEBUG },
1156             # Can be enabled with --enable-recurse
1157             disable_recurse => { type => 'boolean', default => 1 },
1158             # Can be disabled also with --disable-logging
1159             enable_logging => { type => 'boolean', default => 0 },
1160             help => { type => 'code', code => sub{ pod2usage(1); }, alias => '?', action => 1 },
1161             man => { type => 'code', code => sub{ pod2usage( -exitstatus => 0, -verbose => 2 ); }, action => 1 },
1162             quiet => { type => 'boolean', default => 0, alias => 'silent' },
1163             verbose => { type => 'boolean', default => \$VERBOSE, alias => 'v' },
1164             version => { type => 'code', code => sub{ printf( "v%.2f\n", $VERSION ); }, action => 1 },
1165            
1166             api_server => { type => 'string', default => 'api.example.com' },
1167             api_version => { type => 'string', default => 1 },
1168             as_admin => { type => 'boolean' },
1169             dry_run => { type => 'boolean', default => 0 },
1170            
1171             # Can be enabled also with --with-zlib
1172             without_zlib => { type => 'integer', default => 1 },
1173            
1174             name => { type => 'string', class => [qw( person product )] },
1175             created => { type => 'datetime', class => [qw( person product )] },
1176             define => { type => 'string-hash', default => {} },
1177             langs => { type => 'array', class => [qw( person product )], re => qr/^[a-z]{2}([_|-][A-Z]{2})?/, min => 1, default => [qw(en)] },
1178             currency => { type => 'string', class => [qw(product)], name => 'currency', re => qr/^[a-z]{3}$/, error => "must be a three-letter iso 4217 value" },
1179             age => { type => 'integer', class => [qw(person)], name => 'age', },
1180             path => { type => 'file' },
1181             skip => { type => 'file-array' },
1182             url => { type => 'uri', package => 'URI' },
1183             urls => { type => 'uri-array', package => 'URI::Fast' },
1184             };
1185            
1186             # Assuming command line arguments like:
1187             prog.pl --create-user --name Bob --langs fr ja --age 30 --created now --debug 3 \
1188             --path ./here/some/where --skip ./bad/directory ./not/here ./avoid/me/
1189              
1190             my $opt = Getopt::Class->new({
1191             dictionary => $dict,
1192             }) || die( Getopt::Class->error, "\n" );
1193             my $opts = $opt->exec || die( $opt->error, "\n" );
1194             $opt->required( [qw( name langs )] );
1195             my $err = $opt->check_class_data( 'person' );
1196             printf( "User is %s and is %d years old\n", $opts{qw( name age )} ) if( $opts->{debug} );
1197              
1198             # Get all the properties for class person
1199             my $props = $opt->class_properties( 'person' );
1200              
1201             # Get values collected for class 'person'
1202             if( $opts->{create_user} )
1203             {
1204             my $values = $opt->get_class_values( 'person' );
1205             # Having collected the values for our class of properties, and making sure all
1206             # required are here, we can add them to database or make api calls, etc
1207             }
1208             elsif( $opts->{create_product} )
1209             {
1210             # etc...
1211             }
1212            
1213             # Or you can also access those values as object methods
1214             if( $opts->create_product )
1215             {
1216             $opts->langs->push( 'en_GB' ) if( !$opts->langs->length );
1217             printf( "Created on %s\n", $opts->created->iso8601 );
1218             }
1219              
1220             =head1 VERSION
1221              
1222             v1.1.4
1223              
1224             =head1 DESCRIPTION
1225              
1226             L<Getopt::Class> is a lightweight wrapper around L<Getopt::Long> that implements the idea of class of properties and makes it easier and powerful to set up L<Getopt::Long>. This module is particularly useful if you want to provide several sets of options for different features or functions of your program. For example, you may have a part of your program that deals with user while another deals with product. Each of them needs their own properties to be provided.
1227              
1228             =head1 CONSTRUCTOR
1229              
1230             =head2 new
1231              
1232             To instantiate a new L<Getopt::Class> object, pass an hash reference of following parameters:
1233              
1234             =over 4
1235              
1236             =item * C<dictionary>
1237              
1238             This is required. It must contain a key value pair where the value is an anonymous hash reference that can contain the following parameters:
1239              
1240             =over 8
1241              
1242             =item * C<alias>
1243              
1244             This is an array reference of alternative options that can be used in an interchangeable way
1245              
1246             my $dict =
1247             {
1248             last_name => { type => 'string', alias => [qw( family_name surname )] },
1249             };
1250             # would make it possible to use either of the following combinations
1251             --last-name Doe
1252             # or
1253             --surname Doe
1254             # or
1255             --family-name Doe
1256              
1257             =item * C<default>
1258              
1259             This contains the default value. For a string, this could be anything, and also a reference to a scalar, such as:
1260              
1261             our $DEBUG = 0;
1262             my $dict =
1263             {
1264             debug => { type => 'integer', default => \$DEBUG },
1265             };
1266              
1267             It can also be used to provide default value for an array, such as:
1268              
1269             my $dict =
1270             {
1271             langs => { type => 'array', class => [qw( person product )], re => qr/^[a-z]{2}([_|-][A-Z]{2})?/, min => 1, default => [qw(en)] },
1272             };
1273              
1274             But beware that if you provide a value, it will not superseed the existing default value, but add it on top of it, so
1275              
1276             --langs en fr ja
1277              
1278             would not produce an array with C<en>, C<fr> and C<ja> entries, but an array such as:
1279              
1280             ['en', 'en', 'fr', 'ja' ]
1281              
1282             because the initial default value is not replaced when one is provided. This is a design from L<Getopt::Long> and although I could circumvent this, I ma not sure I should.
1283              
1284             =item * C<error>
1285              
1286             A string to be used to set an error by L</"check_class_data">. Typically the string should provide meaningful information as to what the data should normally be. For example:
1287              
1288             my $dict =
1289             {
1290             currency => { type => 'string', class => [qw(product)], name => 'currency', re => qr/^[a-z]{3}$/, error => "must be a three-letter iso 4217 value" },
1291             };
1292              
1293             =item * C<max>
1294              
1295             This is well explained in L<Getopt::Long/"Options with multiple values">
1296              
1297             It serves "to specify the minimal and maximal number of arguments an option takes".
1298              
1299             =item * C<min>
1300              
1301             Same as above
1302              
1303             =item * C<re>
1304              
1305             This must be a regular expression and is used by L</"check_class_data"> to check the sanity of the data provided by the user.
1306             So, for example:
1307              
1308             my $dict =
1309             {
1310             currency => { type => 'string', class => [qw(product)], name => 'currency', re => qr/^[a-z]{3}$/, error => "must be a three-letter iso 4217 value" },
1311             };
1312              
1313             then the user calls your program with, among other options:
1314              
1315             --currency euro
1316              
1317             would set an error that can be retrieved as an output of L</"check_class_data">
1318              
1319             =item * C<required>
1320              
1321             Set this to true or false (1 or 0) to instruct L</"check_class_data"> whether to check if it is missing or not.
1322              
1323             This is an alternative to the L</"required"> method which is used at an earlier stage, during L</"exec">
1324              
1325             =item * C<type>
1326              
1327             Supported types are:
1328              
1329             =over 12
1330              
1331             =item * C<array>
1332              
1333             This type will set the resulting value to be a L<Module::Generic::Array> object of values provided.
1334              
1335             =item * C<boolean>
1336              
1337             If type is C<boolean> and the key is either C<with>, C<without>, C<enable>, C<disable>, their counterpart will automatically be available as well, such as you can do, as show in the excerpt in the synopsis above:
1338              
1339             --enable-recurse --with-zlib
1340              
1341             Be careful though. If, in your dictionary, as shown in the synopsis, you defined C<without_zlib> with a default value of true, then using the option C<--with-zlib> will set that value to false. So in your application, you would need to check like this:
1342              
1343             if( $opts->{without_zlib} )
1344             {
1345             # Do something
1346             }
1347             else
1348             {
1349             # Do something else
1350             }
1351              
1352             =item * C<code>
1353              
1354             Type code implies an anonymous sub routine and should be accompanied with the attribute I<code>, such as:
1355              
1356             { type => 'code', code => sub{ pod2usage(1); exit( 0 ) }, alias => '?', action => 1 },
1357              
1358             =item * C<datetime>
1359              
1360             This type will set the resulting value to be a L<DateTime> object of the value provided.
1361              
1362             =item * C<decimal>
1363              
1364             This type will set the resulting value to be a L<Module::Generic::Number> object of the value provided.
1365              
1366             =item * C<file>
1367              
1368             This type will mark the value as a directory or file path and will become a L<Module::Generic::File> object.
1369              
1370             This is particularly convenient when the user provided you with a relative path, such as:
1371              
1372             ./my_prog.pl --debug 3 --path ./here/
1373              
1374             And if you are not very careful and inadvertently change directory like when using L<File::Find>, then this relative path could lead to some unpleasant surprise.
1375              
1376             Setting this argument type to C<file> ensure the resulting value is a L<Module::Generic::File>, whose underlying file or directory will be resolved to their absolute path.
1377              
1378             =item * C<file-array>
1379              
1380             Same as C<file> argument type, but allows multiple value saved as an array. For example:
1381              
1382             ./my_prog.pl --skip ./not/here ./avoid/me/ ./skip/this/directory
1383              
1384             This would result in the option property C<skip> being an L<array object|Module::Generic::Array> containing 3 entries.
1385              
1386             =item * C<hash>
1387              
1388             Type C<hash> is convenient for free key-value pair such as:
1389              
1390             --define customer_id=10 --define transaction_id 123
1391              
1392             would result for C<define> with an anonymous hash as value containing C<customer_id> with value C<10> and C<transaction_id> with value C<123>
1393              
1394             =item * C<integer>
1395              
1396             This type will set the resulting value to be a L<Module::Generic::Number> object of the value provided.
1397              
1398             =item * C<scalar>
1399              
1400             This type will set the resulting value to be a L<Module::Generic::Scalar> object of the value provided.
1401              
1402             =item * C<string>
1403              
1404             Same as C<scalar>. This type will set the resulting value to be a L<Module::Generic::Scalar> object of the value provided.
1405              
1406             =item * C<string-hash>
1407              
1408             This type will set the resulting value to be an L<Module::Generic::Hash> object.
1409              
1410             It enables the passing of <key=value> pairs as documented in L<Getopt::Long/"Options with hash values">
1411              
1412             So you coul do something like:
1413              
1414             use Getopt::Class;
1415             my $dict =
1416             {
1417             help => { type => 'code', code => sub{ pod2usage(1); }, alias => '?', action => 1 },
1418             man => { type => 'code', code => sub{ pod2usage( -exitstatus => 0, -verbose => 2 ); }, action => 1 },
1419             quiet => { type => 'boolean', default => 0, alias => 'silent' },
1420             verbose => { type => 'boolean', default => \$VERBOSE, alias => 'v' },
1421             version => { type => 'code', code => sub{ printf( "v%.2f\n", $VERSION ); }, action => 1 },
1422            
1423             dry_run => { type => 'boolean', default => 0 },
1424             name => { type => 'string', class => [qw( person product )] },
1425             created => { type => 'datetime', class => [qw( person product )] },
1426             define => { type => 'string-hash', default => {} },
1427             };
1428            
1429             # Assuming command line arguments like:
1430             prog.pl --create-user --name Bob --langs fr ja --age 30 --created now --debug 3 \
1431             --path ./here/some/where --skip ./bad/directory ./not/here ./avoid/me/
1432              
1433             my $opt = Getopt::Class->new({
1434             dictionary => $dict,
1435             }) || die( Getopt::Class->error, "\n" );
1436             my $opts = $opt->exec || die( $opt->error, "\n" );
1437             # etc...
1438              
1439             And then, call your script like:
1440              
1441             ./my_script.pl --define os=linux --define vendor=ubuntu
1442              
1443             And, then you would have an hash reference of two keys and their value: C<os> and C<vendor> available in C<< $opts->{define} >>
1444              
1445             =item * C<uri>
1446              
1447             This type will mark the value as a directory or file path and will become a L<URI> object, by default.
1448              
1449             You can override this default pacage, by using C<package> property, such as:
1450              
1451             url => { type => 'uri', package => 'URI' }
1452              
1453             =item * C<uri-array>
1454              
1455             Same as C<uri> argument type, but allows multiple value saved as an array. For example:
1456              
1457             ./my_prog.pl --uris https://example.com/some/where https://example.com/some/where/else
1458              
1459             This would result in the option property C<uris> being an L<array object|Module::Generic::Array> containing 2 entries.
1460              
1461             =back
1462              
1463             Also as seen in the example above, you can add additional properties to be used in your program, here such as C<action> that could be used to identify all options that are used to trigger an action or a call to a sub routine.
1464              
1465             =back
1466              
1467             =item * C<debug>
1468              
1469             This takes an integer, and is used to set the level of debugging. Anything under 3 will not provide anything meaningful.
1470              
1471             =back
1472              
1473             =head1 METHODS
1474              
1475             =head2 check_class_data
1476              
1477             Provided with a string corresponding to a class name, this will check the data provided by the user.
1478              
1479             Currently this means it checks if the data is present when the attribute I<required> is set, and it checks the data against a regular expression if one is provided with the attribute I<re>
1480              
1481             It returns an hash reference with 2 keys: I<missing> and I<regexp>. Each with an anonymous hash reference with key matching the option name and the value the error string. So:
1482              
1483             my $dict =
1484             {
1485             name => { type => 'string', class => [qw( person product )], required => 1 },
1486             langs => { type => 'array', class => [qw( person product )], re => qr/^[a-z]{2}([_|-][A-Z]{2})?/, min => 1, default => [qw(en)] },
1487             };
1488              
1489             Assuming your user calls your program without C<--name> and with C<--langs FR EN> this would have L</"check_class_data"> return the following data structure:
1490              
1491             $errors =
1492             {
1493             missing => { name => "name (name) is missing" },
1494             regexp => { langs => "langs (langs) does not match requirements" },
1495             };
1496              
1497             =head2 class
1498              
1499             Provided with a string representing a property class, and this returns an hash reference of all the dictionary entries matching this class
1500              
1501             =head2 classes
1502              
1503             This returns an hash reference containing class names, each of which has an anonymous hash reference with corresponding dictionary entries
1504              
1505             =head2 class_properties
1506              
1507             Provided with a string representing a class name, this returns an array reference of options, a.k.a. class properties.
1508              
1509             The array reference is a L<Module::Generic::Array> object.
1510              
1511             =head2 configure
1512              
1513             This calls L<Getopt::Long/"configure"> with the L</"configure_options">.
1514              
1515             It can be overriden by calling L</"configure"> with an array reference.
1516              
1517             If there is an error, it will return undef and set an L</"error"> accordingly.
1518              
1519             Otherwise, it returns the L<Getopt::Class> object, so it can be chained.
1520              
1521             =head2 configure_errors
1522              
1523             This returns an array reference of the errors generated by L<Getopt::Long> upon calling L<Getopt::Long/"getoptions"> by L</"exec">
1524              
1525             The array is an L<Module::Generic::Array> object
1526              
1527             =head2 configure_options
1528              
1529             This returns an array reference of the L<Getopt::Long> configuration options upon calling L<Getopt::Long/"configure"> by method L</"configure">
1530              
1531             The array is an L<Module::Generic::Array> object
1532              
1533             =head2 dictionary
1534              
1535             This returns the hash reference representing the dictionary set when the object was instantiated. See L</"new"> method.
1536              
1537             =head2 error
1538              
1539             Return the last error set as a L<Module::Generic::Exception> object. Because the object can be stringified, you can do directly:
1540              
1541             die( $opt->error, "\n" ); # with a stack trace
1542              
1543             or
1544              
1545             die( sprintf( "Error occurred at line %d in file %s with message %s\n", $opt->error->line, $opt->error->file, $opt->error->message ) );
1546              
1547             =head2 exec
1548              
1549             This calls L<Getopt::Long/"getoptions"> with the L</"options"> hash reference and the L</"parameters"> array reference and after having called L</"configure"> to configure L<Getopt::Long> with the proper parameters according to the dictionary provided at the time of object instantiation.
1550              
1551             If there are any L<Getopt::Long> error, they can be retrieved with method L</"configure_errors">
1552              
1553             my $opt = Getopt::Class->new({ dictionary => $dict }) || die( Getopt::Class->error );
1554             my $opts = $opt->exec || die( $opt->error );
1555             if( $opt->configure_errors->length > 0 )
1556             {
1557             # do something about it
1558             }
1559              
1560             If any required options have been specified with the method L</"required">, it will check any missing option then and set an array of those missing options that can be retrieved with method L</"missing">
1561              
1562             This method makes sure that any option can be accessed with underscore or dash whichever, so a dictionary entry such as:
1563              
1564             my $dict =
1565             {
1566             create_customer => { type => 'boolean', alias => [qw(create_client create_user)], action => 1 },
1567             };
1568              
1569             can be called by your user like:
1570              
1571             ---create-customer
1572             # or
1573             --create-client
1574             # or
1575             --create-user
1576              
1577             because a duplicate entry with the underscore replaced by a dash is created (actually it's an alias of one to another). So you can say in your program:
1578              
1579             my $opts = $opt->exec || die( $opt->error );
1580             if( $opts->{create_user} )
1581             {
1582             # do something
1583             }
1584              
1585             L</"exec"> returns an hash reference whose properties can be accessed directly, but those properties can also be accessed as methods.
1586              
1587             This is made possible because the hash reference returned is a blessed object from L<Getopt::Class::Values> and provides an object oriented access to all the option values.
1588              
1589             A string is an object from L<Module::Generic::Scalar>
1590              
1591             $opts->customer_name->index( 'Doe' ) != -1
1592              
1593             A boolean is an object from L<Module::Generic::Boolean>
1594              
1595             An integer or decimal is an object from L<Text::Number>
1596              
1597             A date/dateime value is an object from L<DateTime>
1598              
1599             $opts->created->iso8601 # 2020-05-01T17:10:20
1600              
1601             An hash reference is an object created with L<Module::Generic/"_set_get_hash_as_object">
1602              
1603             $opts->metadata->transaction_id
1604              
1605             An array reference is an object created with L<Module::Generic/"_set_get_array_as_object">
1606              
1607             $opts->langs->push( 'en_GB' ) if( !$opts->langs->exists( 'en_GB' ) );
1608             $opts->langs->forEach(sub{
1609             $self->active_user_lang( shift( @_ ) );
1610             });
1611              
1612             Whatever the object type of the option value is based on the dictionary definitions you provide to L</"new">
1613              
1614             =head2 get_class_values
1615              
1616             Provided with a string representing a property class, and this returns an hash reference of all the key-value pairs provided by your user. So:
1617              
1618             my $dict =
1619             {
1620             create_customer => { type => 'boolean', alias => [qw(create_client create_user)], action => 1 },
1621             name => { type => 'string', class => [qw( person product )] },
1622             created => { type => 'datetime', class => [qw( person product )] },
1623             define => { type => 'string-hash', default => {} },
1624             langs => { type => 'array', class => [qw( person product )], re => qr/^[a-z]{2}([_|-][A-Z]{2})?/, min => 1, default => [] },
1625             currency => { type => 'string', class => [qw(product)], name => 'currency', re => qr/^[a-z]{3}$/, error => "must be a three-letter iso 4217 value" },
1626             age => { type => 'integer', class => [qw(person)], name => 'age', },
1627             };
1628              
1629             Then the user calls your program with:
1630              
1631             --create-user --name Bob --age 30 --langs en ja --created now
1632              
1633             # In your app
1634             my $opt = Getopt::Class->new({ dictionary => $dict }) || die( Getopt::Class->error );
1635             my $opts = $opt->exec || die( $opt->error );
1636             # $vals being an hash reference as a subset of all the values returned in $opts above
1637             my $vals = $opt->get_class_values( 'person' )
1638             # returns an hash only with keys name, age, langs and created
1639              
1640             =head2 getopt
1641              
1642             Sets or get the L<Getopt::Long::Parser> object. You can provide yours if you want but beware that certain options are necessary for L<Getopt::Class> to work. You can check those options with the method L</"configure_options">
1643              
1644             =head2 missing
1645              
1646             Returns an array of missing options. The array reference returned is a L<Module::Generic::Array> object, so you can do thins like
1647              
1648             if( $opt->missing->length > 0 )
1649             {
1650             # do something
1651             }
1652              
1653             =head2 options
1654              
1655             Returns an hash reference of options created by L</"new"> based on the dictionary you provide. This hash reference is used by L</"exec"> to call L<Getopt::Long/"getoptions">
1656              
1657             =head2 parameters
1658              
1659             Returns an array reference of parameters created by L</"new"> based on the dictionary you provide. This hash reference is used by L</"exec"> to call L<Getopt::Long/"getoptions">
1660              
1661             This array reference is a L<Module::Generic::Array> object
1662              
1663             =head2 required
1664              
1665             Set or get the array reference of required options. This returns a L<Module::Generic::Array> object.
1666              
1667             =head2 usage
1668              
1669             Set or get the anonymous subroutine or sub routine reference used to show the user the proper usage of your program.
1670              
1671             This is called by L</"exec"> after calling L<Getopt::Long/"getoptions"> if there is an error, i.e. if L<Getopt::Long/"getoptions"> does not return a true value.
1672              
1673             If you use object to call the sub routine usage, I recommend using the module L<curry>
1674              
1675             If this is not set, L</"exec"> will simply return undef or an empty list depending on the calling context.
1676              
1677             =head1 ERROR HANDLING
1678              
1679             This module never dies, or at least not by design. If an error occurs, each method returns undef and sets an error that can be retrieved with the method L</"error">
1680              
1681             =head1 AUTHOR
1682              
1683             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
1684              
1685             =head1 SEE ALSO
1686              
1687             L<Getopt::Long>
1688              
1689             =head1 COPYRIGHT & LICENSE
1690              
1691             Copyright (c) 2019-2020 DEGUEST Pte. Ltd.
1692              
1693             You can use, copy, modify and redistribute this package and associated
1694             files under the same terms as Perl itself.
1695              
1696             =cut