cvsuser     04/01/19 06:51:41

  Modified:    App-Options CHANGES Makefile.PL TODO
               App-Options/lib/App Options.pm
               App-Options/t app.conf main.t
  Log:
  changed regexp sections, regexp types, value quoting, env var identification
  
  Revision  Changes    Path
  1.2       +42 -3     p5ee/App-Options/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/CHANGES,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -w -r1.1 -r1.2
  --- CHANGES   16 Nov 2003 21:21:22 -0000      1.1
  +++ CHANGES   19 Jan 2004 14:51:40 -0000      1.2
  @@ -1,7 +1,46 @@
  -#########################################
  +#############################################################################
   # CHANGE LOG
  -#########################################
  +#############################################################################
   
  -VERSION 0.60
  +VERSION 0.62
  + x [prog] matches "prog" only. [/prog/] matches by regular expression.
  +   In version 0.61, the section [list] would match ($app =~ /list/).
  +   In version 0.62, the section [list] only matches ($app eq "list"),
  +   while [/list/] matches ($app =~ /list/).
  + x type="/regexp/" matches regexp. unknown types ignored.
  +   In version 0.61, an option type which was not one of the known
  +   option types (integer, float, date, datetime, etc.), was considered
  +   automatically as a regular expression.
  +   In version 0.62, only types which take the form "/regexp/" are
  +   actually regular expressions.  All other unknown "types" are ignored.
  +   This sets the groundwork for better forward-compatibility when new
  +   types are introduced, they will not break older versions of the code.
  +   It also just seems clearer.
  + x "show_all" option - shows all defined options, not just in [ options ]
  +   In version 0.61, the --help option would show all variables defined
  +   in the code, on the command line, or in any of the files (i.e.show_all=1).
  +   However, if the "options" argument is used in the init() method in the
  +   code, only those enumerated options would be shown (i.e. show_all=0).
  +   In version 0.62, the same behavior applies unless the "show_all"
  +   parameter is given explicitly.
  + x quoting, var = " hello world "
  +   In version 0.61, variable values in the option file had leading and
  +   trailing spaces removed.  This meant that a value of a single space
  +   was not possible.
  +   In version 0.62, the same behavior applies.  However, if the 
  +   remaining text starts with " and ends with ", those quote marks are
  +   removed.  i.e. var = " " will result in the value of a single space,
  +   and message = "Hello world. " will have a trailing space.
  + x check list of configurable environment vars ("env") instead of
  +   "APP_${uc_var}"
  +   In version 0.61, variable values could be supplied by specifying the
  +   variable prefixed with "APP_".  (i.e. "path" could be specified
  +   with "APP_PATH")
  +   In version 0.62, a list of environment variable names may be given
  +   and the first with a supplied value is used for the value.
  +   (i.e. options => { path => { env => "PATH" } } will cause the
  +   "path" variable to be set from the "PATH" environment variable.)
  +
  +VERSION 0.61
    o Initial release
   
  
  
  
  1.3       +2 -2      p5ee/App-Options/Makefile.PL
  
  Index: Makefile.PL
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/Makefile.PL,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -w -r1.2 -r1.3
  --- Makefile.PL       7 Jan 2004 15:24:07 -0000       1.2
  +++ Makefile.PL       19 Jan 2004 14:51:40 -0000      1.3
  @@ -1,6 +1,6 @@
   
   ######################################################################
  -## File: $Id: Makefile.PL,v 1.2 2004/01/07 15:24:07 spadkins Exp $
  +## File: $Id: Makefile.PL,v 1.3 2004/01/19 14:51:40 spadkins Exp $
   ######################################################################
   
   use ExtUtils::MakeMaker;
  @@ -14,7 +14,7 @@
   %opts = (
       'NAME'        => 'App-Options',
       'DISTNAME'    => 'App-Options',
  -    'VERSION'     => '0.61',
  +    'VERSION'     => '0.62',
       'EXE_FILES'   => [ @programs ],
       'dist'        => {'COMPRESS'=>'gzip -9f', 'SUFFIX' => 'gz',
                         'ZIP'=>'/usr/bin/zip','ZIPFLAGS'=>'-rl'},
  
  
  
  1.3       +8 -3      p5ee/App-Options/TODO
  
  Index: TODO
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/TODO,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -w -r1.2 -r1.3
  --- TODO      7 Jan 2004 15:24:07 -0000       1.2
  +++ TODO      19 Jan 2004 14:51:40 -0000      1.3
  @@ -1,8 +1,13 @@
   ######################################################################
  -## File: $Id: TODO,v 1.2 2004/01/07 15:24:07 spadkins Exp $
  +## File: $Id: TODO,v 1.3 2004/01/19 14:51:40 spadkins Exp $
   ######################################################################
  - o quoting, var = " hello world "
  - o check list of configurable environment vars instead of "APP_${uc_var}"
  + 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 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
  
  
  
  1.3       +110 -72   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.2
  retrieving revision 1.3
  diff -u -w -r1.2 -r1.3
  --- Options.pm        7 Jan 2004 15:24:07 -0000       1.2
  +++ Options.pm        19 Jan 2004 14:51:41 -0000      1.3
  @@ -1,6 +1,6 @@
   
   #############################################################################
  -## $Id: Options.pm,v 1.2 2004/01/07 15:24:07 spadkins Exp $
  +## $Id: Options.pm,v 1.3 2004/01/19 14:51:41 spadkins Exp $
   #############################################################################
   
   package App::Options;
  @@ -41,7 +41,7 @@
                   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",
  +                prefix        => "type=string;required;env=PREFIX",
                   perlinc       => undef,         # no default
                   debug_options => "type=integer",
                   import        => "type=string",
  @@ -124,7 +124,7 @@
                   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",
  +                prefix        => "type=string;required;env=PREFIX",
                   perlinc       => undef,         # no default
                   debug_options => "type=int",
                   import        => "type=string",
  @@ -165,7 +165,7 @@
           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).
  +        "boolean", "date", "time", "datetime", /regexp/).
       description - printed next to the option in the "usage" page
   
   The init() method stores command line options and option
  @@ -230,6 +230,39 @@
           $init_options{values} = $values;
       }
   
  +    #######################################################################
  +    # populate "option" (the information about each option!)
  +    #######################################################################
  +
  +    my ($var, $value, @vars, $option);
  +    $option = $init_options{option};
  +
  +    if ($option) {
  +        croak "App::Options->init(): 'option' arg must be a hash reference"
  +            if (ref($option) ne "HASH");
  +
  +        my (@args, $hash, $arg);
  +        foreach $var (keys %$option) {
  +            $value = $option->{$var};
  +            if (ref($value) eq "") {
  +                $hash = {};
  +                $option->{$var} = $hash;
  +                @args = split(/ *; */,$value);
  +                foreach $arg (@args) {
  +                    if ($arg =~ /^([^=]+)=(.*)$/) {
  +                        $hash->{$1} = $2;
  +                    }
  +                    elsif (! defined $hash->{default}) {
  +                        $hash->{default} = $arg;
  +                    }
  +                    else {
  +                        $hash->{$arg} = 1;
  +                    }
  +                }
  +            }
  +        }
  +    }
  +
       #################################################################
       # we do all this within a BEGIN block because we want to get an
       # option file and update @INC so that it will be used by
  @@ -245,7 +278,6 @@
       # an option without an "=" (i.e. --help) acts as --help=1
       # Put the var/value pairs in %$values
       #################################################################
  -    my ($var, $value);
       if (! $init_options{no_cmd_args}) {
           while ($#ARGV >= 0 && $ARGV[0] =~ /^--?([^=-][^=]*)(=?)(.*)/) {
               $var = $1;
  @@ -315,7 +347,7 @@
   
       $prefix = "." if (!$prefix);   # last resort: current directory
   
  -    my ($env_var, @env_vars);
  +    my ($env_var, @env_vars, $regexp);
       if (! $init_options{no_option_file}) {
           #################################################################
           # 5. Define the standard places to look for an option file
  @@ -337,8 +369,8 @@
           #################################################################
   
           local(*App::FILE);
  -        my ($option_file, $exclude_section);
  -        my ($regexp, $expr, @expr, $exclude);
  +        my ($option_file, $exclude_section, $app_specified);
  +        my ($cond, @cond, $exclude);
           while ($#option_file > -1) {
               $option_file = shift(@option_file);
               $exclude_section = 0;
  @@ -348,19 +380,34 @@
                   while (<App::FILE>) {
                       chomp;
                       # for lines that are like "[regexp]" or even "[regexp] var = 
value"
  -                    # or "[regexp;var=value]" or "[regexp;var1=value1;var2=value2]"
  +                    # or "[value;var=value]" or 
"[/regexp/;var1=value1;var2=/regexp2/]"
                       if (s|^ *\[([^\[\]]*)\] *||) {
  -                        @expr = split(/;/,$1);
  -                        $regexp = undef;
  -                        $exclude = 0;
  -                        foreach $expr (@expr) {
  -                            if ($expr =~ /^([^=]+)=(.*)$/) {  # a variable-based 
condition
  -                                $exclude = ((defined $values->{$1} ? $values->{$1} 
: "") ne $2);
  -                            }
  -                            else {  # a program name regexp
  -                                $regexp = $expr;
  -                                $exclude = ($regexp ne "" && $regexp ne "ALL" && 
$app !~ m!^$regexp$!);
  -                                $exclude = (!defined $regexp && $#expr > -1 && 
$exclude_section) if (!$exclude);
  +                        @cond = split(/;/,$1);   # separate the conditions that 
must be satisfied
  +                        $exclude = 0;            # assume the condition allows 
inclusion (! $exclude)
  +                        $app_specified = 0;      # the app (program name) itself 
has not yet been specified
  +                        foreach $cond (@cond) {  # check each condition
  +                            if ($cond =~ /^([^=]+)=(.*)$/) {  # i.e. [city=ATL] or 
[name=/[Ss]tephen/]
  +                                $var = $1;
  +                                $value = $2;
  +                                $app_specified = 1 if ($var eq "app");
  +                            }
  +                            else {              # i.e. [go] matches the program 
(app) named "go"
  +                                $var = "app";
  +                                $value = $cond;
  +                                $app_specified = 1;
  +                            }
  +                            if ($value =~ m!^/(.*)/$!) {  # variable's value must 
match the regexp
  +                                $regexp = $1;
  +                                $exclude = ((defined $values->{$var} ? 
$values->{$var} : "") !~ /$regexp/);
  +                            }
  +                            elsif ($var eq "app" && ($value eq "" || $value eq 
"ALL")) {
  +                                $exclude = 0;   # "" and "ALL" are special 
wildcards for the "app" variable
  +                            }
  +                            else {  # a variable's value must match exactly
  +                                $exclude = ((defined $values->{$var} ? 
$values->{$var} : "") ne $value);
  +                            }
  +                            if (!$app_specified && !$exclude) {
  +                                $exclude = ($#cond > -1 && $exclude_section);
                               }
                               last if ($exclude);
                           }
  @@ -386,22 +433,31 @@
                       if (/^([a-zA-Z0-9_.-]+) *= *(.*)/) {  # untainting also happens
                           $var = $1;
                           $value = $2;
  -                        # TODO: quoting, var = " hello world "
  +                        $value =~ s/^"(.*)"$/$1/;  # quoting, var = " hello world " 
(enables leading/trailing spaces)
  +                        
                           # TODO: here documents, var = <<EOF
                           # only add values which have never been defined before
  -                        if (!defined $values->{$var}) {
  -                            if (! $init_options{no_env_vars}) {
  -                                $env_var = "APP_" . uc($var);
  +                        if (!defined $values->{$var} && 
!$init_options{no_env_vars}) {
  +                            if ($option && $option->{$var}{env}) {
  +                                @env_vars = split(/[,;]/, $option->{$var}{env});
  +                            }
  +                            else {
  +                                @env_vars = ( "APP_" . uc($var) );
  +                            }
  +                            foreach $env_var (@env_vars) {
                                   if (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;
                               $values->{$var} = $value;    # save all in %App::options
                           }
                       }
                   }
  +                }
                   close(App::FILE);
   
                   if ($values->{flush_imports}) {
  @@ -409,7 +465,7 @@
                       delete $values->{flush_imports};
                   }
                   if ($values->{import}) {
  -                    unshift(@option_file, split(/[,:; ]+/, $values->{import}));
  +                    unshift(@option_file, split(/[,; ]+/, $values->{import}));
                       delete $values->{import};
                   }
               }
  @@ -417,39 +473,6 @@
       }
   
       #################################################################
  -    # 6b. convert $init_options{option} to deep hash
  -    #################################################################
  -
  -    my (@vars, $option);
  -    $option = $init_options{option};
  -
  -    if ($option) {
  -        croak "App::Options->init(): 'option' arg must be a hash reference"
  -            if (ref($option) ne "HASH");
  -
  -        my (@args, $hash, $arg);
  -        foreach $var (keys %$option) {
  -            $value = $option->{$var};
  -            if (ref($value) eq "") {
  -                $hash = {};
  -                $option->{$var} = $hash;
  -                @args = split(/ *; */,$value);
  -                foreach $arg (@args) {
  -                    if ($arg =~ /^([^=]+)=(.*)$/) {
  -                        $hash->{$1} = $2;
  -                    }
  -                    elsif (! defined $hash->{default}) {
  -                        $hash->{default} = $arg;
  -                    }
  -                    else {
  -                        $hash->{$arg} = 1;
  -                    }
  -                }
  -            }
  -        }
  -    }
  -
  -    #################################################################
       # 6c. fill in ENV vars and defaults
       #################################################################
   
  @@ -468,16 +491,26 @@
           if (!defined $values->{$var}) {
               $value = $option ? $option->{$var}{default} : undef;
               if (! $init_options{no_env_vars}) {
  -                $env_var = "APP_" . uc($var);
  +                if ($option && $option->{$var}{env}) {
  +                    @env_vars = split(/[,;]/, $option->{$var}{env});
  +                }
  +                else {
  +                    @env_vars = ( "APP_" . uc($var) );
  +                }
  +                foreach $env_var (@env_vars) {
                   if (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;
               $values->{$var} = $value;    # save all in %App::options
           }
       }
  +    }
   
       #################################################################
       # 7. establish the definitive (not inferred) $prefix
  @@ -589,8 +622,9 @@
                       print "Error: \"$var\" must be of type \"$type\" (format 
\"HH:MM::SS\") (not \"$value\")\n";
                   }
               }
  -            else {
  -                if ($value !~ /$type/) {
  +            elsif ($type =~ m!^/(.*)/$!) {
  +                $regexp = $1;
  +                if ($value !~ /$regexp/) {
                       $exit_status = 1;
                       print "Error: \"$var\" must match \"$type\" (not \"$value\")\n";
                   }
  @@ -619,20 +653,24 @@
       my ($values, $init_options) = @_;
       print STDERR "Usage: $0 [options]\n";
       printf STDERR "       --%-32s print this message (also -?)\n", "help";
  -    my (@vars);
  +    my (@vars, $show_all, %option_seen);
       if ($init_options->{options}) {
           @vars = @{$init_options->{options}};
  +        $show_all = 0 if (! defined $show_all);
       }
  -    elsif ($init_options->{option}) {
  -        @vars = (sort keys %{$init_options->{option}});
  +    if ($init_options->{option} && ($show_all || $#vars == -1)) {
  +        push(@vars, (sort keys %{$init_options->{option}}));
  +        $show_all = 0 if (! defined $show_all);
       }
  -    else {
  -        @vars = (sort keys %$values);
  +    if ($show_all || (!defined $show_all && $#vars == -1)) {
  +        push(@vars, (sort keys %$values));
       }
       my ($var, $value, $type, $desc, $option);
       my ($var_str, $value_str, $type_str, $desc_str);
       $option = $init_options->{option} || {};
       foreach $var (@vars) {
  +        next if ($option_seen{$var});
  +        $option_seen{$var} = 1;
           next if ($var eq "?" || $var eq "help");
           $value = $values->{$var};
           $type  = $option->{$var}{type} || "";
  
  
  
  1.2       +3 -1      p5ee/App-Options/t/app.conf
  
  Index: app.conf
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/t/app.conf,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -w -r1.1 -r1.2
  --- app.conf  16 Nov 2003 21:21:22 -0000      1.1
  +++ app.conf  19 Jan 2004 14:51:41 -0000      1.2
  @@ -22,5 +22,7 @@
   var6 = value6
   [ALL]
   var8 = value8
  -[ma.*]
  +[main]
  +var9 = value9
  +[/ma/]
   var7 = value7
  
  
  
  1.3       +16 -2     p5ee/App-Options/t/main.t
  
  Index: main.t
  ===================================================================
  RCS file: /cvs/public/p5ee/App-Options/t/main.t,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -w -r1.2 -r1.3
  --- main.t    11 Dec 2003 16:07:00 -0000      1.2
  +++ main.t    19 Jan 2004 14:51:41 -0000      1.3
  @@ -14,7 +14,17 @@
   delete $ENV{PREFIX};
   delete $ENV{DOCUMENT_ROOT};
   
  -App::Options->init();
  +$ENV{VAR10} = "value10";
  +$ENV{APP_VAR11} = "value11";
  +$ENV{VAR12} = "value12";
  +
  +App::Options->init(
  +    option => {
  +        var10 => { env => "VAR10a;VAR10", },
  +        var11 => { },
  +        var12 => { env => "VAR12", },
  +    },
  +);
   #print "CONF:\n   ", join("\n   ",%App::options), "\n";
   ok(%App::options, "put something in %App::options");
   is($App::options{prefix}, "/usr/local", "prefix = /usr/local");
  @@ -30,8 +40,12 @@
   is($App::options{var4}, undef,    "section excluded");
   is($App::options{var5}, "value5", "section exclusion ended");
   is($App::options{var6}, undef,    "section excluded again");
  -is($App::options{var7}, "value7", "section included");
  +is($App::options{var9}, "value9", "section included");
  +is($App::options{var7}, "value7", "section included (regexp)");
   is($App::options{var8}, "value8", "ALL works");
  +is($App::options{var11}, "value11", "default env var works");
  +is($App::options{var12}, "value12", "specified env var works");
  +is($App::options{var10}, "value10", "specified secondary env var works");
   
   %App::options = (
       config_file => "$dir/app.conf",
  
  
  

Reply via email to