File Coverage

blib/lib/Getopt/AsDocumented.pm
Criterion Covered Total %
statement 148 196 75.5
branch 72 116 62.0
condition 5 22 22.7
subroutine 16 22 72.7
pod 8 8 100.0
total 249 364 68.4


line stmt bran cond sub pod time code
1             package Getopt::AsDocumented;
2             $VERSION = v0.0.2;
3              
4 2     2   38568 use warnings;
  2         4  
  2         67  
5 2     2   10 use strict;
  2         4  
  2         114  
6 2     2   19 use Carp;
  2         3  
  2         167  
7              
8 2     2   11 use base 'Getopt::Base';
  2         2  
  2         2561  
9              
10             =head1 NAME
11              
12             Getopt::AsDocumented - declare options as pod documentation
13              
14             =head1 SYNOPSIS
15              
16             =head1 Options
17             ...
18             =item -w, --what WHAT
19              
20             =cut
21              
22             sub main {
23             my (@args) = @_;
24             my $opt = Getopt::AsDocumented->process(\@args) or return;
25              
26             my $what = $opt->what;
27             ...
28             }
29              
30             =head1 About
31              
32             This module allows you to declare your program's command-line options as
33             pod documentation. It provides syntax to declare types and defaults in
34             a way which is also readable as documentation.
35              
36             Note: This is built on Getopt::Base and some advanced features are still
37             growing. Your help is welcome.
38              
39             =head1 Methods
40              
41             =head2 process
42              
43             Loads the pod from your current file and processes the command-line
44             arguments.
45              
46             my $opt = Getopt::AsDocumented->process(\@args) or return;
47              
48             =cut
49              
50             sub process {
51 1     1 1 845 my $self = shift;
52 1         2 my $args = shift;
53 1 50       5 (@_ % 2) and croak("odd number of elements in \%settings");
54 1         3 my %also = @_;
55 1 50       3 $self = $self->new(%also) unless(ref $self);
56              
57 1         8 return $self->SUPER::process($args);
58             } # process ############################################################
59              
60             =head1 Option Specification
61              
62             =head2 With/Without Opterands
63              
64             If an option is followed by a word, it requires an opterand.
65              
66             =item --foo FOO
67              
68             Otherwise, it is a simple flag (boolean) option
69              
70             =item --foo
71              
72             =head3 Booleans
73              
74             Each boolean option will automatically generate a '--no-' form which
75             negates it. You may choose to mention this and/or link aliases to it.
76              
77             =item --foo
78              
79             Sets the fooness.
80              
81             =item -x, --ex-foo, --no-foo
82              
83             =head2 Types
84              
85             Non-boolean options may be typed as strings, numbers, or integers. The
86             type is included in parenthesis after the option spec. If the type is not mentioned, it defaults to C.
87              
88             =head3 integer
89              
90             An integer.
91              
92             =item --foo FOO (integer)
93              
94             =head3 number
95              
96             A floating-point number.
97              
98             =item --foo FOO (number)
99              
100             =head3 string
101              
102             A string. This is the default, but may be included for clarity.
103              
104             =item --foo FOO (string)
105              
106             =head2 Scalar/HASH/ARRAY
107              
108             Any non-boolean option can take one of the following forms.
109              
110             =head3 Scalar
111              
112             An option followed by a simple word means that only one value is
113             assigned to it (if the user repeats it, a prior value is overwritten.)
114              
115             =item --foo FOO
116              
117             =head3 HASH
118              
119             A HASH option is followed by something of the form C<\w+=.*>. Each
120             opterand is treated as a $key=$value pair.
121              
122             =item --foo BAR=BAZ
123              
124             =head3 LIST
125              
126             A LIST option is followed by another mention of itself within []
127             brackets with an ellipsis to indicate optional additional elements.
128              
129             =item --foo FOO [--foo ...]
130              
131             If a list option requires an explicit type, this must be included after
132             the bracketed text.
133              
134             =item --foo FOO [--foo ...] (integer)
135              
136             =head2 Defaults
137              
138             An option's default may be set by the string "DEFAULT: " at the
139             beginning of a paragraph. The remainder of that paragraph contains the
140             default value.
141              
142             =item --foo FOO
143              
144             The setting for foo.
145              
146             DEFAULT: bar
147              
148             Any leading whitespace after the ':' is removed.
149              
150             A single leading backslash (if present) will be removed and the rest of
151             the string will be treated as a literal.
152              
153             A boolean default may be "NO". Without a value, a boolean will default
154             to undef. Anything true will be translated to '1'.
155              
156             =item --foo
157              
158             Whether to foo or not.
159              
160             DEFAULT: yes. Use --no-foo to disable this.
161              
162             The strings "no" or "false" may also be used as "0".
163              
164             If the default is enclosed with braces ({}), it is interpreted as a
165             block of code. For literal braces, use a leading backslash.
166              
167             =item --input FILENAME
168              
169             Input file.
170              
171             DEFAULT: {File::Fu->home + 'input.txt'}
172              
173             =head1 Handlers
174              
175             =head2 config_file_handler
176              
177             Loads the user's configuration file. All of the values from the
178             configuration will be loaded into the options object I any
179             options from the command-line are processed.
180              
181             $go->config_file_handler;
182              
183             =cut
184              
185             sub config_file_handler {
186 0     0 1 0 my $self = shift;
187 0         0 my ($file) = shift;
188 0         0 $self->load_config_file($file);
189             } # config_file_handler ################################################
190              
191             =head2 load_config_file
192              
193             $self->load_config_file($file);
194              
195             =cut
196              
197             sub load_config_file {
198 1     1 1 2 my $self = shift;
199 1         1 my ($file) = @_;
200              
201             my $mod = sub {
202 1     1   2 foreach my $m (qw(YAML::XS YAML::Syck YAML)) {
203 3 100       158 eval("require $m") and return($m);
204             }
205 0         0 croak("cannot load any yaml module $@");
206 1         7 }->();
207 1 50       7955 croak("what?") unless($mod);
208              
209 1         11 my $loader = $mod->can('LoadFile');
210              
211 1         4 my ($data) = $loader->($file);
212 1         17251 $self->set_values(%$data);
213             } # load_config_file ###################################################
214              
215             =head2 make_object
216              
217             Wraps the super method in order to load the config file.
218              
219             $obj = $self->make_object;
220              
221             =cut
222              
223             sub make_object {
224 1     1 1 19 my $self = shift;
225 1         7 my $obj = $self->SUPER::make_object(@_);
226              
227 1 50       1672 if(my $do = $obj->can('config_file')) {
228             # XXX this is so wrong
229 1         2 my %defaults = map({@$_} @{$self->{_defaults}});
  0         0  
  1         3  
230 1         2 my $lazy = $defaults{config_file};
231              
232 1 50 33     23 if(my $file = $do->($obj) ||
233             $lazy && do {$obj->{config_file} = $lazy->()}
234             ) {
235 1         8 local $self->{object} = $obj; # must have a context
236 1 50       26 $self->load_config_file($file) if(-e $file);
237             }
238             }
239              
240 0         0 return($obj);
241             } # make_object ########################################################
242              
243              
244             =head2 handler
245              
246             Accessor.
247              
248             my $handler = $go->handler;
249              
250             =cut
251              
252 0     0 1 0 sub handler { shift->{handler} }
253              
254             =head2 version_handler
255              
256             Prints the version from your handler/caller()'s package.
257              
258             $go->version_handler;
259              
260             Sets the quit flag.
261              
262             =cut
263              
264             sub version_handler {
265 0     0 1 0 my $self = shift;
266              
267 0         0 my $caller = $self->{handler};
268 0   0     0 $caller = ref($caller) || $caller;
269 0         0 eval {require version}; # for stringy VERSION() support (I hope)
  0         0  
270 0   0     0 my $v = $caller->VERSION || main->VERSION || '';
271 0   0     0 my $name = $self->{program_name} || do {
272             require File::Basename;
273             File::Basename::basename($0)
274             };
275 0         0 print "$name version $v\n";
276 0         0 $self->quit;
277             } # version_handler ####################################################
278              
279             =head2 help_handler
280              
281             Prints a help message based on the USAGE and OPTIONS sections from your
282             pod. Uses the first sentence from each C<=item> section, or
283             alternatively: C<=for help> content found within the C<=item> section.
284              
285             $go->help_handler;
286              
287             Sets the quit flag.
288              
289             =cut
290              
291             sub help_handler {
292 0     0 1 0 my $self = shift;
293 0         0 print "Usage:\n", $self->{usage}, "\n\n";
294              
295 0         0 my @options = map({
296 0         0 my $d = $self->{opt_data}{$_};
297 0         0 my $type = $d->{type};
298             [
299 0 0 0     0 $self->{help_bits}{$_} .
300             (($type ne 'boolean' and $type ne 'string') ?
301             (' (' . substr($type, 0, 3) . ')') : '')
302             ,
303             $self->{help}{$_}
304             ]
305 0         0 } @{$self->{help_order}});
306 0         0 my ($longest) = sort({$b <=> $a} map({length($_->[0])} @options));
  0         0  
  0         0  
307 0         0 @options = map({sprintf('%-'.$longest."s %s", @$_)} @options);
  0         0  
308 0         0 print join("\n ",
309             "Options:", @options
310             ), "\n";
311              
312 0         0 $self->quit;
313             } # help_handler #######################################################
314              
315             =head1 Other Methods
316              
317             =head2 new
318              
319             my $go = Getopt::AsDocumented->new(%settings);
320              
321             =over
322              
323             =item pod => $string
324              
325             =item from_file => $filename
326              
327             =item handler => $classname
328              
329             =back
330              
331             =cut
332              
333             sub new {
334 1     1 1 10 my $class = shift;
335 1 50       5 (@_ % 2) and croak("odd number of elements in \%settings");
336 1         5 my %setup = @_;
337              
338 1         2 my %pass;
339 1         2 foreach my $key (qw(arg_handler)) {
340 1 50       6 $pass{$key} = delete($setup{$key}) if(exists($setup{$key}));
341             }
342              
343 1         13 my $self = $class->SUPER::new(%pass);
344              
345 1         33 $self->_init(%setup);
346              
347 1         5 return($self);
348             } # new ################################################################
349              
350              
351             =for internal
352             =head2 _init
353              
354             $self->_init(%setup);
355              
356             =cut
357              
358             sub _init {
359 1     1   2 my $self = shift;
360 1         3 my %setup = @_;
361              
362 1         2 my $fh;
363 1 50       4 if(my $pod = $setup{pod}) {
364 0 0       0 open($fh, '<', \$pod) or croak("cannot open string $!");
365             }
366             else {
367 1   33     5 my $file = $setup{from_file} || (caller(2))[1];
368             # TODO allow searching @INC?
369 1 50       41 open($fh, '<', $file) or croak("cannot open '$file' $!");
370             }
371              
372             # TODO check this against the =for getopt_handler ... case
373 1   33     13 $self->{handler} = $setup{handler} || (caller(2))[0];
374              
375 1         3 $self->{help_order} = [];
376              
377 1         26 my $parser = Getopt::AsDocumented::PodParser->new;
378 1         7 $parser->{__go} = $self;
379 1         3 $parser->{__the_fh} = $fh;
380 1         228 $parser->parse_from_filehandle($fh);
381             } # _init ##############################################################
382              
383             {
384             package Getopt::AsDocumented::PodParser;
385 2     2   13812 use base 'Pod::Parser';
  2         5  
  2         5016  
386              
387             sub command {
388 26     26   48 my ($self, $command, $p) = @_;
389              
390 26         79 $p =~ s/\n+$//;
391              
392             #warn "-- ", $p, "\n";
393 26 100       67 if($command =~ m/^head/) {
394 2 50       13 if($self->{__options}) {
    100          
    50          
395             # done
396 0         0 $self->__store_last;
397 0         0 return seek($self->{__the_fh}, 0, 2);
398             }
399             elsif($p =~ m/^options$/i) {
400 1         2 $self->{__options} = {};
401             }
402             elsif($p =~ m/^usage$/i) {
403             #warn "usage: $p";
404 1         3 $self->{__usage} = $p;
405             }
406 2         94 return;
407             }
408             # hmm, we also need to ditch any directives which aren't in the Usage
409             # or Options sections
410              
411 24 50       530 $self->{__options} or return; # not there yet
412              
413 24 100       55 if($command eq 'item') {
    100          
414 14         31 $self->__store_last;
415              
416 14         46 my %setup;
417              
418             my @opts;
419 14         66 while($p =~ s/^([^ ,]+)(,?)(?: |$)//) {
420 26 100       58 push(@opts, $1); last unless($2);
  26         124  
421             }
422              
423             # number|integer|string type
424 14 100       67 if($p =~ s/ \(([^ ]+)\)$//) { $setup{type} = $1; }
  5         13  
425              
426 14 100       55 $setup{help_bit} = join(', ', @opts) . ($p ? ' '.$p : '');
427              
428             # list/hash form detection
429 14 100       47 if($p =~ s/ \[--[^ ]+ \.\.\.\]$//) {
    100          
430 1         3 $setup{form} = 'ARRAY';
431             }
432             elsif($p =~ m/^\w+=/) {
433 1         2 $setup{form} = 'HASH';
434             }
435              
436             # warn " stuff($p)\n" if($p);
437 14 100       36 $setup{example} = $p if($p);
438 14 100       22 if($p) {
439 9   100     29 $setup{type} ||= 'string';
440             }
441             else {
442 5         10 $setup{type} = 'boolean';
443             }
444              
445             # parse-out the various short and alias forms
446             # the last one is the canonical form
447 14         16 my @short;
448             my @long;
449 14         24 foreach my $opt (@opts) {
450 26 100       78 if($opt =~ s/^--//) {
451 16         30 $opt =~ s/-/_/g;
452 16         46 push(@long, $opt);
453             }
454             else {
455 10 50       47 $opt =~ s/^-// or Carp::croak("'$opt' must have a leading dash");
456 10 50       24 (length($opt) == 1) or Carp::croak("'$opt' malformed");
457 10         22 push(@short, $opt);
458             }
459             }
460              
461 14         26 my $canon = pop(@long);
462              
463 14 100       36 if($canon =~ m/^no_(.*)/) {
464 1         3 my $what = $1;
465 1         2 $setup{opposes} = $what;
466             # implicit 'opposes' -- vs
467             # warn "$canon (@long)- opposes $what\n";
468             #$self->{__go}->add_aliases($canon, \@short, @long);
469             #return;
470             }
471             # warn "canon: $canon\n";
472             # warn "long: @long\n";
473             # warn "short: @short\n";
474 14         28 $setup{aliases} = \@long;
475 14         24 $setup{short} = \@short;
476              
477 14         26 $setup{canon} = $canon;
478 14         19 $self->{__current} = \%setup;
479 14         669 return;
480             }
481             elsif($command eq 'back') {
482 1         9 $self->__store_last;
483 1         144 return;
484             }
485              
486 9 100       65 if($command eq 'for') {
487 8         19 my ($t, @and) = split(/\n=for /, $p);
488              
489 8         17 my %for_items = map({$_ => 1} qw(positional help isa call opposes));
  40         79  
490             my %for_globals = (
491             handler => sub {
492 0     0   0 my $class = shift;
493 0 0       0 unless($class->can('VERSION')) {
494 0         0 eval("require $class");
495 0 0       0 $@ and Carp::croak("cannot load your handler: $@");
496             }
497 0         0 $self->{__go}{handler} = $class;
498             },
499             program_name => sub {
500 0     0   0 $self->{__go}{program_name} = shift;
501             },
502 8         59 );
503              
504              
505 8         22 my ($thing, $val) = split(/ /, $t, 2);
506              
507 8 50       17 if($for_items{$thing}) {
    0          
508 8 50       17 $self->{__current} or Carp::croak("'=for $thing' out of context");
509 8 100       24 $self->{__current}{$thing} = defined($val) ? $val : 1;
510             }
511 0         0 elsif(my $do = $for_globals{$thing}) {
512 0 0       0 $self->{__current} and Carp::croak("'=for $thing' out of context");
513 0         0 $do->($val);
514             }
515             else {warn "unhandled: $t\n"}
516              
517 8         299 $self->command('for' => $_) for(@and);
518             }
519             }
520             sub verbatim {
521 2     2   3 my ($parser, $t) = @_;
522 2 100       50 if(delete($parser->{__usage})) {
523 1         5 $t =~ s/\n+$//;
524 1         48 $parser->{__go}->{usage} = $t;
525             }
526             }
527              
528             sub end_pod {
529 1     1   3 shift->__store_last;
530             }
531             sub textblock {
532 18     18   24 my ($self, $p) = @_;
533              
534 18 50       47 my $s = $self->{__current} or return;
535 18 100       219 if($p =~ m/^DEFAULT(?::|\s*=)\s*(.*)/) {
    100          
536 4         7 my $def = $1;
537              
538 4 50       14 if($def =~ s/^\\//) {
    50          
539             # everything after that is literal
540             }
541             elsif($def =~ s/^\{//) {
542 0 0       0 $def =~ s/\}$// or croak("DEFAULT must have closing brace");
543 0         0 my $sub = eval("sub { $def }");
544 0 0       0 $@ and Carp::croak("error $@\nin DEFAULT block '$def'");
545 0         0 $def = $sub;
546             }
547             else { # normalize it
548 4 100       13 if($def =~ s/^(["'])//) {
549 1         16 $def =~ s/$1$//;
550             }
551             # warn "$s->{canon} $s->{type}\n";
552 4 100       17 if($s->{type} eq 'boolean') {
553 1         12 $def =~ s/^(no|false)$/0/i;
554 1 50       4 $def = 1 if($def);
555             }
556             }
557              
558 4         266 $s->{default} = $def;
559             }
560             elsif(not $s->{help}) {
561             # make help from the first sentence
562 10         37 $p =~ s/\n+$//;
563 10         21 $p = lcfirst($p);
564 10         40 $p =~ s/\.(\)?)( *|$).*/$1/s;
565             # TODO some coverage of this - and what to do about parens?
566             #warn "text: $p\n";
567 10         462 $s->{help} = $p;
568             }
569             }
570              
571             sub __store_last {
572 16     16   21 my $parser = shift;
573              
574 16 100       88 my $setup = delete($parser->{__current}) or return;
575 14 50       39 my $name = delete($setup->{canon}) or die "nothing here";
576 14         25 my $pos = delete($setup->{positional});
577              
578 14         18 my $self = $parser->{__go};
579              
580 14         23 my %auto_actions = map({$_ => 1}
  42         96  
581             qw(help version config_file));
582 14 50       54 if(my $call = $setup->{call}) {
    100          
583 0 0       0 my $handler = $call =~ s/^(.*)::// ? $1 : $self->{handler};
584             # TODO caller should be able to pass handler as an object?
585 0 0       0 $setup->{call} = $handler->can($call) or
586             Carp::croak("'$handler' cannot '$call()'");
587             }
588             elsif($auto_actions{$name}) {
589 3 50       21 $setup->{call} = $self->can($name . '_handler') or
590             Carp::croak("no handler defined for $name");
591             }
592              
593 14         19 push(@{$self->{help_order}}, $name);
  14         31  
594             # TODO ^-- does not work with =for opposes $something
595              
596 14         39 $self->{help_bits}{$name} = delete($setup->{help_bit});
597 14         32 $self->{help}{$name} = delete($setup->{help});
598 14         67 $self->add_option($name, %$setup);
599 14 100       575 $self->add_positionals($name) if($pos);
600             }
601              
602             }
603             ########################################################################
604              
605              
606              
607              
608              
609              
610             =head1 AUTHOR
611              
612             Eric Wilhelm @
613              
614             http://scratchcomputing.com/
615              
616             =head1 BUGS
617              
618             If you found this module on CPAN, please report any bugs or feature
619             requests through the web interface at L. I will be
620             notified, and then you'll automatically be notified of progress on your
621             bug as I make changes.
622              
623             If you pulled this development version from my /svn/, please contact me
624             directly.
625              
626             =head1 COPYRIGHT
627              
628             Copyright (C) 2009 Eric L. Wilhelm, All Rights Reserved.
629              
630             =head1 NO WARRANTY
631              
632             Absolutely, positively NO WARRANTY, neither express or implied, is
633             offered with this software. You use this software at your own risk. In
634             case of loss, no person or entity owes you anything whatsoever. You
635             have been warned.
636              
637             =head1 LICENSE
638              
639             This program is free software; you can redistribute it and/or modify it
640             under the same terms as Perl itself.
641              
642             =cut
643              
644             # vi:ts=2:sw=2:et:sta
645             1;