File Coverage

blib/lib/OptArgs.pm
Criterion Covered Total %
statement 337 361 93.3
branch 154 212 72.6
condition 61 94 64.8
subroutine 20 21 95.2
pod 7 7 100.0
total 579 695 83.3


line stmt bran cond sub pod time code
1             package OptArgs;
2 11     11   532885 use strict;
  11         114  
  11         275  
3 11     11   46 use warnings;
  11         15  
  11         256  
4 11     11   43 use Carp qw/croak carp/;
  11         15  
  11         520  
5 11     11   5846 use Encode qw/decode/;
  11         89119  
  11         1905  
6             use Exporter::Tidy
7 11         68 default => [qw/opt arg optargs usage subcmd/],
8 11     11   9600 other => [qw/dispatch class_optargs/];
  11         135  
9 11     11   7339 use Getopt::Long qw/GetOptionsFromArray/;
  11         121245  
  11         52  
10 11     11   1820 use List::Util qw/max/;
  11         20  
  11         42491  
11              
12             our $VERSION = '0.1.21';
13             our $COLOUR = 0;
14             our $ABBREV = 0;
15             our $SORT = 0;
16             our $PRINT_DEFAULT = 0;
17             our $PRINT_ISA = 0;
18              
19             my %seen; # hash of hashes keyed by 'caller', then opt/arg name
20             my %opts; # option configuration keyed by 'caller'
21             my %args; # argument configuration keyed by 'caller'
22             my %caller; # current 'caller' keyed by real caller
23             my %desc; # sub-command descriptions
24             my %dispatching; # track optargs() calls from dispatch classes
25             my %hidden; # subcmd hiding by default
26              
27             # internal function for App::optargs
28             sub _cmdlist {
29 4     4   12 return sort grep { $_ ne 'App::optargs' } keys %seen;
  36         62  
30             }
31              
32             # ------------------------------------------------------------------------
33             # Sub-command definition
34             #
35             # This works by faking caller context in opt() and arg()
36             # ------------------------------------------------------------------------
37             my %subcmd_params = (
38             cmd => undef,
39             comment => undef,
40             hidden => undef,
41              
42             # alias => '',
43             # ishelp => undef,
44             );
45              
46             my @subcmd_required = (qw/cmd comment/);
47              
48             sub subcmd {
49 18     18 1 1552 my $params = {@_};
50 18         50 my $caller = caller;
51              
52 18 100       32 if ( my @missing = grep { !exists $params->{$_} } @subcmd_required ) {
  36         84  
53 1         177 croak "missing required parameter(s): @missing";
54             }
55              
56 17 50       37 if ( my @invalid = grep { !exists $subcmd_params{$_} } keys %$params ) {
  36         87  
57 0         0 my @valid = keys %subcmd_params;
58 0         0 croak "invalid parameter(s): @invalid (valid: @valid)";
59             }
60              
61             # croak "'ishelp' can only be applied to Bool opts"
62             # if $params->{ishelp} and $params->{isa} ne 'Bool';
63              
64             my @cmd =
65             ref $params->{cmd} eq 'ARRAY'
66 10         18 ? @{ $params->{cmd} }
67 17 100       46 : ( $params->{cmd} );
68 17 50       33 croak 'missing cmd elements' unless @cmd;
69              
70 17         24 my $name = pop @cmd;
71 17         28 my $parent = join( '::', $caller, @cmd );
72 17         27 $parent =~ s/-/_/g;
73              
74 17 100       111 croak "parent command not found: @cmd" unless $seen{$parent};
75              
76 16         31 my $package = $parent . '::' . $name;
77 16         21 $package =~ s/-/_/g;
78              
79 16 100       97 croak "sub command already defined: @cmd $name" if $seen{$package};
80              
81 15         21 $caller{$caller} = $package;
82 15         33 $desc{$package} = $params->{comment};
83 15         26 $seen{$package} = {};
84 15         32 $opts{$package} = [];
85 15         22 $args{$package} = [];
86 15         22 $hidden{$package} = $params->{hidden};
87              
88 15         17 my $parent_arg = ( grep { $_->{isa} eq 'SubCmd' } @{ $args{$parent} } )[0];
  16         39  
  15         47  
89 15         24 push( @{ $parent_arg->{subcommands} }, $name );
  15         29  
90              
91 15         37 return;
92             }
93              
94             # ------------------------------------------------------------------------
95             # Option definition
96             # ------------------------------------------------------------------------
97             my %opt_params = (
98             isa => undef,
99             isa_name => undef,
100             comment => undef,
101             default => undef,
102             alias => '',
103             ishelp => undef,
104             hidden => undef,
105             );
106              
107             my @opt_required = (qw/isa comment/);
108              
109             my %opt_isa = (
110             'Bool' => '!',
111             'Counter' => '+',
112             'Str' => '=s',
113             'Int' => '=i',
114             'Num' => '=f',
115             'ArrayRef' => '=s@',
116             'HashRef' => '=s%',
117             );
118              
119             sub opt {
120 43     43 1 7312 my $name = shift;
121 43         115 my $params = {@_};
122 43         68 my $caller = caller;
123 43   66     138 my $package = $caller{$caller} || $caller;
124              
125 43 100       311 croak 'usage: opt $name => (%parameters)' unless $name;
126 39 100       208 croak "'$name' already defined" if $seen{$package}->{$name};
127              
128 37 100       58 if ( my @missing = grep { !exists $params->{$_} } @opt_required ) {
  74         179  
129 2         121 croak "missing required parameter(s): @missing";
130             }
131              
132 35 100       86 if ( my @invalid = grep { !exists $opt_params{$_} } keys %$params ) {
  91         167  
133 1         3 my @valid = keys %opt_params;
134 1         65 croak "invalid parameter(s): @invalid (valid: @valid)";
135             }
136              
137             croak "'ishelp' can only be applied to Bool opts"
138 34 100 100     167 if $params->{ishelp} and $params->{isa} ne 'Bool';
139              
140             croak "unknown type: $params->{isa}"
141 33 100       128 unless exists $opt_isa{ $params->{isa} };
142              
143 32         216 $params = { %opt_params, %$params };
144 32         89 $params->{package} = $package;
145 32         49 $params->{name} = $name;
146 32         45 $params->{length} = length $name;
147 32         36 $params->{acount} = do { my @tmp = split( '|', $params->{alias} ) };
  32         88  
148 32         48 $params->{type} = 'opt';
149 32         44 $params->{ISA} = $params->{name};
150              
151 32 100       133 if ( ( my $dashed = $params->{name} ) =~ s/_/-/g ) {
152 3         9 $params->{dashed} = $dashed;
153 3         10 $params->{ISA} .= '|' . $dashed;
154             }
155              
156 32 100       71 $params->{ISA} .= '|' . $params->{alias} if $params->{alias};
157 32         61 $params->{ISA} .= $opt_isa{ $params->{isa} };
158              
159 32         40 push( @{ $opts{$package} }, $params );
  32         55  
160 32   100     81 $args{$package} ||= [];
161 32         57 $seen{$package}->{$name}++;
162              
163 32         62 return;
164             }
165              
166             # ------------------------------------------------------------------------
167             # Argument definition
168             # ------------------------------------------------------------------------
169             my %arg_params = (
170             isa => undef,
171             comment => undef,
172             required => undef,
173             default => undef,
174             greedy => undef,
175             fallback => undef,
176             );
177              
178             my @arg_required = (qw/isa comment/);
179              
180             my %arg_isa = (
181             'Str' => '=s',
182             'Int' => '=i',
183             'Num' => '=f',
184             'ArrayRef' => '=s@',
185             'HashRef' => '=s%',
186             'SubCmd' => '=s',
187             );
188              
189             sub arg {
190 32     32 1 7838 my $name = shift;
191 32         118 my $params = {@_};
192 32         54 my $caller = caller;
193 32   66     108 my $package = $caller{$caller} || $caller;
194              
195 32 100       289 croak 'usage: arg $name => (%parameters)' unless $name;
196 28 100       198 croak "'$name' already defined" if $seen{$package}->{$name};
197              
198 26 100       48 if ( my @missing = grep { !exists $params->{$_} } @arg_required ) {
  52         136  
199 2         119 croak "missing required parameter(s): @missing";
200             }
201              
202 24 100       63 if ( my @invalid = grep { !exists $arg_params{$_} } keys %$params ) {
  65         122  
203 1         4 my @valid = keys %arg_params;
204 1         64 croak "invalid parameter(s): @invalid (valid: @valid)";
205             }
206              
207             croak "unknown type: $params->{isa}"
208 23 100       127 unless exists $arg_isa{ $params->{isa} };
209              
210             croak "'default' and 'required' cannot be used together"
211 22 100 100     131 if defined $params->{default} and defined $params->{required};
212              
213             croak "'fallback' only valid with isa 'SubCmd'"
214 21 100 100     110 if $params->{fallback} and $params->{isa} ne 'SubCmd';
215              
216             croak "fallback must be a hashref"
217 20 100 100     107 if defined $params->{fallback} && ref $params->{fallback} ne 'HASH';
218              
219 19         33 $params->{package} = $package;
220 19         26 $params->{name} = $name;
221 19         42 $params->{length} = length $name;
222 19         28 $params->{acount} = 0;
223 19         39 $params->{type} = 'arg';
224 19         57 $params->{ISA} = $params->{name} . $arg_isa{ $params->{isa} };
225              
226 19         23 push( @{ $args{$package} }, $params );
  19         40  
227 19   100     51 $opts{$package} ||= [];
228 19         41 $seen{$package}->{$name}++;
229              
230 19 100       39 if ( $params->{fallback} ) {
231 1         3 my $p = $package . '::' . uc $params->{fallback}->{name};
232 1         3 $p =~ s/-/_/g;
233 1         2 $opts{$p} = [];
234 1         2 $args{$p} = [];
235 1         2 $desc{$p} = $params->{fallback}->{comment};
236             }
237              
238 19         38 return;
239             }
240              
241             # ------------------------------------------------------------------------
242             # Usage message generation
243             # ------------------------------------------------------------------------
244              
245             sub _usage {
246 7     7   26 my $caller = shift;
247 7         10 my $error = shift;
248 7         19 my $ishelp = shift;
249 7         57 my $terminal = -t STDOUT;
250 7 50 66     31 my $red = ( $COLOUR && $terminal ) ? "\e[0;31m" : '';
251 7         10 my $yellow = ''; #( $COLOUR && $terminal ) ? "\e[0;33m" : '';
252 7         9 my $grey = ''; #( $COLOUR && $terminal ) ? "\e[1;30m" : '';
253 7 50 66     23 my $reset = ( $COLOUR && $terminal ) ? "\e[0m" : '';
254 7         14 my $parent = $caller;
255 7         14 my @args = @{ $args{$caller} };
  7         19  
256 7         10 my @opts = @{ $opts{$caller} };
  7         13  
257 7         22 my @parents;
258             my @usage;
259 7         0 my @uargs;
260 7         0 my @uopts;
261 7         0 my $usage;
262              
263 7         34 require File::Basename;
264 7 50       248 my $me = File::Basename::basename( defined &static::list ? $^X : $0 );
265              
266 7 100       21 if ($error) {
267 3         11 $usage .= "${red}error:$reset $error\n\n";
268             }
269              
270 7 100       30 $usage .= $yellow . ( $ishelp ? 'help:' : 'usage:' ) . $reset . ' ' . $me;
271              
272 7         30 while ( $parent =~ s/(.*)::(.*)/$1/ ) {
273 8 100       26 last unless $seen{$parent};
274 3         7 ( my $name = $2 ) =~ s/_/-/g;
275 3         6 unshift( @parents, $name );
276 3         5 unshift( @opts, @{ $opts{$parent} } );
  3         14  
277             }
278              
279 7 100       18 $usage .= ' ' . join( ' ', @parents ) if @parents;
280              
281 7         12 my $last = $args[$#args];
282              
283 7 100       35 if ($last) {
284 6         11 foreach my $def (@args) {
285 6         12 $usage .= ' ';
286 6 50       32 $usage .= '[' unless $def->{required};
287 6         15 $usage .= uc $def->{name};
288 6 50       11 $usage .= '...' if $def->{greedy};
289 6 50       18 $usage .= ']' unless $def->{required};
290 6         18 push( @uargs, [ uc $def->{name}, $def->{comment} ] );
291             }
292             }
293              
294 7 50       20 $usage .= ' [OPTIONS...]' if @opts;
295              
296 7         22 $usage .= "\n";
297              
298             $usage .= "\n ${grey}Synopsis:$reset\n $desc{$caller}\n"
299 7 50 66     24 if $ishelp and $desc{$caller};
300              
301 7 50 66     26 if ( $ishelp and my $version = $caller->VERSION ) {
302 0         0 $usage .= "\n ${grey}Version:$reset\n $version\n";
303             }
304              
305 7 100 100     35 if ( $last && $last->{isa} eq 'SubCmd' ) {
306 4         17 $usage .= "\n ${grey}" . ucfirst( $last->{name} ) . ":$reset\n";
307              
308 4         5 my @subcommands = @{ $last->{subcommands} };
  4         10  
309              
310             push( @subcommands, uc $last->{fallback}->{name} )
311             if (
312             exists $last->{fallback}
313             && ( $ishelp
314             or !$last->{fallback}->{hidden} )
315 4 0 0     10 );
      33        
316              
317 4 100       12 @subcommands = sort @subcommands if $SORT;
318              
319 4         7 foreach my $subcommand (@subcommands) {
320 10         16 my $pkg = $last->{package} . '::' . $subcommand;
321 10         15 $pkg =~ s/-/_/g;
322 10 50 66     23 next if $hidden{$pkg} and !$ishelp;
323 10         22 push( @usage, [ $subcommand, $desc{$pkg} ] );
324             }
325              
326             }
327              
328 7 100       23 @opts = sort { $a->{name} cmp $b->{name} } @opts if $SORT;
  3         6  
329              
330 7         12 foreach my $opt (@opts) {
331 18 50 33     45 next if $opt->{hidden} and !$ishelp;
332              
333 18         38 ( my $name = $opt->{name} ) =~ s/_/-/g;
334              
335 18 50 66     47 if ( $opt->{isa} eq 'Bool' and $opt->{default} ) {
336 0         0 $name = 'no-' . $name;
337             }
338              
339 18         21 my $default = '';
340 18 0 33     37 if ( $PRINT_DEFAULT && defined $opt->{default} and !$opt->{ishelp} ) {
      33        
341             my $value =
342             ref $opt->{default} eq 'CODE'
343             ? $opt->{default}->( {%$opt} )
344 0 0       0 : $opt->{default};
345 0 0       0 if ( $opt->{isa} eq 'Bool' ) {
346 0 0       0 $value = $value ? 'true' : 'false';
347             }
348 0         0 $default = " [default: $value]";
349             }
350              
351 18 50       23 if ($PRINT_ISA) {
352 0 0 0     0 if ( $opt->{isa_name} ) {
    0 0        
    0          
    0          
353 0         0 $name .= '=' . uc $opt->{isa_name};
354             }
355             elsif ($opt->{isa} eq 'Str'
356             || $opt->{isa} eq 'HashRef'
357             || $opt->{isa} eq 'ArrayRef' )
358             {
359 0         0 $name .= '=STR';
360             }
361             elsif ( $opt->{isa} eq 'Int' ) {
362 0         0 $name .= '=INT';
363             }
364             elsif ( $opt->{isa} eq 'Num' ) {
365 0         0 $name .= '=NUM';
366             }
367             }
368              
369 18 100       33 $name .= ',' if $opt->{alias};
370             push(
371             @uopts,
372             [
373             '--' . $name,
374             $opt->{alias}
375             ? '-' . $opt->{alias}
376             : '',
377 18 100       60 $opt->{comment} . $default
378             ]
379             );
380             }
381              
382 7 50       14 if (@uopts) {
383 7         10 my $w1 = max( map { length $_->[0] } @uopts );
  18         42  
384 7         16 my $fmt = '%-' . $w1 . "s %s";
385              
386 7         11 @uopts = map { [ sprintf( $fmt, $_->[0], $_->[1] ), $_->[2] ] } @uopts;
  18         67  
387             }
388              
389 7         43 my $w1 = max( map { length $_->[0] } @usage, @uargs, @uopts );
  34         46  
390 7         15 my $format = ' %-' . $w1 . "s %s\n";
391              
392 7 100       33 if (@usage) {
393 4         5 foreach my $row (@usage) {
394 10         34 $usage .= sprintf( $format, @$row );
395             }
396             }
397 7 100 100     37 if ( @uargs and $last->{isa} ne 'SubCmd' ) {
398 2         7 $usage .= "\n ${grey}Arguments:$reset\n";
399 2         3 foreach my $row (@uargs) {
400 2         9 $usage .= sprintf( $format, @$row );
401             }
402             }
403 7 50       12 if (@uopts) {
404 7         16 $usage .= "\n ${grey}Options:$reset\n";
405 7         12 foreach my $row (@uopts) {
406 18         36 $usage .= sprintf( $format, @$row );
407             }
408             }
409              
410 7         21 $usage .= "\n";
411 7         81 return bless( \$usage, 'OptArgs::Usage' );
412             }
413              
414             sub _synopsis {
415 32     32   42 my $caller = shift;
416 32         34 my $parent = $caller;
417 32         34 my @args = @{ $args{$caller} };
  32         58  
418 32         37 my @parents;
419              
420 32         118 require File::Basename;
421 32         657 my $usage = File::Basename::basename($0);
422              
423 32         147 while ( $parent =~ s/(.*)::(.*)/$1/ ) {
424 88 100       168 last unless $seen{$parent};
425 56         86 ( my $name = $2 ) =~ s/_/-/g;
426 56         198 unshift( @parents, $name );
427             }
428              
429 32 100       72 $usage .= ' ' . join( ' ', @parents ) if @parents;
430              
431 32 100       61 if ( my $last = $args[$#args] ) {
432 12         15 foreach my $def (@args) {
433 12         13 $usage .= ' ';
434 12 50       24 $usage .= '[' unless $def->{required};
435 12         16 $usage .= uc $def->{name};
436 12 50       20 $usage .= '...' if $def->{greedy};
437 12 50       20 $usage .= ']' unless $def->{required};
438             }
439             }
440              
441 32         92 return 'usage: ' . $usage . "\n";
442             }
443              
444             sub usage {
445 0     0 1 0 my $caller = caller;
446 0         0 return _usage( $caller, @_ );
447             }
448              
449             # ------------------------------------------------------------------------
450             # Option/Argument processing
451             # ------------------------------------------------------------------------
452             sub _optargs {
453 50     50   80 my $caller = shift;
454 50         89 my $source = \@_;
455 50         83 my $source_hash = {};
456 50         70 my $package = $caller;
457              
458 50 100 100     196 if ( !@_ and @ARGV ) {
459             my $CODESET =
460 27         43 eval { require I18N::Langinfo; I18N::Langinfo::CODESET() };
  27         3576  
  27         4620  
461              
462 27 50       79 if ($CODESET) {
463 27         95 my $codeset = I18N::Langinfo::langinfo($CODESET);
464 27         127 $_ = decode( $codeset, $_ ) for @ARGV;
465             }
466              
467 27         6701 $source = \@ARGV;
468             }
469             else {
470 23         58 $source_hash = { map { %$_ } grep { ref $_ eq 'HASH' } @$source };
  0         0  
  29         65  
471 23         48 $source = [ grep { ref $_ ne 'HASH' } @$source ];
  29         52  
472             }
473              
474 50 50       92 map { Carp::croak('_optargs argument undefined!') if !defined $_ } @$source;
  84         204  
475              
476             croak "no option or argument defined for $caller"
477             unless exists $opts{$package}
478 50 50 66     180 or exists $args{$package};
479              
480 49         183 Getopt::Long::Configure(qw/pass_through no_auto_abbrev no_ignore_case/);
481              
482 49         2412 my @config = ( @{ $opts{$package} }, @{ $args{$package} } );
  49         92  
  49         127  
483              
484 49         86 my $ishelp;
485             my $missing_required;
486 49         78 my $optargs = {};
487 49         69 my @coderef_default_keys;
488              
489 49         116 while ( my $try = shift @config ) {
490 210         225 my $result;
491              
492 210 100       407 if ( $try->{type} eq 'opt' ) {
    50          
493 155 50       292 if ( exists $source_hash->{ $try->{name} } ) {
494 0         0 $result = delete $source_hash->{ $try->{name} };
495             }
496             else {
497 155         337 GetOptionsFromArray( $source, $try->{ISA} => \$result );
498             }
499             }
500             elsif ( $try->{type} eq 'arg' ) {
501 55 100 100     197 if (@$source) {
    50          
    100          
502 34 50       102 die _usage( $package, qq{Unknown option "$source->[0]"} )
503             if $source->[0] =~ m/^--\S/;
504              
505             die _usage( $package, qq{Unknown option "$source->[0]"} )
506             if $source->[0] =~ m/^-\S/
507             and !(
508             $source->[0] =~ m/^-\d/ and ( $try->{isa} ne 'Num'
509 34 50 0     81 or $try->{isa} ne 'Int' )
      33        
510             );
511              
512 34 100       65 if ( $try->{greedy} ) {
513 2         3 my @later;
514 2 50 33     5 if ( @config and @$source > @config ) {
515 0         0 push( @later, pop @$source ) for @config;
516             }
517              
518 2 50       4 if ( $try->{isa} eq 'ArrayRef' ) {
    50          
519 0         0 $result = [@$source];
520             }
521             elsif ( $try->{isa} eq 'HashRef' ) {
522 0         0 $result = { map { split /=/, $_ } @$source };
  0         0  
523             }
524             else {
525 2         6 $result = "@$source";
526             }
527              
528 2         7 shift @$source while @$source;
529 2         4 push( @$source, @later );
530             }
531             else {
532 32 100       87 if ( $try->{isa} eq 'ArrayRef' ) {
    100          
533 1         4 $result = [ shift @$source ];
534             }
535             elsif ( $try->{isa} eq 'HashRef' ) {
536 1         6 $result = { split /=/, shift @$source };
537             }
538             else {
539 30         43 $result = shift @$source;
540             }
541             }
542              
543             # TODO: type check using Param::Utils?
544             }
545             elsif ( exists $source_hash->{ $try->{name} } ) {
546 0         0 $result = delete $source_hash->{ $try->{name} };
547             }
548             elsif ( $try->{required} and !$ishelp ) {
549 2         4 $missing_required++;
550 2         8 next;
551             }
552              
553 53 100 100     123 if ( $try->{isa} eq 'SubCmd' and $result ) {
554              
555             # look up abbreviated words
556 10 100       22 if ($ABBREV) {
557 6         698 require Text::Abbrev;
558             my %words =
559 13         77 map { m/^$package\:\:(\w+)$/; $1 => 1 }
  13         44  
560 6         61 grep { m/^$package\:\:(\w+)$/ }
  48         173  
561             keys %seen;
562 6         22 my %abbrev = Text::Abbrev::abbrev( keys %words );
563 6 50       347 $result = $abbrev{$result} if defined $abbrev{$result};
564             }
565              
566 10         17 my $newpackage = $package . '::' . $result;
567 10         20 $newpackage =~ s/-/_/g;
568              
569 10 100       23 if ( exists $seen{$newpackage} ) {
    50          
570 8         9 $package = $newpackage;
571 8         15 @config = grep { $_->{type} eq 'opt' } @config;
  0         0  
572 8         9 push( @config, @{ $opts{$package} }, @{ $args{$package} } );
  8         12  
  8         13  
573             }
574             elsif ( !$ishelp ) {
575 2 100       7 if ( $try->{fallback} ) {
576 1         3 unshift @$source, $result;
577 1         2 $try->{fallback}->{type} = 'arg';
578 1         2 unshift( @config, $try->{fallback} );
579 1         2 next;
580             }
581             else {
582             die _usage( $package,
583 1         8 "Unknown " . uc( $try->{name} ) . qq{ "$result"} );
584             }
585             }
586              
587 8         9 $result = undef;
588             }
589              
590             }
591              
592 206 100       21759 if ( defined $result ) {
    100          
593 51         173 $optargs->{ $try->{name} } = $result;
594             }
595             elsif ( defined $try->{default} ) {
596             push( @coderef_default_keys, $try->{name} )
597 16 100       42 if ref $try->{default} eq 'CODE';
598 16         31 $optargs->{ $try->{name} } = $result = $try->{default};
599             }
600              
601 206 100 100     646 $ishelp = 1 if $result and $try->{ishelp};
602              
603             }
604              
605 48 100       254 if ($ishelp) {
    100          
    100          
    50          
606 2         6 die _usage( $package, undef, 1 );
607             }
608             elsif ($missing_required) {
609 2         9 die _usage($package);
610             }
611             elsif (@$source) {
612 2         12 die _usage( $package, "Unexpected options or arguments: @$source" );
613             }
614             elsif ( my @unexpected = keys %$source_hash ) {
615 0         0 die _usage( $package,
616             "Unexpected HASH options or arguments: @unexpected" );
617             }
618              
619             # Re-calculate the default if it was a subref
620 42         77 foreach my $key (@coderef_default_keys) {
621 8         541 $optargs->{$key} = $optargs->{$key}->( {%$optargs} );
622             }
623              
624 42         2142 return ( $package, $optargs );
625             }
626              
627             sub optargs {
628 37     37 1 2687 my $caller = caller;
629              
630             carp "optargs() called from dispatch handler"
631 37 50       107 if $dispatching{$caller};
632              
633 37         97 my ( $package, $optargs ) = _optargs( $caller, @_ );
634 34         244 return $optargs;
635             }
636              
637             sub class_optargs {
638 13     13 1 24 my $caller = shift;
639              
640 13 50       22 croak 'dispatch($class, [@argv])' unless $caller;
641             carp "optargs_class() called from dispatch handler"
642 13 50       25 if $dispatching{$caller};
643              
644 13 50       457 die $@ unless eval "require $caller;";
645              
646 13         45 my ( $class, $optargs ) = _optargs( $caller, @_ );
647              
648 8 50       392 croak $@ unless eval "require $class;1;";
649 8         30 return ( $class, $optargs );
650             }
651              
652             sub dispatch {
653 16     16 1 7865 my $method = shift;
654 16         28 my $class = shift;
655              
656 16 100 100     195 croak 'dispatch($method, $class, [@argv])' unless $method and $class;
657 14 100       720 croak $@ unless eval "require $class;1;";
658              
659 13         47 my ( $package, $optargs ) = class_optargs( $class, @_ );
660              
661 8         52 my $sub = $package->can($method);
662 8 50       18 die "Can't find method $method via package $package" unless $sub;
663              
664 8         14 $dispatching{$class}++;
665 8         19 my @results = $sub->($optargs);
666 8         247 $dispatching{$class}--;
667 8 100       33 return @results if wantarray;
668 4         12 return $results[0];
669             }
670              
671             package OptArgs::Usage;
672             use overload
673 7     7   219 bool => sub { 1 },
674 7     7   1858 '""' => sub { ${ $_[0] } },
  7         76  
675 11     11   100 fallback => 1;
  11         20  
  11         119  
676              
677             1;
678              
679             __END__