cvsuser     04/01/30 07:15:07

  Modified:    App-Options CHANGES Makefile.PL TODO
               App-Options/lib/App Options.pm
  Log:
  dramatically improved documentation, minor bug fixes and feature enhancements
  
  Revision  Changes    Path
  1.3       +8 -0      p5ee/App-Options/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/CHANGES,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -w -r1.2 -r1.3
  --- CHANGES   19 Jan 2004 14:51:40 -0000      1.2
  +++ CHANGES   30 Jan 2004 15:15:07 -0000      1.3
  @@ -2,6 +2,14 @@
   # CHANGE LOG
   #############################################################################
   
  +VERSION 0.63
  + x improve documentation (api reference, logic flow, usage tutorial)
  + x "integer" type now matches integers with underscores (i.e. 1_000_000)
  + x "float" type now matches numbers with underscores (i.e. 1_000.000_001)
  + x added /etc/app/app.conf to the end of the option file search path
  + x default "prefix" is now "/usr/local" instead of "."
  + x {env} attribute of "" means don't search any env variable
  +
   VERSION 0.62
    x [prog] matches "prog" only. [/prog/] matches by regular expression.
      In version 0.61, the section [list] would match ($app =~ /list/).
  
  
  
  1.4       +2 -2      p5ee/App-Options/Makefile.PL
  
  Index: Makefile.PL
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/Makefile.PL,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -w -r1.3 -r1.4
  --- Makefile.PL       19 Jan 2004 14:51:40 -0000      1.3
  +++ Makefile.PL       30 Jan 2004 15:15:07 -0000      1.4
  @@ -1,6 +1,6 @@
   
   ######################################################################
  -## File: $Id: Makefile.PL,v 1.3 2004/01/19 14:51:40 spadkins Exp $
  +## File: $Id: Makefile.PL,v 1.4 2004/01/30 15:15:07 spadkins Exp $
   ######################################################################
   
   use ExtUtils::MakeMaker;
  @@ -14,7 +14,7 @@
   %opts = (
       'NAME'        => 'App-Options',
       'DISTNAME'    => 'App-Options',
  -    'VERSION'     => '0.62',
  +    'VERSION'     => '0.63',
       'EXE_FILES'   => [ @programs ],
       'dist'        => {'COMPRESS'=>'gzip -9f', 'SUFFIX' => 'gz',
                         'ZIP'=>'/usr/bin/zip','ZIPFLAGS'=>'-rl'},
  
  
  
  1.4       +16 -8     p5ee/App-Options/TODO
  
  Index: TODO
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/TODO,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -w -r1.3 -r1.4
  --- TODO      19 Jan 2004 14:51:40 -0000      1.3
  +++ TODO      30 Jan 2004 15:15:07 -0000      1.4
  @@ -1,16 +1,24 @@
   ######################################################################
  -## File: $Id: TODO,v 1.3 2004/01/19 14:51:40 spadkins Exp $
  +## File: $Id: TODO,v 1.4 2004/01/30 15:15:07 spadkins Exp $
   ######################################################################
  +
  +These two items are what will be required to go to version 1.00.
  + o figure out a way to do it outside the BEGIN block (i.e. use App::Options 
qw(:init))
  +
  +These are other interesting things
  + o make lots more tests
  + o improve debug_options (env vars, etc.)
    o enforce other option parsing rules (single letter + arg, single/double dash)
    o option aliases/synonyms/alternates (i.e. -s = --silent)
  - o "strict" option: 0 = no strictness
  -                    1 = [most strict] options from file not defined by program 
cause errors
  -                    2 = silently don't include options from file not defined by 
program
  -                    3 = unknown options cause error (program only can define 
options)
  -                    4 = [slightly strict] unknown options cause error (file can 
define options),
  + o "secure" attribute
  + o "strict" option:
  +       0 = no strictness
  +       1 = unknown options cause error (file can define options),
  +       2 = [1] + unknown options cause error (program only can define options)
  +       3 = [1+2] + silently don't include options from file not defined by program
  +       4 = [1+2+3] + options from file not defined by program cause errors
    o write "prefix.pod"
  - o figure out a way to do it outside the BEGIN block (i.e. use App::Options 
qw(:init))
    o here documents, var = <<EOF
  - o try use lib "dir"; instead of unshift(@INC,"dir")
  + o try use lib "dir"; instead of unshift(@INC,"dir") (interaction with "arch")
    o consider checking the PERL5LIB variable under -T
   
  
  
  
  1.4       +928 -115  p5ee/App-Options/lib/App/Options.pm
  
  Index: Options.pm
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/lib/App/Options.pm,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -w -r1.3 -r1.4
  --- Options.pm        19 Jan 2004 14:51:41 -0000      1.3
  +++ Options.pm        30 Jan 2004 15:15:07 -0000      1.4
  @@ -1,6 +1,6 @@
   
   #############################################################################
  -## $Id: Options.pm,v 1.3 2004/01/19 14:51:41 spadkins Exp $
  +## $Id: Options.pm,v 1.4 2004/01/30 15:15:07 spadkins Exp $
   #############################################################################
   
   package App::Options;
  @@ -19,45 +19,43 @@
   
       BEGIN {
           use App::Options;
  -        App::Options->init();  # reads into %App::options by default
  +        App::Options->init();  # reads option values into %App::options by default
       }
   
  -    print "Options:\n";
  -    foreach $var (sort keys %App::options) {
  -        printf "    %-20s => [%s]\n", $var, $App::options{$var};
  -    }
  +    # do something with the options (in %App::options)
  +    use DBI;
  +    $dsn = "dbi:mysql:database=$App::options{dbname}";
  +    $dbh = DBI->connect($dsn, $App::options{dbuser}, $App::options{dbpass});
  +    ...
   
  -  or a more full-featured example...
  +  Get help from the command line (assuming program is named "prog") ...
   
  -    #!/usr/local/bin/perl
  +    prog -?
   
  -    BEGIN {
  -        use App::Options;
  -        App::Options->init(
  -            values => \%MyPackage::some_hash,
  -            options => [ "option_file", "prefix", "app", "app_path_info",
  -                        "perlinc", "debug_options", "import", ],
  -            option => {
  -                option_file   => "~/.app/app.conf",         # set default
  -                app           => "default=app;type=string", # default & type
  -                app_path_info => {default=>"",type=>"string"}, # as a hashref
  -                prefix        => "type=string;required;env=PREFIX",
  -                perlinc       => undef,         # no default
  -                debug_options => "type=integer",
  -                import        => "type=string",
  -                flush_imports => 1,
  -            },
  -            no_cmd_args => 1,
  -            no_env_vars => 1,
  -            no_option_file => 1,
  -            print_usage => sub { my ($values, $init_options) = @_; print "Use it 
right!\n"; },
  -        );
  -    }
  +  Option values may be provided on the command line, in environment
  +  variables, and option files.  (i.e. $ENV{APP_DBNAME} would set
  +  the value of %App::options{dbname} by default.)
   
  -    print "Options:\n";
  -    foreach $var (sort keys %MyPackage::some_hash) {
  -        printf "    %-20s => [%s]\n", $var, $MyPkg::my_hash{$var};
  -    }
  +  The "dbname" and other options could also be set in one of the
  +  following configuration files
  +
  +    $HOME/.app/prog.conf
  +    $HOME/.app/app.conf
  +    $PROGDIR/prog.conf
  +    $PROGDIR/app.conf
  +    $PREFIX/prog.conf
  +    $PREFIX/app.conf
  +    /etc/app/app.conf
  +
  +  with a file format like
  +
  +    [prog]
  +    dbname = prod
  +    dbuser = scott
  +    dbpass = tiger
  +
  +  See below for a more detailed explanation of these and other
  +  advanced features.
   
   =head1 DESCRIPTION
   
  @@ -65,27 +63,60 @@
   option files, and program defaults to produce a hash of
   option values.
   
  -All of this may be done within the BEGIN block so that the @INC variable
  -can be modified in time to affect "use" statements within the 
  -regular code.  This is particularly important to support the installation
  -of multiple versions of a Perl application on the same physical computer.
  +=head1 RELATION TO OTHER CONFIGURATION/OPTION PARSING MODULES
  +
  +A number of modules are posted on CPAN which do command-line
  +processing.
  +
  + http://search.cpan.org/modlist/Option_Parameter_Config_Processing
   
  -App::Options supports the P5EE/App-Context variant of the Perl 5 Enterprise
  -Environment.  See the P5EE web sites for more information.
  +App::Options is different than most of the Getopt::* modules
  +because it integrates the processing of command line options,
  +environment variables, and config files.
  +
  +It is different from AppConfig (to which its description bears the
  +most resemblance) by its ability to configure a suite of programs
  +with a cascading set of configuration files. This allows for a large
  +number of programs in a large software system to share a small set
  +of configuration files. Some of the option values may be shared, and
  +some may be targetted at a single program or a pattern-matched set
  +of programs.
  +
  +Furthermore, its special treatment of the "perlinc"
  +option facilitates the inclusion ("use") of application-specific
  +perl modules from special places to enable the installation of
  +multiple versions of an application on the same system (i.e.
  +/usr/myproduct/version).
  +
  +App::Options is also the easiest command-line processing system
  +that I have found anywhere. It then provides a smooth transition to
  +more advanced features only as they are needed.  Every single
  +quick and dirty script I ever write from now on can afford
  +to use App::Options.
  +
  +The documentation of App::Options takes three
  +forms below.
  +
  +  Reference - describing the API (methods, args)
  +  Flow - describing the order and logic of processing
  +  Tutorial - describing how to use the API in practical situations
  +
  +Hopefully these will be complementary and useful, even if they
  +are somewhat redundant.
  +
  +=head1 RELATION TO THE P5EE PROJECT
  +
  +App::Options was motivated by and supports the P5EE/App-Context variant
  +of the Perl 5 Enterprise Environment (P5EE).  However, App::Options has no
  +dependency on any other module in the P5EE project, and it is very useful
  +without any knowledge or use of other elements of the P5EE project.
  +
  +See the P5EE web sites for more information on the P5EE project.
   
       http://www.officevision.com/pub/p5ee
       http://p5ee.perl.org
   
  -App::Options is in its own distribution because it will be very stable
  -and can be installed in the default perl places on the system.
  -This is different than the App-Context, App-Repository, and App-Widget
  -distributions which are expected to evolve significantly.
  -
  -A developer writing an application based on the P5EE/App-Context framework
  -will want to install App-Options in the default perl places.  The other
  -distributions will be installed in release-specific locations.
  -
  -=head1 Methods
  +=head1 REFERENCE: Methods
   
   =cut
   
  @@ -99,11 +130,34 @@
       * Signature: App::Options->init(%named);
       * Signature: App::Options->init($myvalues);
       * Signature: App::Options->init($myvalues, %named);
  +     (NOTE: %named represents a list of name/value pairs used as named args.
  +            Params listed below without a $ are named args.)
       * Param:  $myvalues     HASH
  +              specify a hash reference other than %App::options to put
  +              configuration values in.
       * Param:  values        HASH
  +              specify a hash reference other than %App::options to put
  +              configuration values in.
  +    * Param:  options       ARRAY
  +              specify a limited, ordered list of options to be displayed
  +              when the "--help" or "-?" options are invoked
  +    * Param:  option        HASH
  +              specify additional attributes of any of
  +              the various options to the program (see below)
  +    * Param:  no_cmd_args 
  +              do not process command line arguments
  +    * Param:  no_env_vars 
  +              do not read environment variables
  +    * Param:  no_option_file 
  +              do not read in the option file(s)
  +    * Param:  print_usage 
  +              provide an alternate print_usage() function
       * Return: void
  -    * Throws: <none>
  -    * Since:  0.01
  +    * Throws: "App::Options->init(): must have an even number of vars/values for 
named args"
  +    * Throws: "App::Options->init(): 'values' arg must be a hash reference"
  +    * Throws: "App::Options->init(): 'option' arg must be a hash reference"
  +    * Throws: "App::Options->init(): 'options' arg must be an array reference"
  +    * Since:  0.60
   
       Sample Usage: 
   
  @@ -133,20 +187,20 @@
               no_cmd_args => 1,
               no_env_vars => 1,
               no_option_file => 1,
  -            print_usage => sub { my ($values, $init_options) = @_; print "Use it 
right!\n"; },
  +            print_usage => sub { my ($values, $init_args) = @_; print "Use it 
right!\n"; },
           );
       }
   
   The init() method reads the command line args (@ARGV),
  -finds an options file, and loads it, all in a way which
  +then finds an options file, and loads it, all in a way which
   can be done in a BEGIN block.  This is important to be able
   to modify the @INC array so that normal "use" and "require"
   statements will work with the configured @INC path.
   
  -The various named parameters of the init() method are as follows.
  +The following named arguments are understood by the init() method.
   
       values - specify a hash reference other than %App::options to
  -        put configuration values in.
  +             put option values in.
       options - specify a limited, ordered list of options to be
           displayed when the "--help" or "-?" options are invoked
       option - specify optional additional information about any of
  @@ -154,32 +208,35 @@
       no_cmd_args - do not process command line arguments
       no_env_vars - do not read environment variables
       no_option_file - do not read in the option file
  +    show_all - force showing all options in "--help" even when
  +             "options" list specified
       print_usage - provide an alternate print_usage() function
   
   The additional information that can be specified about any individual
  -option variable is as follows.
  +option variable using the "option" arg above is as follows.
   
  -    default - the default variable if none supplied on the command
  +    default - the default value if none supplied on the command
           line, in an environment variable, or in an option file
       required - the program will not run unless a value is provided
           for this option
       type - if a value is provided, the program will not run unless
           the value matches the type ("string", "integer", "float",
           "boolean", "date", "time", "datetime", /regexp/).
  +    env - a list of semicolon-separated environment variable names
  +        to be used to find the value instead of "APP_{VARNAME}".
       description - printed next to the option in the "usage" page
   
   The init() method stores command line options and option
   file values all in the global %App::options hash (unless the
   "values" argument specifies another reference to a hash to use).
  -The special keys to this resulting hash are as follows.
   
  -    option_file - specifies the exact file name of the option file useful
  -       for command line usage (i.e. "app --option_file=/path/to/app.conf")
  -       "option_file" is automatically set with the option file that it found
  -       if it is not supplied at the outset as an argument.
  +The special options are as follows.
  +
  +    option_file - specifies the exact file name of the option file to be
  +       used (i.e. "app --option_file=/path/to/app.conf").
   
       app - specifies the tag that will be used when searching for
  -       a option file. (i.e. "app --app=myapp" will search for "myapp.conf"
  +       an option file. (i.e. "app --app=myapp" will search for "myapp.conf"
          before it searches for "app.conf")
          "app" is automatically set with the stem of the program file that 
          was run (or the first part of PATH_INFO) if it is not supplied at
  @@ -193,7 +250,7 @@
          set explicitly, it is detected from the following places:
             1. PREFIX environment variable
             2. the real path of the program with /bin or /cgi-bin stripped
  -          3. the current directory
  +          3. /usr/local
          If it is autodetected from one of those three places, that is
          only provisional, in order to find the "option_file".  The "prefix"
          variable should be set authoritatively in the "option_file" if it
  @@ -208,7 +265,11 @@
          which option files are being used and what the resulting variable
          values are.
   
  -    import - a list of additional option files to be processed
  +    import - a list of additional option files to be processed.
  +       An imported file goes on the head of the queue of files to be
  +       processed.
  +
  +    flush_imports - flush all pending imported option files.
   
   =cut
   
  @@ -220,14 +281,14 @@
       my $values = ($#_ > -1 && ref($_[0]) eq "HASH") ? shift : \%App::options;
   
       ($#_ % 2 == 1) || croak "App::Options->init(): must have an even number of 
vars/values for named args";
  -    my %init_options = @_;
  +    my %init_args = @_;
       # "values" in named arg list overrides the one supplied as an initial hashref
  -    if (defined $init_options{values}) {
  -        (ref($init_options{values}) eq "HASH") || croak "App::Options->init(): 
'values' arg must be a hash reference";
  -        $values = $init_options{values};
  +    if (defined $init_args{values}) {
  +        (ref($init_args{values}) eq "HASH") || croak "App::Options->init(): 
'values' arg must be a hash reference";
  +        $values = $init_args{values};
       }
       else {
  -        $init_options{values} = $values;
  +        $init_args{values} = $values;
       }
   
       #######################################################################
  @@ -235,7 +296,7 @@
       #######################################################################
   
       my ($var, $value, @vars, $option);
  -    $option = $init_options{option};
  +    $option = $init_args{option};
   
       if ($option) {
           croak "App::Options->init(): 'option' arg must be a hash reference"
  @@ -278,13 +339,16 @@
       # an option without an "=" (i.e. --help) acts as --help=1
       # Put the var/value pairs in %$values
       #################################################################
  -    if (! $init_options{no_cmd_args}) {
  +    if (! $init_args{no_cmd_args}) {
           while ($#ARGV >= 0 && $ARGV[0] =~ /^--?([^=-][^=]*)(=?)(.*)/) {
               $var = $1;
               $value = ($2 eq "") ? 1 : $3;
               shift @ARGV;
               $values->{$var} = $value;
           }
  +        if ($#ARGV >= 0 && $ARGV[0] eq "--") {
  +            shift @ARGV;
  +        }
       }
   
       #################################################################
  @@ -340,15 +404,19 @@
       # to the directory in which the script runs.
       if (!$prefix) {
           use Cwd 'abs_path';
  -        $prefix = abs_path($prog_dir);
  -        $prefix =~ s!/bin$!!;
  -        $prefix =~ s!/cgi-bin.*$!!;
  +        my $abs_prog_dir = abs_path($prog_dir);
  +        if ($abs_prog_dir =~ s!/bin$!!) {
  +            $prefix = $abs_prog_dir;
  +        }
  +        elsif ($abs_prog_dir =~ s!/cgi-bin.*$!!) {
  +            $prefix = $abs_prog_dir;
  +        }
       }
   
  -    $prefix = "." if (!$prefix);   # last resort: current directory
  +    $prefix = "/usr/local" if (!$prefix);   # last resort: current directory
   
       my ($env_var, @env_vars, $regexp);
  -    if (! $init_options{no_option_file}) {
  +    if (! $init_args{no_option_file}) {
           #################################################################
           # 5. Define the standard places to look for an option file
           #################################################################
  @@ -360,6 +428,7 @@
           push(@option_file, "$prog_dir/app.conf");
           push(@option_file, "$prefix/etc/app/$app.conf") if ($app ne "app");
           push(@option_file, "$prefix/etc/app/app.conf");
  +        push(@option_file, "/etc/app/app.conf");
   
           #################################################################
           # 6. now actually read in the file(s)
  @@ -368,16 +437,16 @@
           #    are indicated by an "import" line
           #################################################################
   
  -        local(*App::FILE);
  +        local(*App::Options::FILE);
           my ($option_file, $exclude_section, $app_specified);
           my ($cond, @cond, $exclude);
           while ($#option_file > -1) {
               $option_file = shift(@option_file);
               $exclude_section = 0;
               print STDERR "Looking for option file [$option_file]\n" if 
($values->{debug_options});
  -            if (open(App::FILE, "< $option_file")) {
  +            if (open(App::Options::FILE, "< $option_file")) {
                   print STDERR "Found option file [$option_file]\n" if 
($values->{debug_options});
  -                while (<App::FILE>) {
  +                while (<App::Options::FILE>) {
                       chomp;
                       # for lines that are like "[regexp]" or even "[regexp] var = 
value"
                       # or "[value;var=value]" or 
"[/regexp/;var1=value1;var2=/regexp2/]"
  @@ -411,6 +480,7 @@
                               }
                               last if ($exclude);
                           }
  +                        s/^#.*$//;               # delete comments
                           if ($_) {
                               # this is a single-line condition, don't change the 
$exclude_section flag
                               next if ($exclude);
  @@ -437,19 +507,26 @@
                           
                           # TODO: here documents, var = <<EOF
                           # only add values which have never been defined before
  -                        if (!defined $values->{$var} && 
!$init_options{no_env_vars}) {
  -                            if ($option && $option->{$var}{env}) {
  +                        if (!defined $values->{$var}) {
  +                            if (!$init_args{no_env_vars}) {
  +                                if ($option && defined $option->{$var}{env}) {
  +                                    if ($option->{$var}{env} eq "") {
  +                                        @env_vars = ();
  +                                    }
  +                                    else {
                                   @env_vars = split(/[,;]/, $option->{$var}{env});
                               }
  +                                }
                               else {
                                   @env_vars = ( "APP_" . uc($var) );
                               }
                               foreach $env_var (@env_vars) {
  -                                if (defined $ENV{$env_var}) {
  +                                    if ($env_var && defined $ENV{$env_var}) {
                                       $value = $ENV{$env_var};
                                       last;
                                   }
                               }
  +                            }
                               # do variable substitutions, var = ${prefix}/bin
                               if (defined $value) {
                                   $value =~ s/\$\{([a-zA-Z0-9_\.-]+)\}/(defined 
$values->{$1} ? $values->{$1} : "")/eg;
  @@ -458,7 +535,7 @@
                           }
                       }
                   }
  -                close(App::FILE);
  +                close(App::Options::FILE);
   
                   if ($values->{flush_imports}) {
                       @option_file = ();  # throw out other files to look for
  @@ -477,10 +554,10 @@
       #################################################################
   
       @vars = ();
  -    if ($init_options{options}) {
  +    if ($init_args{options}) {
           croak "App::Options->init(): 'options' arg must be an array reference"
  -            if (ref($init_options{options}) ne "ARRAY");
  -        push(@vars, @{$init_options{options}});
  +            if (ref($init_args{options}) ne "ARRAY");
  +        push(@vars, @{$init_args{options}});
       }
   
       if ($option) {
  @@ -490,15 +567,20 @@
       foreach $var (@vars) {
           if (!defined $values->{$var}) {
               $value = $option ? $option->{$var}{default} : undef;
  -            if (!$init_options{no_env_vars}) {
  -                if ($option && $option->{$var}{env}) {
  +            if (!$init_args{no_env_vars}) {
  +                if ($option && defined $option->{$var}{env}) {
  +                    if ($option->{$var}{env} eq "") {
  +                        @env_vars = ();
  +                    }
  +                    else {
                       @env_vars = split(/[,;]/, $option->{$var}{env});
                   }
  +                }
                   else {
                       @env_vars = ( "APP_" . uc($var) );
                   }
                   foreach $env_var (@env_vars) {
  -                    if (defined $ENV{$env_var}) {
  +                    if ($env_var && defined $ENV{$env_var}) {
                           $value = $ENV{$env_var};
                           last;
                       }
  @@ -507,8 +589,8 @@
               # do variable substitutions, var = ${prefix}/bin
               if (defined $value) {
                   $value =~ s/\$\{([a-zA-Z0-9_\.-]+)\}/(defined $values->{$1} ? 
$values->{$1} : "")/eg;
  -                $values->{$var} = $value;    # save all in %App::options
               }
  +            $values->{$var} = $value;    # save all in %App::options
           }
       }
   
  @@ -574,6 +656,16 @@
           $exit_status = 0;
       }
   
  +    #################################################################
  +    # These are the actual Perl regular expressions which match
  +    # numbers.  The regexes we use are approximately correct.
  +    #################################################################
  +    # \d(_?\d)*(\.(\d(_?\d)*)?)?[Ee][\+\-]?(\d(_?\d)*)  12 12.34 12.
  +    # \.\d(_?\d)*[Ee][\+\-]?(\d(_?\d)*)                 .34
  +    # 0b[01](_?[01])*
  +    # 0[0-7](_?[0-7])*
  +    # 0x[0-9A-Fa-f](_?[0-9A-Fa-f])*
  +
       my ($type);
       if ($option) {
           @vars = (sort keys %$option);
  @@ -583,14 +675,14 @@
               $value = $values->{$var};
               next if (! defined $value);
               if ($type eq "integer") {
  -                if ($value !~ /^-?[0-9]+$/) {
  +                if ($value !~ /^-?[0-9_]+$/) {
                       $exit_status = 1;
                       print "Error: \"$var\" must be of type \"$type\" (not 
\"$value\")\n";
                   }
               }
               elsif ($type eq "float") {
  -                if ($value !~ /^-?[0-9]+\.?[0-9]*([eE][+-]?[0-9]+)?$/ &&
  -                    $value !~ /^-?[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?$/) {
  +                if ($value !~ /^-?[0-9_]+\.?[0-9_]*([eE][+-]?[0-9_]+)?$/ &&
  +                    $value !~ /^-?\.[0-9_]+([eE][+-]?[0-9_]+)?$/) {
                       $exit_status = 1;
                       print "Error: \"$var\" must be of type \"$type\" (not 
\"$value\")\n";
                   }
  @@ -638,11 +730,11 @@
       }
   
       if ($exit_status >= 0) {
  -        if ($init_options{print_usage}) {
  -            &{$init_options{print_usage}}($values, \%init_options);
  +        if ($init_args{print_usage}) {
  +            &{$init_args{print_usage}}($values, \%init_args);
           }
           else {
  -            App::Options->print_usage($values, \%init_options);
  +            App::Options->print_usage($values, \%init_args);
           }
           exit($exit_status);
       }
  @@ -650,16 +742,16 @@
   
   sub print_usage {
       shift if ($#_ > -1 && $_[0] eq "App::Options");
  -    my ($values, $init_options) = @_;
  +    my ($values, $init_args) = @_;
       print STDERR "Usage: $0 [options]\n";
       printf STDERR "       --%-32s print this message (also -?)\n", "help";
       my (@vars, $show_all, %option_seen);
  -    if ($init_options->{options}) {
  -        @vars = @{$init_options->{options}};
  +    if ($init_args->{options}) {
  +        @vars = @{$init_args->{options}};
           $show_all = 0 if (! defined $show_all);
       }
  -    if ($init_options->{option} && ($show_all || $#vars == -1)) {
  -        push(@vars, (sort keys %{$init_options->{option}}));
  +    if ($init_args->{option} && ($show_all || $#vars == -1)) {
  +        push(@vars, (sort keys %{$init_args->{option}}));
           $show_all = 0 if (! defined $show_all);
       }
       if ($show_all || (!defined $show_all && $#vars == -1)) {
  @@ -667,7 +759,7 @@
       }
       my ($var, $value, $type, $desc, $option);
       my ($var_str, $value_str, $type_str, $desc_str);
  -    $option = $init_options->{option} || {};
  +    $option = $init_args->{option} || {};
       foreach $var (@vars) {
           next if ($option_seen{$var});
           $option_seen{$var} = 1;
  @@ -682,6 +774,727 @@
           printf STDERR "       --%-32s [%s]$type_str$desc_str\n", $var_str, 
$value_str;
       }
   }
  +
  +=head1 FLOW: OPTION PROCESSING DETAILS
  +
  +Basic Concept - By calling App::Options->init(),
  +your program parses the command line, environment variables,
  +and option files, and puts var/value pairs into a
  +global option hash, %App::options.
  +
  +    BEGIN {
  +        use App::Options;
  +        App::Options->init();
  +    }
  +
  +One of the args to init() is the "values" arg, which allows
  +for a different hash to be specified as the target of all
  +option variables and values.
  +
  +    App::Options->init(values => \%Mymodule::opts);
  +
  +Throughout the following description of option processing,
  +the %App::options hash may be referred to as the "options hash".
  +However it will be understood that some other hash (as
  +specified by the "values" arg) may actually be used.
  +
  +=head2 Command Line Arguments
  +
  +Unless the "no_cmd_args" arg is specified to init(), the
  +first source of option values is the command line.
  +
  +Each command line argument that begins with a "-" or a "--" is
  +considered to be an option.  It may take any form such as
  +
  +    --verbose      # long option, no arg
  +    --verbose=5    # long option, with arg
  +    --city=ATL     # long option, with arg
  +    -x             # short option, no arg
  +    -t=12:30       # short option, with arg
  +
  +All detected options are shifted out of @ARGV and the values are
  +set in the options hash (%App::options).  Options without args
  +are understood to have a value of "1".  So "--verbose" is
  +identical to "--verbose=1".
  +
  +Naturally, the "--" option terminates command line option processing.
  +
  +=head2 Command Line Argument Variable Substitution
  +
  +Any value which includes a variable undergoes variable substitution
  +before it is placed in the option hash. i.e.
  +
  +    logdir = ${prefix}/log
  +
  +This line will be expanded properly.
  +(Of course, the variable and its value should be already set in the
  +option hash.)
  +
  +Environment variables are not searched for the command line options
  +because the command line options would override them anyway.
  +
  +=head2 Special Option "app"
  +
  +If the special option, "app", was not given on the command line,
  +it is initialized.  This option is useful for including or excluding
  +different sections of the option files.
  +
  +To handle the special case that the program is running in a CGI
  +environment, the PATH_INFO variable is checked first.
  +The first segment of the PATH_INFO is stripped off, and that becomes
  +the value of the "app" option.
  +
  +Otherwise, the stem of the program name becomes the value of the
  +"app" option.  The stem is the program name without any trailing
  +extension (i.e. ".exe", ".pl", etc.).
  +
  +=head2 The Program Directory
  +
  +One of the places that will be searched for option files is the
  +directory in which the program exists on the file system.
  +This directory is known internally as "$prog_dir".
  +
  +=head2 Special Option "prefix"
  +
  +The special option, "prefix", represents the root directory of the
  +software installation.  On a Unix system, a suite of software might
  +by installed at "/usr/myproduct/thisversion", and that would be
  +the "prefix".  Under this directory, you would expect to find the
  +"src", "bin", "lib", and "etc" directories, as well as perhaps
  +"cgi-bin", "htdocs", and others.
  +
  +If the "prefix" option is not specified on the command line,
  +the $PREFIX environment variable is used.
  +
  +If that is not set, the $prog_dir with the trailing "/bin" or
  +"/cgi-bin" stripped off is used.
  +
  +=head2 Option Files
  +
  +Unless the "no_option_file" arg is specified to init(), the
  +next source of option values is the option files.
  +
  +By default, a cascading set of option files are all consulted
  +to allow individual users to specify values that override the
  +normal values for certain programs.  Furthermore, the
  +values for individual programs can override the values configured
  +generally system-wide. 
  +
  +The resulting value for an option variable comes from the first
  +place that it is ever seen.  Subsequent mentions of the option
  +variable within the same or other option files will be ignored.
  +
  +The following files are consulted in order.
  +
  +    $ENV{HOME}/.app/$app.conf
  +    $ENV{HOME}/.app/app.conf
  +    $prog_dir/$app.conf
  +    $prog_dir/app.conf
  +    $prefix/etc/app/$app.conf
  +    $prefix/etc/app/app.conf
  +    /etc/app/app.conf
  +
  +Thus, a system administrator might set up the $prefix/etc/app/app.conf
  +file with system-wide defaults.  All option configuration could be done
  +in this single file, separating the relevant variables into different
  +sections for each different program to be configured.
  +
  +However, if the administrator decided that there were too many parameters
  +for a single program such that it cluttered this file, he might put the
  +option values for that program into the $prefix/etc/app/$app.conf file.
  +This distinction is a matter of preference, as both methods are equally
  +functional.
  +
  +A program developer may decide to override some of the system-wide
  +option values for everyone by putting option files in the program's own
  +directory.
  +
  +Furthermore, a user may decide to override some of the resulting
  +option values by putting some option files in the appropriate
  +place under his home directory.
  +
  +This separation of config files also allows for secure information
  +(such as database passwords) to be required to be provided in the
  +user's own (secured) option files rather than in read-only
  +system-wide option files.
  +
  +Specifying the "--debug_options" option on the command line will
  +assist in figuring out which files App::Options is looking at.
  +
  +=head2 Option File Format
  +
  +In general an option file takes the form of lines with "var = value".
  +
  +   dbname   = prod     # this is the production database
  +   dbuser   = scott
  +   dbpass   = tiger
  +
  +Trailing comments (preceded by a "#") are trimmed off.
  +Spaces before and after the variable, and before and after the value
  +are all trimmed off.  Then enclosing double-quotes (") are trimmed
  +off.  Variables can be any of the characters in
  +[a-zA-Z0-9_.-].  Values can be any printable characters or the
  +empty string.  Any lines which aren't recognizable as "var = value"
  +lines or section headers (see below) are ignored.
  +
  +If certain variables should be set only for certain programs (or
  +under certain other conditions), section headers may be introduced.
  +The special section headers "[ALL]" and "[]" specify the end of a
  +conditional section and the resumption of unconditional option
  +variables.
  +
  +   [progtest]
  +   dbname   = test     # this is the test database
  +   [ALL]
  +   dbname   = prod     # this is the production database
  +   dbuser   = scott
  +   dbpass   = tiger
  +
  +In this case, the "progtest" program will get "dbname = test" while
  +all other programs will get "dbname = prod".
  +
  +Note that you would not get the desired results if
  +the "dbname = prod" statement was above the "[progtest]"
  +header.  Once an option variable is set, no other occurrence
  +of that variable in any option file will override it.
  +
  +For the special case where you want to specify a section for
  +only one variable as above, the following shortcut is provided.
  +
  +   [progtest] dbname = test # this is the test database
  +   dbname   = prod          # this is the production database
  +   dbuser   = scott
  +   dbpass   = tiger
  +
  +The "[progtest]" section header applied for only the single line.
  +
  +Furthermore, if it were desired to make this override for all
  +programs containing "test" in them, you would use the following
  +syntax.
  +
  +   [/test/] dbname = test   # this is the test database
  +   dbname   = prod          # this is the production database
  +   dbuser   = scott
  +   dbpass   = tiger
  +
  +The "[/test/]" section header tested the "app" option using
  +an arbitrary regular expression.
  +
  +The section headers can create a condition for inclusion
  +based on any of the variables currently in the option
  +hash.  In fact, "[progtest]" is just a synonym for
  +"[app=progtest]" and "[/test/]" is a synonym for "[app=/test/]".
  +
  +If, for instance, the usernames and passwords were different
  +for the different databases, you might have the following.
  +
  +   [/test/] dbname = test   # progs with "test" go to test database
  +   dbname   = prod          # other progs go to the production database
  +   [dbname=test]            # progs
  +   dbuser   = scott
  +   dbpass   = tiger
  +   [dbname=prod]
  +   dbuser   = mike
  +   dbpass   = leopard
  +
  +The conditions created by a section header may be the result of more
  +than a single condition.
  +
  +   [dbname=test;dbuser=scott]
  +   dbpass = tiger
  +   [dbname=test;dbuser=ken]
  +   dbpass = ocelot
  +   [dbname=prod;dbuser=scott]
  +   dbpass = tiger62
  +   [dbname=prod;dbuser=ken]
  +   dbpass = 3.ocelot_
  +
  +Any number of conditions can be included with semicolons separating
  +them.
  +
  +Each time a variable/value pair is found in an option file,
  +it is only included in the option hash if that variable is
  +currently not defined in the option hash.  Therefore, option
  +files never override command line parameters.
  +
  +=head2 Option Environment Variables and Variable Substitution
  +
  +For each variable/value pair that is to be inserted into the
  +option hash from the option files, the corresponding environment
  +variables are searched to see if they are defined.  The environment
  +always overrides an option file value.  (If the
  +"no_env_vars" arg was given to the init() method, this whole
  +step of checking the environment is skipped.)
  +
  +By default, the environment variable for an option variable named
  +"dbuser" would be "APP_DBUSER".  However, if the "env" attribute
  +of the "dbuser" option is set, a different environment variable
  +may be checked instead (see the Tutorial below for examples).
  +
  +After checking the environment for override values,
  +any value which includes a variable undergoes variable substitution
  +before it is placed in the option hash.
  +
  +=head2 import and flush_imports
  +
  +After each option file is read, the special option "flush_imports"
  +is checked.  If set, the list of pending option files to be
  +parsed is cleared, and the flush_imports option is also cleared.
  +
  +This is useful if you do not want to inherit any of the option
  +values defined in system-wide option files.
  +
  +The special option "import" is checked next.  If it is set, it is
  +understood to be a list of option files (separated by /[,; ]+/)
  +to be prepended to the list of pending option files.
  +The import option itself is cleared.
  +
  +=head2 Other Environment Variables and Defaults
  +
  +After command line options and option files have been parsed,
  +all of the other options which are known to the program are
  +checked for environment variables and defaults.
  +
  +Options can be defined for the program with either the
  +"options" arg or the "option" arg to the init() method
  +(or a combination of both).
  +
  +    App::Options->init(
  +        options => [ "dbname", "dbuser", "dbpass" ],
  +        option => {
  +            dbname => {
  +                env => "DBNAME",
  +                default => "devel",
  +            },
  +            dbuser => {
  +                env => "DBUSER;DBI_USER",
  +            },
  +            dbpass => {
  +                env => "", # password in %ENV is security breach
  +            },
  +        },
  +    );
  +
  +For each option variable known, if the value is not already set,
  +then the environment is checked, the default is checked, variable
  +expansion is performed, and the value is entered into the 
  +option hash.
  +
  +=head2 Special Option prefix
  +
  +The special option "prefix" is reconciled and finalized next.
  +
  +Unless it was specified on the command line, the original "prefix"
  +was autodetected.  This may have resulted in a path which was 
  +technically correct but was different than intended due to 
  +symbolic linking on the file system.
  +
  +Since the "prefix" variable may also be set in an option file,
  +there may be a difference between the auto-detected "prefix"
  +and the option file "prefix".  If this case occurs, the
  +option file "prefix" is the one that is accepted as authoritative.
  +
  +=head2 Special Option perlinc
  +
  +One of the primary design goals of App::Options was to be able
  +to support multiple installations of software on a single machine.
  +
  +Thus, you might have different versions of software installed
  +under various directories such as
  +
  +    /usr/product1/1.0.0
  +    /usr/product1/1.1.0
  +    /usr/product1/2.1.5
  +
  +Naturally, slightly different versions of your perl modules will
  +be installed under each different "prefix" directory.
  +When a program runs from /usr/product1/1.1.0/bin, the "prefix"
  +will by "/usr/product1/1.1.0" and we want the @INC variable to
  +be modified so that the appropriate perl modules are included
  +from $prefix/lib/*.
  +
  +This is where the "perlinc" option comes in.
  +
  +If "perlinc" is set, it is understood to be a list of paths
  +(separated by /[ ,;]+/) to be prepended to the @INC variable.
  +
  +If "perlinc" is not set,
  +"$prefix/lib/perl5/$perlversion" and
  +"$prefix/lib/perl5/site_perl/$perlversion" are automatically
  +prepended to the @INC variable as a best guess.
  +
  +=head2 Special Option debug_options
  +
  +If the "debug_options" variable is set (often on the command
  +line), the list of option files that was searched is printed
  +out, the resulting list of variable values is printed out,
  +and the resulting list of include directories (@INC) is printed
  +out.
  +
  +=head2 Help and Validations
  +
  +After all values have been parsed, various conditions are
  +checked to see if the program should not continue and the
  +usage statement be printed out.
  +
  +If the "-?" or "--help" options were set on the command line,
  +the usage statement is printed, and the program is exited.
  +
  +Then each of the options which is defined may be validated.
  +
  +If an option is designated as "required", its value must be
  +defined somewhere (although it may be the empty string).
  +(If it is also required to be a non-empty string, a regex
  +may be provided for the type, i.e. type => "/./".)
  +
  +If an option is designated as having a "type", its value
  +must either be undefined or match a specific regular expression.
  +
  +    Type       Regular Expression
  +    =========  =========================================
  +    string     (any)
  +    integer    /^-?[0-9_]+$/
  +    float      /^-?[0-9_]+\.?[0-9_]*([eE][+-]?[0-9_]+)?$/
  +          (or) /^-?\.[0-9_]+([eE][+-]?[0-9_]+)?$/
  +    boolean    /^[01]$/
  +    date       /^[0-9]{4}-[01][0-9]-[0-3][0-9]$/
  +    datetime   /^[0-9]{4}-[01][0-9]-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]$/
  +    time       /^[0-2][0-9]:[0-5][0-9]:[0-5][0-9]$/
  +    /regexp/   /regexp/
  +
  +Note that an arbitrary regular expression may be designated
  +as the "type" by enclosing it in slashes (i.e. "/^[YN]$/").
  +
  +If the options fail any of the "required" or "type" validation
  +tests, the App::Options::print_usage() function is called
  +to print out a usage statement and the program is exited.
  +
  +=head1 TUTORIAL
  +
  +=head2 Getting Started
  +
  +Create a perl program called "demo1".
  +
  +    #!/usr/bin/perl
  +    BEGIN {
  +        use App::Options;
  +        App::Options->init();            # call this method
  +    }
  +    print "Wow. Here are the options...\n";
  +    foreach (sort keys %App::options) {  # options appear here!
  +        printf("%-20s => %s\n", $_, $App::options{$_});
  +    }
  +
  +Run it different kinds of ways to see how it responds.
  +
  +    demo1
  +    demo1 -x
  +    demo1 -x --verbose
  +    demo1 --x -verbose
  +    demo1 -x=5 --verbose=10 --foo=bar
  +    demo1 --help
  +    demo1 -x=8 --help
  +    demo1 -?
  +    demo1 --debug_options -?
  +    demo1 -x=5 --verbose=10 --foo=bar --debug_options -?
  +
  +Now create a copy of the program.
  +
  +    cp demo1 demo2
  +
  +Start putting entries like the following
  +
  +    x = 7
  +    hello = world
  +    [demo2]
  +    verbose=3
  +    [/demo/]
  +    baz = foo
  +
  +in the following files
  +
  +    $HOME/.app/demo1.conf
  +    $HOME/.app/demo2.conf
  +    $HOME/.app/app.conf
  +    demo1.conf  (same directory as the demo* programs)
  +    demo2.conf  (same directory as the demo* programs)
  +    app.conf    (same directory as the demo* programs)
  +    $PREFIX/etc/app/demo1.conf
  +    $PREFIX/etc/app/demo2.conf
  +    $PREFIX/etc/app/app.conf
  +    /etc/app/app.conf
  +
  +and see how the programs respond in each different case.
  +
  +Next set environment variables like the following and
  +see how the programs respond.
  +
  +    export APP_X=14
  +    export APP_VERBOSE=7
  +    export APP_FOO=xyzzy
  +    export APP_HELLO=Plugh!
  +
  +You are well on your way.
  +
  +=head2 A Development Scenario
  +
  +Now let's imagine that we are writing a suite of programs which operate
  +on a relational database.  These programs are part of a larger
  +system which goes through a development cycle of development,
  +test, and production.  Each step in the development cycle, the
  +programs will run against different databases, but we don't want
  +that to affect the code.
  +
  +Let's suppose that we write a program which lists the customers
  +in a customer table.
  +
  +    create table person (
  +        person_id      integer       not null auto_increment primary key,
  +        first_name     varchar(99)   null,
  +        last_name      varchar(99)   null,
  +        birth_dt       date          null,
  +        company_id     integer       null,
  +        wholesale_ind  char(1)       null,
  +        change_dttm    datetime      not null,
  +    );
  +
  +We call this program "listcust".
  +
  +    #!/usr/local/bin/perl
  +    BEGIN {
  +        use App::Options;
  +        App::Options->init();
  +    }
  +    use strict;
  +    use DBI;
  +    my $dsn = "dbi:$App::options{dbdriver}:database=$App::options{dbname}";
  +    my $dbh = DBI->connect($dsn, $App::options{dbuser}, $App::options{dbpass});
  +    my $sql = "select first_name, last_name, birth_dt, company_id, wholesale_ind, 
change_dttm from person";
  +    my $cust = $dbh->selectall_arrayref($sql);
  +    foreach my $row (@$cust) {
  +        printf("%-24 %-24 %s %9d %s\n", @$row);
  +    }
  +    $dbh->disconnect();
  +
  +Then you can invoke this program with all of the command line options
  +and everything works fine.
  +
  +    listcust --dbdriver=mysql --dbname=prod --dbuser=scott --dbpass=tiger
  +
  +However, if you don't use all of the options, you will get a DBI error.
  +Furthermore, "listcust --help" doesn't help very much.  A system administrator
  +confronting this problem would put the following lines into
  +"$PREFIX/etc/app/app.conf" or "$PREFIX/etc/app/listcust.conf".
  +
  +    dbdriver = mysql
  +    dbname   = prod
  +    dbuser   = scott
  +    dbpass   = tiger
  +
  +If, however, your projects were not in the habit of using the
  +PREFIX environment variable and the program is not installed in
  +$PREFIX/bin, he would have to put the above lines in the same
  +directory as "listcust" in either the "app.conf" file or the
  +"listcust.conf" file.
  +
  +A user (without privileges to the "$PREFIX/etc/app" directory
  +or the directory in which "listcust" lives) would have to put
  +the described lines into "$HOME/.app/app.conf" or
  +"$HOME/.app/listcust.conf".
  +
  +Putting the options in any of those files would make "--help"
  +print something intelligent.
  +
  +A developer, however, might decide that the program should
  +have some defaults.
  +
  +    BEGIN {
  +        use App::Options;
  +        App::Options->init(
  +            option => {
  +                dbdriver => "mysql",
  +                dbname   => "prod",
  +                dbuser   => "scott",
  +                dbpass   => "tiger",
  +            },
  +        );
  +    }
  +
  +(This supplies defaults and also makes "--help" print something
  +intelligent, regardless of whether there are any configuration
  +files.)
  +
  +If all you wanted to do was provide defaults for options,
  +this format would be fine.  However, there are other useful
  +attributes of an option besides just the "default".
  +To use those, you generally would use the more complete form
  +of the "option" arg.
  +
  +    BEGIN {
  +        use App::Options;
  +        App::Options->init(
  +            option => {
  +                dbdriver => { default => "mysql", },
  +                dbname   => { default => "prod",  },
  +                dbuser   => { default => "scott", },
  +                dbpass   => { default => "tiger", },
  +            },
  +        );
  +    }
  +
  +Then we can indicate that these options are all required.
  +If they are not provided, the program will not run.
  +
  +Meanwhile, it makes no sense to provide a "default" for a
  +password.  We can remove the default, but if we ever tried to run
  +the program without providing the password, it would not get
  +past printing a "usage" statement.
  +
  +    BEGIN {
  +        use App::Options;
  +        App::Options->init(
  +            option => {
  +                dbdriver => { required => 1, default => "mysql", },
  +                dbname   => { required => 1, default => "prod",  },
  +                dbuser   => { required => 1, default => "scott", },
  +                dbpass   => { required => 1, },
  +            },
  +        );
  +    }
  +
  +We now might enhance the code in order to list only the 
  +customers which had certain attributes.
  +
  +    my $sql = "select first_name, last_name, birth_dt, company_id, wholesale_ind, 
change_dttm from person";
  +    my (@where);
  +    push(@where, "first_name like '%$App::options{first_name}%'")
  +        if ($App::options{first_name});
  +    push(@where, "last_name like '%$App::options{last_name}%'")
  +        if ($App::options{last_name});
  +    push(@where, "birth_dt = '$App::options{birth_dt}'")
  +        if ($App::options{birth_dt});
  +    push(@where, "company_id = $App::options{company_id}")
  +        if ($App::options{company_id});
  +    push(@where, "wholesale_ind = '$App::options{wholesale_ind}'")
  +        if ($App::options{wholesale_ind});
  +    push(@where, "change_dttm >= '$App::options{change_dttm}'")
  +        if ($App::options{change_dttm});
  +    if ($#where > -1) {
  +        $sql .= "\nwhere " . join("\n  and ", @where) . "\n";
  +    }
  +    my $cust = $dbh->selectall_arrayref($sql);
  +
  +The init() method call might be enhanced to look like this.
  +Also, the order that the options are printed by "--help" can
  +be set with the "options" argument.  (Otherwise, they would
  +print in alphabetical order.)
  +
  +    BEGIN {
  +        use App::Options;
  +        App::Options->init(
  +            options => [ "dbdriver", "dbname", "dbuser", "dbpass",
  +                "first_name", "last_name", "birth_dt", "company_id",
  +                "wholesale_ind", "change_dttm",
  +            ],
  +            option => {
  +                dbdriver => {
  +                    description => "dbi driver name",
  +                    default => "mysql",
  +                    env => "DBDRIVER",  # use a different env variable
  +                    required => 1,
  +                },
  +                dbname   => {
  +                    description => "database name",
  +                    default => "prod", 
  +                    env => "DBNAME",  # use a different env variable
  +                    required => 1,
  +                },
  +                dbuser   => {
  +                    description => "database user",
  +                    default => "scott",
  +                    env => "DBUSER;DBI_USER",  # check both
  +                    required => 1,
  +                },
  +                dbpass   => {
  +                    description => "database password",
  +                    env => "",  # disable env for password (insecure)
  +                    required => 1,
  +                },
  +                first_name => {
  +                    description => "portion of customer's first name",
  +                },
  +                last_name  => {
  +                    description => "portion of customer's last name",
  +                },
  +                birth_dt   => {
  +                    description => "customer's birth date",
  +                    type => "date",
  +                },
  +                company_id => {
  +                    description => "customer's company ID",
  +                    type => "integer",
  +                },
  +                wholesale_ind => {
  +                    description => "indicator of wholesale customer",
  +                    type => "/^[YN]$/",
  +                },
  +                change_dttm => {
  +                    description => "changed-since date/time",
  +                    type => "datetime",
  +                },
  +            },
  +        );
  +    }
  +
  +It should be noted in the example above that the default environment
  +variable name ("APP_${varname}") has been overridden for some of 
  +the options.  The "dbname" variable will be set from "DBNAME"
  +instead of "APP_DBNAME".  The "dbuser" variable will be set
  +from either "DBUSER" or "DBI_USER".
  +
  +It should also be noted that if only the order of the options rather
  +than all of their attributes were desired, the following could
  +have been used.  Using the "options" arg
  +limits the printing of options to only those listed unless the
  +"show_all" argument is also given.  Supplying the "show_all"
  +argument allows for all options set in the option files also
  +to be printed.
  +
  +    BEGIN {
  +        use App::Options;
  +        App::Options->init(
  +            options => [ "dbdriver", "dbname", "dbuser", "dbpass",
  +                "first_name", "last_name", "birth_dt", "company_id",
  +                "wholesale_ind", "change_dttm",
  +            ],
  +            show_all => 1,
  +        );
  +    }
  +
  +If, for some reason, the program needed to put the options
  +into a different option hash (instead of %App::options) or directly
  +specify the option file to use (disregarding the standard option
  +file search path), it may do so using the following syntax.
  +
  +    App::Options->init(
  +        values => \%Mymodule::opts,
  +        option_file => "/path/to/options.conf",
  +    );
  +
  +If, for some reason, the program needs to inhibit one or more
  +of the sources for options, it can do so with one of the
  +following arguments.  Of course, inhibiting all three would
  +be a bit silly.
  +
  +    App::Options->init(
  +        no_cmd_args => 1,
  +        no_option_file => 1,
  +        no_env_vars => 1,
  +    );
  +
  +Hopefully, that's enough to get you going.
  +
  +I welcome all feedback, bug reports, and feature requests.
   
   =head1 ACKNOWLEDGEMENTS
   
  
  
  

Reply via email to