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",