File Coverage

blib/lib/App/Getconf.pm
Criterion Covered Total %
statement 172 287 59.9
branch 66 130 50.7
condition 19 49 38.7
subroutine 25 34 73.5
pod 23 23 100.0
total 305 523 58.3


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4              
5             App::Getconf - singleton-like config store for command-line applications
6              
7             =head1 SYNOPSIS
8              
9             # main.pl
10              
11             use App::Getconf qw{ :schema };
12             use YAML qw{ LoadFile };
13              
14             App::Getconf->option_schema(
15             help => opt { type => 'flag',
16             help => "this message" },
17             version => opt { type => 'flag',
18             help => "print version information" },
19             verbose => opt { type => 'bool',
20             help => "be verbose" },
21             session => schema(
22             timeout => opt { type => 'int', value => 50 },
23             path => opt { type => 'string', value => '/' },
24             ),
25             # ...
26             );
27              
28             App::Getconf->cmdline(\@ARGV);
29             App::Getconf->options(LoadFile('/etc/myapp.yaml'));
30              
31             if (App::Getconf->getopt->help) {
32             print App::Getconf->help_message();
33             exit 0;
34             }
35              
36             # real code...
37              
38             #-------------------------------------------------
39             # My/Module.pm
40              
41             package My::Module;
42              
43             use App::Getconf;
44              
45             sub do_something {
46             my ($self, %args) = @_;
47              
48             my $opts = App::Getconf->getopt;
49              
50             if ($opts->verbose) {
51             print "Entering function do_something()\n";
52             }
53              
54             # ...
55             }
56              
57             =head1 DESCRIPTION
58              
59             This module is yet another command line options parser. But not only.
60             Actually, it's an option container. It's a response to a question: after
61             parsing options (from command line and from config file), how do I pass them
62             down the function call stack?
63              
64             There are two classic approaches. One utilizes global variables. This is not
65             that convenient, because introduces some names treated in special way (not
66             defined inside the current function). The other requires passing option
67             container as an argument to each and every function (you can't always tell in
68             advance that the function will never use the options on one hand, and API
69             changes are tedious on the other).
70              
71             App::Getconf tries a different way, which is not entirely new: the inspiration
72             for this module was L, which is Perl port of log4j Java
73             library. The idea is simple: you need a value accessible similarly to a global
74             variable, but declared locally.
75              
76             =head1 ARCHITECTURE
77              
78             App::Getconf consists of three different types of objects: option
79             containers, option views and option schema nodes.
80              
81             Option container (App::Getconf instance) stores all the options that were set,
82             either from command line or from multi-level hash (e.g. loaded config file).
83              
84             Option container needs to be initialized with option schema: list of allowed
85             options, along with their types (int, float, string, flag and so on). Such
86             schema is composed of nodes created with C function or derivatives.
87              
88             Option view (L instance) is an interface to options
89             list. When option is requested, view does a "lookup" to find appropriate
90             option. For example, view C<$v> for I subsystem was created.
91             When C<< $v->get('timeout') >> was issued, the view will return value of the
92             first existing option: I, I or
93             I. Of course there's also a possibility to omit this lookup.
94              
95             App::Getconf creates a default option container. This default container is
96             used every time when semi-static method (see L
97             section) is called as static one. This is how App::Getconf provides a way of
98             accessing options globally. However, you are not limited to this default
99             container. You may create your own containers with their own option schema. Of
100             course you will need to pass them down the call stack.
101              
102             =head2 Options Lifecycle
103              
104             Option container needs a schema to tell which options are legal and which are
105             not. Defining schema is basically the first thing to do. Schema can also
106             contain initial values for some options.
107              
108             Next go options defined in command line and in config file. Option container
109             can parse command line on its own, it just needs an array of arguments.
110              
111             Two above steps are only to be done once, at the application start, possibly
112             as early as possible. Changing option values, however, is planned in the
113             future to be supported after initialization process, at run-time.
114              
115             From now on, C method may be used in any part of application.
116              
117             =head2 Schema Definition
118              
119             Schema is simply a hashref that contains options. Each value is a node (actual
120             option or alias) or a sub-schema.
121              
122             Full name of an option from sub-schema is I<$schema.$option>, where
123             C<${schema}> is the key, under which sub-schema was stored. Command line
124             option that sets such option is I<--$schema-$option>.
125              
126             Schemas stored under greater depth are analogous.
127              
128             Example of schema:
129              
130             help => opt { type => 'flag', ... },
131             version => opt { type => 'flag', ... },
132             verbose => opt { type => 'bool', ... },
133             session => {
134             timeout => opt { type => 'int', ... },
135             path => opt { type => 'string', ... },
136             '' => opt { type => 'string', ... },
137             },
138             # ...
139              
140             This schema defines options I, I, I,
141             I, I and just plain I. The last one is
142             example of how to define option of the same name as sub-schema.
143              
144             End-user can set these options using command line options, accordingly:
145             I<--help>, I<--version>, I<--verbose>/I<--no-verbose>,
146             I<--session-timeout=###>, I<--session-path=XXX> and I<--session=XXX>.
147              
148             Basic way of creating node is using C function, but there are few
149             shorthands, like C, C and others. See
150             L section for details.
151              
152             Schema is also used, beside validating option correctness, for generating
153             message printed typically after issuing I<--help> option. Only options having
154             C field are included in this message. Other options still may be set in
155             command line, but are not exposed to the user. They are meant mainly to be
156             specified with configuration file or with other means.
157              
158             Order of options in autogenerated help message is lexicographic order. You may
159             provide the order by changing Perl's built-in anonymous hashref C<{}> to call
160             to function C. Example:
161              
162             # ...
163             session => schema(
164             timeout => opt { type => 'int', ... },
165             path => opt { type => 'string', ... },
166             '' => opt { type => 'string', ... },
167             ),
168             # ...
169              
170             You may freely mix hashrefs and C calls, at the same or different
171             nesting levels.
172              
173             =cut
174              
175             package App::Getconf;
176              
177             #-----------------------------------------------------------------------------
178              
179 10     10   244108 use warnings;
  10         29  
  10         447  
180 10     10   62 use strict;
  10         24  
  10         460  
181              
182 10     10   58 use base qw{Exporter};
  10         22  
  10         1154  
183 10     10   62 use Carp;
  10         22  
  10         847  
184 10     10   6458 use App::Getconf::View;
  10         28  
  10         400  
185 10     10   6726 use App::Getconf::Node;
  10         43  
  10         325  
186 10     10   10100 use Tie::IxHash;
  10         62319  
  10         64003  
187              
188             our $VERSION = '0.20.04';
189              
190             our @EXPORT_OK = qw(
191             schema
192             opt opt_alias
193             opt_flag opt_bool
194             opt_int opt_float
195             opt_string opt_path opt_hostname
196             opt_re opt_sub opt_enum
197             );
198              
199             our %EXPORT_TAGS = (
200             schema => [ 'schema', grep { /^opt/ } @EXPORT_OK ],
201             );
202              
203             #-----------------------------------------------------------------------------
204              
205             my $static = new App::Getconf();
206              
207             #-----------------------------------------------------------------------------
208              
209             =head1 MODULE API
210              
211             Following methods are available:
212              
213             =over
214              
215             =cut
216              
217             #-----------------------------------------------------------------------------
218              
219             =item C
220              
221             Constructor.
222              
223             No options are used at the moment.
224              
225             B: You don't need to use the constructor. You may (and typically would)
226             want to use App::Getconf's default container.
227              
228             =cut
229              
230             sub new {
231 89     89 1 4542 my ($class, %opts) = @_;
232              
233 89         746 my $self = bless {
234             aliases => undef,
235             options => undef,
236             args => undef,
237             help => {
238             message => undef,
239             order => undef,
240             },
241             # each getopt() will return
242             getopt_cache => {},
243             }, $class;
244              
245 89         307 return $self;
246             }
247              
248             #-----------------------------------------------------------------------------
249              
250             =back
251              
252             =head2 Semi-Static Methods
253              
254             Methods from this section can be called as instance methods, when you have
255             created own instance of C, or as static methods, when they
256             operate on default instance of C. Typically you would use the
257             latter strategy, as passing option container down the function call stack is
258             somewhat troublesome.
259              
260             =over
261              
262             =cut
263              
264             #-----------------------------------------------------------------------------
265              
266             =item C
267              
268             =item C<< option_schema(key => value, key => value, ...) >>
269              
270             Set expected schema for the options. Schema may be either a hashref (Perl's
271             ordinary or created using C function) or a list of key/value pairs.
272             The latter form has the same result as passing the list to C first,
273             i.e., the options order will be preserved.
274              
275             =cut
276              
277             sub option_schema {
278 78     78 1 265 my ($self, @args) = @_;
279              
280 78 50       325 my $schema = (@args == 1) ? $args[0] : schema(@args);;
281              
282 78 50       366 $self = $static unless ref $self; # static call or non-static?
283              
284 78         209 my @schema = _flatten($schema, "");
285 78         232 $self->{options} = {};
286 78         168 $self->{aliases} = {};
287 78         186 $self->{help}{order} = [];
288 78         250 for my $opt (@schema) {
289 364 100       1307 if ($opt->{opt}->alias) {
290             # alias option
291              
292 43         177 $self->{aliases}{ $opt->{name} } = $opt->{opt};
293              
294             } else {
295             # normal (non-alias) option
296              
297 321         999 $self->{options}{ $opt->{name} } = $opt->{opt};
298             # remember the order of messages
299 321 50       1002 if ($opt->{opt}->help) {
300 0         0 push @{ $self->{help}{order} }, $opt->{name};
  0         0  
301             }
302             }
303             }
304              
305             # NOTE: this can't be moved to inside the previous loop, because there could
306             # be an alias processed earlier than the option it points to
307 78         149 for my $name (sort keys %{ $self->{aliases} }) {
  78         920  
308 43         151 my $dest = $self->{aliases}{$name}->alias;
309              
310             # option can't be an alias and non-alias at the same time
311 43 50       520 if ($self->{aliases}{$dest}) {
    50          
312 0         0 croak "Alias \"$name\" points to another alias called \"$dest\"";
313             } elsif (not $self->{options}{$dest}) {
314 0         0 croak "Alias \"$name\" points to a non-existent option \"$dest\"";
315             }
316             }
317             }
318              
319             =begin Internal
320              
321             =pod _flatten() {{{
322              
323             =item C<_flatten($root, $path)>
324              
325             Function flattens schema hashref tree to a flat hash, where option names are
326             separated by C<.>.
327              
328             C<$root> is a root of schema hashref tree to convert (recursively).
329             C<$path> is used to keep path so far in recursive call. It should be an empty string initially.
330              
331             Returned value is a hash with two fields: I contains full option path,
332             and I is actual L object.
333              
334             =cut
335              
336             sub _flatten {
337 128     128   287 my ($root, $path) = @_;
338              
339 128 100       454 my @opts = eval { tied(%$root)->isa("Tie::IxHash") } ?
  128         1306  
340             keys %$root :
341             sort keys %$root;
342 128         2706 my @result;
343 128         251 for my $o (@opts) {
344 414 100       3537 if (eval { $root->{$o}->isa("App::Getconf::Node") }) {
  414 50       5248  
345 364         4053 my $name = "$path.$o";
346 364         2513 $name =~ s/^\.|\.$//g;
347 364         2096 push @result, { name => $name, opt => $root->{$o} };
348             } elsif (ref $root->{$o} eq 'HASH') {
349             # XXX: don't try $root->{$o}{""}, it will be collected in the recursive
350             # _flatten() call (note that this may leave trailing period for this
351             # option)
352 50         1345 push @result, _flatten($root->{$o}, "$path.$o");
353             }
354             }
355 128         2435 return @result;
356             }
357              
358             =end Internal
359              
360             =pod }}}
361              
362             =cut
363              
364             #-----------------------------------------------------------------------------
365              
366             =item C
367              
368             Return message printed when I<--help> (or similar) option was passed. Message
369             will be C<\n>-terminated.
370              
371             Typical usage:
372              
373             if (App::Getconf->getopt->help) {
374             print App::Getconf->help_message(
375             screen => 130,
376             synopsis => "%0 [ options ] file ...",
377             );
378             exit 0;
379             }
380              
381             Supported options:
382              
383             =over
384              
385             =item C (default: 80)
386              
387             Screen width, in columns.
388              
389             =item C (default: C<$0> with path stripped)
390              
391             Name of the program. Usually C<$0> or a derivative.
392              
393             =item C (default: C<%0 [ options ... ]>)
394              
395             Short call summary. Any occurrence of C<%0> will be replaced with content of
396             C option.
397              
398             Synopsis may be also a multiline string or an array of single-line strings.
399              
400             =item C
401              
402             =item C
403              
404             =item C
405              
406             Three additional text fields: before synopsis, after synopsis but before
407             options list, after options list.
408              
409             Text will be re-wrapped to fit on a terminal of C width. Empty lines
410             will be treated as paragraph separators, but single newline characters will
411             not be preserved.
412              
413             Any occurrence of C<%0> will be replaced with content of C option.
414              
415             =item C (default: 2)
416              
417             =item C (default: 6)
418              
419             Indenting for option header ("--option" with parameter specification, if any)
420             and for option description.
421              
422             =back
423              
424             =cut
425              
426             sub help_message {
427 0     0 1 0 my ($self, %opts) = @_;
428              
429 0 0       0 $self = $static unless ref $self; # static call or non-static?
430              
431 0   0     0 $opts{screen} ||= 80;
432 0   0     0 $opts{arg0} ||= (split m[/], $0)[-1];
433 0   0     0 $opts{synopsis} ||= "%0 [ options ... ]";
434              
435 0   0     0 $opts{option_indent} ||= 2;
436 0   0     0 $opts{description_indent} ||= 6;
437              
438             # $opts{header} ||= undef;
439             # $opts{description} ||= undef;
440             # $opts{footer} ||= undef;
441              
442 0         0 my $help = "";
443 0         0 my $line;
444             my %format_markers;
445              
446             #---------------------------------------------------------
447             # header {{{
448              
449 0 0       0 if ($opts{header}) {
450 0         0 $line = _reformat($opts{header}, $opts{screen});
451 0         0 $line =~ s/%0/$opts{arg0}/g;
452              
453 0         0 $help .= $line;
454 0         0 $help .= "\n"; # additional empty line
455             }
456              
457             # }}}
458             #---------------------------------------------------------
459             # synopsis {{{
460              
461 0 0       0 if (ref $opts{synopsis} eq 'ARRAY') {
462 0         0 $line = join "\n", @{ $opts{synopsis} };
  0         0  
463             } else {
464 0         0 $line = $opts{synopsis};
465             }
466 0         0 $line =~ s/%0/$opts{arg0}/g;
467              
468 0         0 $line =~ s/\s+$//; # strip leading spaces
469              
470 0 0       0 if ($line =~ /\n./) {
471             # multiline synopsis
472 0         0 $format_markers{multiline_synopsis} = 1;
473              
474 0         0 $line =~ s/^[ \t]*/ /mg; # uniform indentation
475 0         0 $help .= sprintf "Usage:\n%s\n", $line;
476              
477             } else {
478             # single line synopsis
479              
480 0         0 $line =~ s/^\s+//; # strip leading spaces
481 0 0       0 if (length($line) < $opts{screen} - 1 - length("Usage: ")) {
482 0         0 $help .= sprintf "Usage: %s\n", $line;
483             } else {
484 0         0 $format_markers{multiline_synopsis} = 1;
485 0         0 $help .= sprintf "Usage:\n%s\n", $line;
486             }
487              
488             }
489              
490             # }}}
491             #---------------------------------------------------------
492             # description (below synopsis) {{{
493              
494 0 0       0 if ($opts{description}) {
495 0         0 $line = _reformat($opts{description}, $opts{screen});
496 0         0 $line =~ s/%0/$opts{arg0}/g;
497              
498 0         0 $help .= "\n";
499 0         0 $help .= $line;
500              
501 0         0 $format_markers{multiline_synopsis} = 1;
502             }
503              
504             # }}}
505             #---------------------------------------------------------
506             # options {{{
507              
508 0 0 0     0 if ($self->{help}{order} && @{ $self->{help}{order} }) {
  0         0  
509 0         0 $line = "Options available:\n";
510              
511 0         0 for my $opt (@{ $self->{help}{order} }) {
  0         0  
512 0 0       0 my $dash_opt = (length $opt > 1) ? "--$opt" : "-$opt";
513 0         0 $dash_opt =~ tr/./-/;
514              
515 0         0 my $node = $self->option_node($opt);
516              
517 0         0 my $init_val = "";
518 0 0       0 if ($node->has_value) {
519 0         0 $init_val = $node->get;
520 0 0       0 $init_val = "" if not defined $init_val;
521 0         0 $init_val = " (initially: $init_val)";
522             }
523              
524             # option header (indented "--option") {{{
525             # TODO: aliases
526 0 0       0 if ($node->type eq 'flag') {
    0          
    0          
527 0         0 $line .= sprintf "%*s%s\n", $opts{option_indent}, "", $dash_opt;
528             } elsif ($node->type eq 'bool') {
529 0         0 my $neg_dash_opt = "--no-$opt";
530 0         0 $neg_dash_opt =~ tr/./-/;
531 0 0       0 $line .= sprintf "%*s%s, %s\n",
532             $opts{option_indent}, "",
533             ($node->get ?
534             ($neg_dash_opt, $dash_opt) :
535             ($dash_opt, $neg_dash_opt));
536             } elsif ($node->has_default) {
537 0         0 my $type = $node->type;
538 0 0       0 if ($node->enum) {
539 0         0 $type = join "|", @{ $node->enum };
  0         0  
540             }
541 0         0 $line .= sprintf "%*s%s, %s=%s%s\n",
542             $opts{option_indent}, "",
543             $dash_opt,
544             $dash_opt, $type,
545             $init_val;
546             } else {
547 0         0 my $type = $node->type;
548 0 0       0 if ($node->enum) {
549 0         0 $type = join "|", @{ $node->enum };
  0         0  
550             }
551              
552 0         0 $line .= sprintf "%*s%s=%s%s\n",
553             $opts{option_indent}, "",
554             $dash_opt, $type,
555             $init_val;
556             }
557             # }}}
558              
559             # option description (reformatted help message) # {{{
560 0         0 $line .= _reformat(
561             $node->help,
562             $opts{screen}, $opts{description_indent}
563             );
564             # }}}
565             }
566              
567 0 0 0     0 if (_nlines($line) > 16 || $format_markers{multiline_synopsis} ||
      0        
      0        
568             $opts{header} || $opts{description}) {
569             # additional empty line between synopsis and options description
570 0         0 $help .= "\n";
571             }
572              
573 0         0 $help .= $line;
574 0         0 $format_markers{has_options} = 1;
575             }
576              
577             # }}}
578             #---------------------------------------------------------
579             # footer {{{
580              
581 0 0       0 if ($opts{footer}) {
582 0         0 $line = _reformat($opts{footer}, $opts{screen});
583 0         0 $line =~ s/%0/$opts{arg0}/g;
584              
585 0         0 $help .= "\n";
586 0         0 $help .= $line;
587             }
588              
589             # }}}
590             #---------------------------------------------------------
591              
592 0         0 return $help;
593             }
594              
595             =begin Internal
596              
597             =pod _nlines(), _reformat() {{{
598              
599             =item C<_nlines($string)>
600              
601             Calculate number of lines in C<$string>.
602              
603             =cut
604              
605             sub _nlines {
606 0     0   0 my ($str) = @_;
607              
608 0         0 my $nlines =()= ($str =~ /\n/g);
609              
610 0         0 return $nlines;
611             }
612              
613             =item C<_reformat($string, $max_width, $indent)>
614              
615             Reformat a multiparagraph string to include maximum of C<$width-1> characters
616             per line, including indentation.
617              
618             =cut
619              
620             sub _reformat {
621 0     0   0 my ($str, $width, $indent) = @_;
622              
623 0   0     0 $indent ||= 0;
624              
625 0         0 my @result;
626              
627 0         0 $str =~ s/^\s+//;
628 0         0 for my $para (split /\n\s*\n[ \t]*/, $str) {
629 0         0 my $r = "";
630 0         0 my $line = "";
631 0         0 for my $w (split /\s+/, $para) {
632 0 0       0 if ($line eq "") {
    0          
633 0         0 $line = (" " x $indent) . $w;
634             } elsif (length($line) + 1 + length($w) < $width) {
635 0         0 $line .= " " . $w;
636             } else {
637 0         0 $r .= $line . "\n";
638 0         0 $line = (" " x $indent) . $w;
639             }
640             }
641 0         0 $r .= $line . "\n";
642 0         0 push @result, $r;
643             }
644              
645 0         0 return join "\n", @result;
646             }
647              
648             =end Internal
649              
650             =pod }}}
651              
652             =cut
653              
654             #-----------------------------------------------------------------------------
655              
656             =item C
657              
658             Set options read from configuration file (hashref).
659              
660             Example usage:
661              
662             App::Getconf->options(YAML::LoadFile("/etc/myapp.yaml"));
663              
664             =cut
665              
666             sub options {
667 31     31 1 61 my ($self, $options) = @_;
668              
669 31 50       89 $self = $static unless ref $self; # static call or non-static?
670              
671 31         94 $self->set_verify($options);
672             }
673              
674             #-----------------------------------------------------------------------------
675              
676             =item C
677              
678             Set options based on command line arguments (arrayref). If C<$arguments> was
679             not specified, C<@ARGV> is used.
680              
681             Method returns list of messages (single line, no C<\n> at end) for errors that
682             were found, naturally empty if nothing was found.
683              
684             Arguments that were not options can be retrieved using C method.
685              
686             Example usage:
687              
688             App::Getconf->cmdline(\@ARGV);
689             # the same: App::Getconf->cmdline();
690             for my $arg (App::Getconf->args()) {
691             # ...
692             }
693              
694             =cut
695              
696             sub cmdline {
697 41     41 1 82 my ($self, $arguments) = @_;
698              
699 41 50       138 $self = $static unless ref $self; # static call or non-static?
700              
701 41 50       66 my @args = @{ $arguments || \@ARGV };
  41         167  
702 41         75 my @left;
703             my @errors;
704              
705             OPTION:
706 41         238 for (my $i = 0; $i < @args; ++$i) {
707 59         85 my $option;
708             my $option_name;
709 0         0 my $option_arg; # undef only when no argument, with argument at least ""
710              
711 59 100       458 if ($args[$i] =~ /^--([a-zA-Z0-9-]+)=(.*)$/) {
    100          
    50          
    100          
    50          
712             # long option with parameter {{{
713              
714 16         50 $option_name = $1;
715 16         42 $option_arg = $2;
716 16         30 $option = "--$option_name";
717              
718 16         50 push @errors, $self->_try_set($option, $option_name, $option_arg);
719              
720             # }}}
721             } elsif ($args[$i] =~ /^--([a-zA-Z0-9-]+)$/) {
722             # long option, possible parameter in next argument {{{
723              
724 34         97 $option_name = $1;
725 34         65 $option = $args[$i];
726              
727             # there's no option of exactly the same name, but the --option looks
728             # like a negation of Boolean
729 34 100 100     133 if (!$self->has_option($option_name) && $option_name =~ /^no-/) {
730 1         4 my $negated_name = substr $option_name, 3;
731              
732             # there is an option without "--no-" prefix and that option is
733             # a Boolean, so it might be actually negated
734 1 50 33     4 if ($self->has_option($negated_name) &&
735             $self->option_node($negated_name)->type() eq 'bool') {
736 1         2 $option_name = $negated_name;
737 1         2 $option = "--$negated_name";
738 1         3 $option_arg = 0;
739             }
740             }
741              
742 34 100 100     99 if ($self->has_option($option_name) &&
743             $self->option_node($option_name)->requires_arg()) {
744             # consume the next argument, if this is possible; if not, report an
745             # error
746 19 100       191 if ($i < $#args) {
747             # TODO: if $args[++$i] =~ /^-/, don't consume it (require people to
748             # use "--foo=-arg" form)
749 18         45 $option_arg = $args[++$i];
750             } else {
751 1         6 push @errors, {
752             option => $option,
753             cause => "missing argument",
754             };
755             }
756             }
757              
758 34         138 push @errors, $self->_try_set($option, $option_name, $option_arg);
759              
760             # }}}
761             } elsif ($args[$i] =~ /^-([a-zA-Z0-9]+)$/) {
762             # set of short options {{{
763              
764 0         0 my @short_opts = split //, $1;
765              
766 0         0 for my $sopt (@short_opts) {
767             # XXX: short options can't have arguments specified
768 0         0 push @errors, $self->_try_set("-$sopt", $sopt);
769             }
770              
771 0         0 next OPTION;
772              
773             # }}}
774             } elsif ($args[$i] eq "--") {
775             # end-of-options marker {{{
776              
777             # mark all the rest of arguments as non-options
778 2         9 push @left, @args[$i + 1 .. $#args];
779 2         6 last OPTION;
780              
781             # }}}
782             } elsif ($args[$i] =~ /^-/) {
783             # anything beginning with dash (e.g. "-@", "--()&*^&^") {{{
784              
785 0         0 push @errors, {
786             option => $args[$i],
787             cause => "unknown option",
788             };
789              
790             # }}}
791             } else {
792             # non-option {{{
793              
794 7         13 push @left, $args[$i];
795 7         24 next OPTION;
796              
797             # }}}
798             }
799             }
800              
801 41         97 $self->{args} = \@left;
802              
803 41 100       108 if (@errors) {
804             # TODO: use $_->{"eval"}
805 5         13 return map { "$_->{option}: $_->{cause}" } @errors;
  6         45  
806             } else {
807 36         137 return;
808             }
809             }
810              
811             #-----------------------------------------------------------------------------
812              
813             =item C
814              
815             Check if the schema contains a command line option called C<$name> (aliases
816             are resolved).
817              
818             B: This is a semi-internal API.
819              
820             =cut
821              
822             sub has_option {
823 194     194 1 392 my ($self, $name) = @_;
824              
825 194 50       487 $self = $static unless ref $self; # static call or non-static?
826              
827 194         346 $name =~ tr/-/./;
828              
829 194   100     1442 return defined $self->{options}{$name} || defined $self->{aliases}{$name};
830             }
831              
832             =item C
833              
834             Retrieve an option node (L) corresponding to C<$name>.
835              
836             Method Cs when no such option is defined in schema.
837              
838             B: This is a semi-internal API.
839              
840             =cut
841              
842             sub option_node {
843 127     127 1 205 my ($self, $name) = @_;
844              
845 127 50       375 $self = $static unless ref $self; # static call or non-static?
846              
847 127         167 $name =~ tr/-/./;
848              
849 127 100       404 if ($self->{options}{$name}) {
850 120         575 return $self->{options}{$name};
851             }
852              
853 7 50       21 if ($self->{aliases}{$name}) {
854 7         27 my $target = $self->{aliases}{$name}->alias;
855 7         29 return $self->{options}{$target};
856             }
857              
858 0         0 croak "No option called $name in schema";
859             }
860              
861             =begin Internal
862              
863             =pod _try_set() {{{
864              
865             =item C<_try_set($option, $option_name, $option_argument)>
866              
867             Try setting option C<$option_name> (C<$option> was the actual name, under
868             which it was specified -- mainly I<-X> or I<--long-X>). If the option was
869             given a parameter (empty string counts here, too), it should be specified as
870             C<$option_argument>, otherwise C<$option_argument> should be left C.
871              
872             In case of success, returned value is empty list. In case of failure,
873             returned value is a hashref with two keys: I
874             I containing an error message. There could be third key I,
875             containing C<$@>. Method is suitable for
876             C<< push @errors, $o->_try_set(...) >>.
877              
878             =cut
879              
880             sub _try_set {
881 50     50   115 my ($self, $option, $opt_name, $opt_arg) = @_;
882              
883 50 100       115 if (not $self->has_option($opt_name)) {
884             return {
885 1         7 option => $option,
886             cause => "unknown option",
887             };
888             }
889              
890 49         136 my $node = $self->option_node($opt_name);
891              
892 49 100       129 if (defined $opt_arg) {
893 35 100       54 if (not eval { $node->set($opt_arg); "OK" }) {
  35         173  
  32         123  
894 3         197 chomp $@;
895             return {
896 3         32 option => $option,
897             cause => "invalid option argument: $opt_arg",
898             eval => $@,
899             };
900             }
901             } else { # not defined $opt_arg
902             # XXX: this is important not to pass an argument to $node->set() here, as
903             # it would try to set undef
904 14 100       26 if (not eval { $node->set(); "OK" }) {
  14         201  
  13         45  
905 1         151 chomp $@;
906             return {
907 1         15 option => $option,
908             cause => "invalid option argument: ",
909             eval => $@,
910             };
911             }
912             }
913              
914 45         306 return ();
915             }
916              
917             =end Internal
918              
919             =pod }}}
920              
921             =cut
922              
923             #-----------------------------------------------------------------------------
924              
925             =item C
926              
927             =item C
928              
929             Set value(s) with verification against schema. If C<$path> was specified,
930             options start with this prefix. If values were verified successfully, they are
931             saved in internal storage.
932              
933             B: This is a semi-internal API.
934              
935             =cut
936              
937             sub set_verify {
938 75     75 1 130 my ($self, $data, $path) = @_;
939              
940 75 50       300 $self = $static unless ref $self; # static call or non-static?
941              
942 75   100     259 $path ||= "";
943              
944 75   100     276 my $datum_type = lc(ref $data) || "scalar";
945              
946 75 100       263 if ($datum_type ne 'hash') {
947             # this is an option, but there's no corresponding schema node
948 34 50       72 if (not $self->has_option($path)) {
949             # $path: unknown option ($datum_type)
950 0         0 croak "Unexpected $datum_type option ($path)";
951             }
952              
953 34         107 $self->option_node($path)->set($data);
954              
955 30         115 return;
956             }
957              
958             # more complex case: data is a hash
959              
960             # if no corresponding node in schema, just go deeper
961             # if there is corresponding node, but it's not a hash, just go deeper, too
962 41 100 66     109 if (!$self->has_option($path) ||
963             $self->option_node($path)->storage() ne 'hash') {
964 36         123 for my $o (keys %$data) {
965 44         97 my $new_path = "$path.$o";
966 44         267 $new_path =~ s/^\.|\.$//g;
967              
968 44         154 $self->set_verify($data->{$o}, $new_path);
969             }
970              
971 32         109 return;
972             }
973              
974             # it's sure that option called $path exists and it's storage type is "hash"
975             # also, this option's type is hash
976              
977 5         15 my $node = $self->option_node($path);
978 5         14 for my $k (keys %$data) {
979 7         23 $node->set($k, $data->{$k});
980             }
981             }
982              
983             #-----------------------------------------------------------------------------
984              
985             =item C
986              
987             Retrieve non-option arguments (e.g. everything after "--") passed from command
988             line.
989              
990             Values returned by this method are set by C method.
991              
992             =cut
993              
994             sub args {
995 6     6 1 33 my ($self) = @_;
996              
997 6 50       18 $self = $static unless ref $self; # static call or non-static?
998              
999 6         9 return @{ $self->{args} };
  6         51  
1000             }
1001              
1002             #-----------------------------------------------------------------------------
1003              
1004             =item C
1005              
1006             Retrieve a view of options (L) appropriate for
1007             package or subsystem called C<$package>.
1008              
1009             If C<$package> was not provided, caller's package name is used.
1010              
1011             C<$package> sets option search path. See C, C option
1012             description in L for details.
1013              
1014             Typical usage:
1015              
1016             sub foo {
1017             my (@args) = @_;
1018              
1019             my $opts = App::Getconf->getopt(__PACKAGE__);
1020              
1021             if ($opts->ssl) {
1022             # ...
1023              
1024             =cut
1025              
1026             sub getopt {
1027 5     5 1 1034 my ($self, $package) = @_;
1028              
1029 5 50       24 $self = $static unless ref $self; # static call or non-static?
1030 5 100       20 if (not defined $package) {
1031 1         3 $package = caller;
1032 1 50 33     13 if (!defined $package || $package eq 'main') {
1033 1         22 $package = '';
1034             }
1035             }
1036              
1037 5         17 $package =~ s{/|::}{.}g;
1038 5         24 $package = lc $package;
1039              
1040 5 50       31 if (not $self->{getopt_cache}{$package}) {
1041 5         34 $self->{getopt_cache}{$package} = new App::Getconf::View(
1042             prefix => $package,
1043             options => $self->{options},
1044             );
1045             }
1046              
1047 5         20 return $self->{getopt_cache}{$package};
1048             }
1049              
1050             #-----------------------------------------------------------------------------
1051              
1052             =back
1053              
1054             =cut
1055              
1056             #-----------------------------------------------------------------------------
1057              
1058             =head2 Functions Defining Schema
1059              
1060             =over
1061              
1062             =cut
1063              
1064             #-----------------------------------------------------------------------------
1065              
1066             =item C<< schema(key => value, key => value, ...) >>
1067              
1068             Create a hashref from key/value pairs. The resulting hash is tied to
1069             L, so the order of keys is preserved.
1070              
1071             Main use is for defining order of options in I<--help> message, otherwise it
1072             acts just like anonymous hashref creation (C<< { key => value, ... } >>).
1073              
1074             =cut
1075              
1076             sub schema {
1077 81     81 1 678 my (@args) = @_;
1078              
1079 81         550 tie my %h, 'Tie::IxHash';
1080 81         1532 %h = @args;
1081              
1082 81         6264 return \%h;
1083             }
1084              
1085             #-----------------------------------------------------------------------------
1086              
1087             =item C
1088              
1089             Generic option specification.
1090              
1091             Possible data:
1092              
1093             opt {
1094             type => 'flag' | 'bool' | 'int' | 'float' | 'string',
1095             check => qr// | sub {} | ["enum", "value", ...],
1096             storage => undef | \$foo | [] | {},
1097             help => "message displayed on --help",
1098             value => "initial value",
1099             default => "default value",
1100             }
1101              
1102             If type is not specified, the option is treated as a string.
1103              
1104             Check is for verifying correctness of specified option. It may be a regexp,
1105             callback function (it gets the value to check as a first argument and in C<$_>
1106             variable) or list of possible string values.
1107              
1108             Types of options:
1109              
1110             =over
1111              
1112             =item C
1113              
1114             Simple option, like I<--help> or I<--version>. Flag's value tells how many
1115             times it was encountered.
1116              
1117             =item C
1118              
1119             ON/OFF option. May be turned on (I<--verbose>) or off (I<--no-verbose>).
1120              
1121             =item C
1122              
1123             Option containing an integer.
1124              
1125             =item C
1126              
1127             Option containing a floating point number.
1128              
1129             =item C
1130              
1131             Option containing a string. This is the default.
1132              
1133             =back
1134              
1135             Storage tells if the option is a single-value (default), multi-value
1136             accumulator (e.g. may be specified in command line multiple times, and the
1137             option arguments will be stored in an array) or multi-value hash accumulator
1138             (similar, but option argument is specified as C, and the value part
1139             is validated). Note that this specify only type of storage, not the actual
1140             container.
1141              
1142             B: Don't specify option with a hash storage and that has sub-options
1143             (see L). Verification can't tell whether the value is
1144             meant for the hash under this option or for one of its sub-options.
1145              
1146             Presence of C key indicates that this option should be exposed to
1147             end-users in I<--help> message. Options lacking this key will be skipped (but
1148             stil honoured by App::Getconf).
1149              
1150             Except for flags (I<--help>) and bool (I<--no-verbose>) options, the rest of
1151             types require an argument. It may be specified as I<--timeout=120> or as
1152             I<--timeout 120>. This requirement may be loosened by providing
1153             C value. This way end-user may just provide I<--timeout> option, and
1154             the argument to the option is taken from default. (Of course, only
1155             I<--timeout=120> form is supported if the argument needs to be provided.)
1156              
1157             Initial value (C key) is the value set for the option just after
1158             defining schema. It may or may not be changed with command line options (which
1159             is different from C, for which the option still needs to be
1160             specified).
1161              
1162             Initial and default values are both subject to check that was specified, if
1163             any.
1164              
1165             Help message will not retain any formatting, all whitespaces are converted to
1166             single space (empty lines are squeezed to single empty line). On the other
1167             hand, the message will be pretty wrapped and indented, while you don't need to
1168             worry about formatting the string if it is longer and broken to separate lines
1169             in your source code, so I think it's a good trade-off.
1170              
1171             =cut
1172              
1173             sub opt($) {
1174 322     322 1 1559 my ($data) = @_;
1175              
1176 322   100     1285 my $type = $data->{type} || "string";
1177 322         515 my $check = $data->{check};
1178 322         576 my $storage = $data->{storage};
1179 322         483 my $help = $data->{help};
1180 322         550 my $value = $data->{value}; # not necessary, but kept for convention
1181 322         470 my $default = $data->{default}; # not necessary, but kept for convention
1182              
1183 322 100       717 if (ref $storage) {
1184             # make sure the store is not a reference to something outside of this
1185             # function
1186 30 100       170 if (ref $storage eq 'ARRAY') {
    50          
    0          
1187 15         31 $storage = 'array';
1188             } elsif (ref $storage eq 'HASH') {
1189 15         26 $storage = 'hash';
1190             } elsif (ref $storage eq 'SCALAR') {
1191 0         0 $storage = 'scalar';
1192             } # TODO: else die?
1193             } else {
1194 292         460 $storage = 'scalar';
1195             }
1196              
1197 322 100       2350 return new App::Getconf::Node(
    100          
1198             type => $type,
1199             check => $check,
1200             storage => $storage,
1201             help => $help,
1202             # XXX: this way undefs are possible to represent as undefs
1203             (exists $data->{value} ? (value => $value ) : ()),
1204             (exists $data->{default} ? (default => $default) : ()),
1205             );
1206             }
1207              
1208             =item C
1209              
1210             Create an alias for C<$option>. Note that aliases are purely for command line.
1211             L and C method don't honour them.
1212              
1213             Aliases may only point to non-alias options.
1214              
1215             =cut
1216              
1217             sub opt_alias($) {
1218 43     43 1 70 my ($dest_option) = @_;
1219              
1220 43         200 return new App::Getconf::Node(alias => $dest_option);
1221             }
1222              
1223             =item C
1224              
1225             Flag option (like I<--help>, I<--verbose> or I<--debug>).
1226              
1227             =cut
1228              
1229             sub opt_flag() {
1230 18     18 1 113 return opt { type => 'flag' };
1231             }
1232              
1233             =item C
1234              
1235             Boolean option (like I<--recursive>). Such option gets its counterpart
1236             called I<--no-${option}> (mentioned I<--recursive> gets I<--no-recursive>).
1237              
1238             =cut
1239              
1240             sub opt_bool() {
1241 1     1 1 4 return opt { type => 'bool' };
1242             }
1243              
1244             =item C
1245              
1246             Integer option (I<--retries=3>).
1247              
1248             =cut
1249              
1250             sub opt_int() {
1251 8     8 1 29 return opt { type => 'int' };
1252             }
1253              
1254             =item C
1255              
1256             Option specifying a floating point number.
1257              
1258             =cut
1259              
1260             sub opt_float() {
1261 0     0 1 0 return opt { type => 'float' };
1262             }
1263              
1264             =item C
1265              
1266             Option specifying a string.
1267              
1268             =cut
1269              
1270             sub opt_string() {
1271 20     20 1 83 return opt { type => 'string' };
1272             }
1273              
1274             =item C
1275              
1276             Option specifying a path in local filesystem.
1277              
1278             =cut
1279              
1280             sub opt_path() {
1281             # TODO: some checks on how this looks like
1282             # * existing file
1283             # * existing directory
1284             # * non-existing file (directory exists)
1285             # * Maasai?
1286 0     0 1   return opt { type => 'string' };
1287             }
1288              
1289             =item C
1290              
1291             Option specifying a hostname.
1292              
1293             B: This doesn't check DNS for the hostname to exist. This only checks
1294             hostname's syntactic correctness (and only to some degree).
1295              
1296             =cut
1297              
1298             sub opt_hostname() {
1299 0     0 1   return opt { check => qr/^[a-z0-9-]+(\.[a-z0-9-]+)*$/i };
1300             }
1301              
1302             =item C
1303              
1304             Option specifying a string, with check specified as regexp.
1305              
1306             =cut
1307              
1308             sub opt_re($) {
1309 0     0 1   my ($re) = @_;
1310              
1311 0           return opt { check => $re };
1312             }
1313              
1314             =item C
1315              
1316             =item C
1317              
1318             Option specifying a string, with check specified as function (code ref).
1319              
1320             Subroutine will have C<$_> set to value to check, and the value will be the
1321             only argument (C<@_>) passed.
1322              
1323             Subroutine should return C when option value should be accepted,
1324             C otherwise.
1325              
1326             =cut
1327              
1328             sub opt_sub(&) {
1329 0     0 1   my ($sub) = @_;
1330              
1331 0           return opt { check => $sub };
1332             }
1333              
1334             =item C
1335              
1336             Option specifying a string. The string must be one of the specified in the
1337             array.
1338              
1339             =cut
1340              
1341             sub opt_enum($) {
1342 0     0 1   my ($choices) = @_;
1343              
1344 0           return opt { check => $choices };
1345             }
1346              
1347             #-----------------------------------------------------------------------------
1348              
1349             =back
1350              
1351             =cut
1352              
1353             #-----------------------------------------------------------------------------
1354              
1355             =head1 AUTHOR
1356              
1357             Stanislaw Klekot, C<< >>
1358              
1359             =head1 LICENSE AND COPYRIGHT
1360              
1361             Copyright 2012 Stanislaw Klekot.
1362              
1363             This program is free software; you can redistribute it and/or modify it
1364             under the terms of either: the GNU General Public License as published
1365             by the Free Software Foundation; or the Artistic License.
1366              
1367             See http://dev.perl.org/licenses/ for more information.
1368              
1369             =head1 SEE ALSO
1370              
1371             L, L, L.
1372              
1373             =cut
1374              
1375             #-----------------------------------------------------------------------------
1376             1;
1377             # vim:ft=perl:foldmethod=marker