home *** CD-ROM | disk | FTP | other *** search
- #============================================================================
- #
- # AppConfig::State.pm
- #
- # Perl5 module in which configuration information for an application can
- # be stored and manipulated. AppConfig::State objects maintain knowledge
- # about variables; their identities, options, aliases, targets, callbacks
- # and so on. This module is used by a number of other AppConfig::* modules.
- #
- # Written by Andy Wardley <abw@wardley.org>
- #
- # Copyright (C) 1997-2003 Andy Wardley. All Rights Reserved.
- # Copyright (C) 1997,1998 Canon Research Centre Europe Ltd.
- #
- # $Id: State.pm,v 1.61 2004/02/04 10:11:23 abw Exp $
- #
- #----------------------------------------------------------------------------
- #
- # TODO
- #
- # * Change varlist() to varhash() and provide another varlist() method
- # which returns a list. Multiple parameters passed implies a hash
- # slice/list grep, a single parameter should indicate a regex.
- #
- # * Perhaps allow a callback to be installed which is called *instead* of
- # the get() and set() methods (or rather, is called by them).
- #
- # * Maybe CMDARG should be in there to specify extra command-line only
- # options that get added to the AppConfig::GetOpt alias construction,
- # but not applied in config files, general usage, etc. The GLOBAL
- # CMDARG might be specified as a format, e.g. "-%c" where %s = name,
- # %c = first character, %u - first unique sequence(?). Will
- # GetOpt::Long handle --long to -l application automagically?
- #
- # * ..and an added thought is that CASE sensitivity may be required for the
- # command line (-v vs -V, -r vs -R, for example), but not for parsing
- # config files where you may wish to treat "Name", "NAME" and "name" alike.
- #
- #============================================================================
-
- package AppConfig::State;
-
- require 5.004;
-
- use strict;
- use vars qw( $VERSION $AUTOLOAD $DEBUG );
-
- # need access to AppConfig::ARGCOUNT_*
- use AppConfig qw(:argcount);
-
- $VERSION = sprintf("%d.%02d", q$Revision: 1.61 $ =~ /(\d+)\.(\d+)/);
- $DEBUG = 0;
-
- # internal per-variable hashes that AUTOLOAD should provide access to
- my %METHVARS;
- @METHVARS{ qw( EXPAND ARGS ARGCOUNT ) } = ();
-
- # internal values that AUTOLOAD should provide access to
- my %METHFLAGS;
- @METHFLAGS{ qw( PEDANTIC ) } = ();
-
- # variable attributes that may be specified in GLOBAL;
- my @GLOBAL_OK = qw( DEFAULT EXPAND VALIDATE ACTION ARGS ARGCOUNT );
-
-
- #------------------------------------------------------------------------
- # new(\%config, @vars)
- #
- # Module constructor. A reference to a hash array containing
- # configuration options may be passed as the first parameter. This is
- # passed off to _configure() for processing. See _configure() for
- # information about configurarion options. The remaining parameters
- # may be variable definitions and are passed en masse to define() for
- # processing.
- #
- # Returns a reference to a newly created AppConfig::State object.
- #------------------------------------------------------------------------
-
- sub new {
- my $class = shift;
-
- my $self = {
- # internal hash arrays to store variable specification information
- VARIABLE => { }, # variable values
- DEFAULT => { }, # default values
- ALIAS => { }, # known aliases ALIAS => VARIABLE
- ALIASES => { }, # reverse alias lookup VARIABLE => ALIASES
- ARGCOUNT => { }, # arguments expected
- ARGS => { }, # specific argument pattern (AppConfig::Getopt)
- EXPAND => { }, # variable expansion (AppConfig::File)
- VALIDATE => { }, # validation regexen or functions
- ACTION => { }, # callback functions for when variable is set
- GLOBAL => { }, # default global settings for new variables
-
- # other internal data
- CREATE => 0, # auto-create variables when set
- CASE => 0, # case sensitivity flag (1 = sensitive)
- PEDANTIC => 0, # return immediately on parse warnings
- EHANDLER => undef, # error handler (let's hope we don't need it!)
- ERROR => '', # error message
- };
-
- bless $self, $class;
-
- # configure if first param is a config hash ref
- $self->_configure(shift)
- if ref($_[0]) eq 'HASH';
-
- # call define(@_) to handle any variables definitions
- $self->define(@_)
- if @_;
-
- return $self;
- }
-
-
- #------------------------------------------------------------------------
- # define($variable, \%cfg, [$variable, \%cfg, ...])
- #
- # Defines one or more variables. The first parameter specifies the
- # variable name. The following parameter may reference a hash of
- # configuration options for the variable. Further variables and
- # configuration hashes may follow and are processed in turn. If the
- # parameter immediately following a variable name isn't a hash reference
- # then it is ignored and the variable is defined without a specific
- # configuration, although any default parameters as specified in the
- # GLOBAL option will apply.
- #
- # The $variable value may contain an alias/args definition in compact
- # format, such as "Foo|Bar=1".
- #
- # A warning is issued (via _error()) if an invalid option is specified.
- #------------------------------------------------------------------------
-
- sub define {
- my $self = shift;
- my ($var, $args, $count, $opt, $val, $cfg, @names);
-
- while (@_) {
- $var = shift;
- $cfg = ref($_[0]) eq 'HASH' ? shift : { };
-
- # variable may be specified in compact format, 'foo|bar=i@'
- if ($var =~ s/(.+?)([!+=:].*)/$1/) {
-
- # anything coming after the name|alias list is the ARGS
- $cfg->{ ARGS } = $2
- if length $2;
- }
-
- # examine any ARGS option
- if (defined ($args = $cfg->{ ARGS })) {
- ARGGCOUNT: {
- $count = ARGCOUNT_NONE, last if $args =~ /^!/;
- $count = ARGCOUNT_LIST, last if $args =~ /@/;
- $count = ARGCOUNT_HASH, last if $args =~ /%/;
- $count = ARGCOUNT_ONE;
- }
- $cfg->{ ARGCOUNT } = $count;
- }
-
- # split aliases out
- @names = split(/\|/, $var);
- $var = shift @names;
- $cfg->{ ALIAS } = [ @names ] if @names;
-
- # variable name gets folded to lower unless CASE sensitive
- $var = lc $var unless $self->{ CASE };
-
- # activate $variable (so it does 'exist()')
- $self->{ VARIABLE }->{ $var } = undef;
-
- # merge GLOBAL and variable-specific configurations
- $cfg = { %{ $self->{ GLOBAL } }, %$cfg };
-
- # examine each variable configuration parameter
- while (($opt, $val) = each %$cfg) {
- $opt = uc $opt;
-
- # DEFAULT, VALIDATE, EXPAND, ARGS and ARGCOUNT are stored as
- # they are;
- $opt =~ /^DEFAULT|VALIDATE|EXPAND|ARGS|ARGCOUNT$/ && do {
- $self->{ $opt }->{ $var } = $val;
- next;
- };
-
- # CMDARG has been deprecated
- $opt eq 'CMDARG' && do {
- $self->_error("CMDARG has been deprecated. "
- . "Please use an ALIAS if required.");
- next;
- };
-
- # ACTION should be a code ref
- $opt eq 'ACTION' && do {
- unless (ref($val) eq 'CODE') {
- $self->_error("'$opt' value is not a code reference");
- next;
- };
-
- # store code ref, forcing keyword to upper case
- $self->{ ACTION }->{ $var } = $val;
-
- next;
- };
-
- # ALIAS creates alias links to the variable name
- $opt eq 'ALIAS' && do {
-
- # coerce $val to an array if not already so
- $val = [ split(/\|/, $val) ]
- unless ref($val) eq 'ARRAY';
-
- # fold to lower case unless CASE sensitivity set
- unless ($self->{ CASE }) {
- @$val = map { lc } @$val;
- }
-
- # store list of aliases...
- $self->{ ALIASES }->{ $var } = $val;
-
- # ...and create ALIAS => VARIABLE lookup hash entries
- foreach my $a (@$val) {
- $self->{ ALIAS }->{ $a } = $var;
- }
-
- next;
- };
-
- # default
- $self->_error("$opt is not a valid configuration item");
- }
-
- # set variable to default value
- $self->_default($var);
-
- # DEBUG: dump new variable definition
- if ($DEBUG) {
- print STDERR "Variable defined:\n";
- $self->_dump_var($var);
- }
- }
- }
-
-
- #------------------------------------------------------------------------
- # get($variable)
- #
- # Returns the value of the variable specified, $variable. Returns undef
- # if the variable does not exists or is undefined and send a warning
- # message to the _error() function.
- #------------------------------------------------------------------------
-
- sub get {
- my $self = shift;
- my $variable = shift;
- my $negate = 0;
- my $value;
-
- # _varname returns variable name after aliasing and case conversion
- # $negate indicates if the name got converted from "no<var>" to "<var>"
- $variable = $self->_varname($variable, \$negate);
-
- # check the variable has been defined
- unless (exists($self->{ VARIABLE }->{ $variable })) {
- $self->_error("$variable: no such variable");
- return undef;
- }
-
- # DEBUG
- print STDERR "$self->get($variable) => ",
- defined $self->{ VARIABLE }->{ $variable }
- ? $self->{ VARIABLE }->{ $variable }
- : "<undef>",
- "\n"
- if $DEBUG;
-
- # return variable value, possibly negated if the name was "no<var>"
- $value = $self->{ VARIABLE }->{ $variable };
-
- return $negate ? !$value : $value;
- }
-
-
- #------------------------------------------------------------------------
- # set($variable, $value)
- #
- # Assigns the value, $value, to the variable specified.
- #
- # Returns 1 if the variable is successfully updated or 0 if the variable
- # does not exist. If an ACTION sub-routine exists for the variable, it
- # will be executed and its return value passed back.
- #------------------------------------------------------------------------
-
- sub set {
- my $self = shift;
- my $variable = shift;
- my $value = shift;
- my $negate = 0;
- my $create;
-
- # _varname returns variable name after aliasing and case conversion
- # $negate indicates if the name got converted from "no<var>" to "<var>"
- $variable = $self->_varname($variable, \$negate);
-
- # check the variable exists
- if (exists($self->{ VARIABLE }->{ $variable })) {
- # variable found, so apply any value negation
- $value = $value ? 0 : 1 if $negate;
- }
- else {
- # auto-create variable if CREATE is 1 or a pattern matching
- # the variable name (real name, not an alias)
- $create = $self->{ CREATE };
- if (defined $create
- && ($create eq '1' || $variable =~ /$create/)) {
- $self->define($variable);
-
- print STDERR "Auto-created $variable\n" if $DEBUG;
- }
- else {
- $self->_error("$variable: no such variable");
- return 0;
- }
- }
-
- # call the validate($variable, $value) method to perform any validation
- unless ($self->_validate($variable, $value)) {
- $self->_error("$variable: invalid value: $value");
- return 0;
- }
-
- # DEBUG
- print STDERR "$self->set($variable, ",
- defined $value
- ? $value
- : "<undef>",
- ")\n"
- if $DEBUG;
-
-
- # set the variable value depending on its ARGCOUNT
- my $argcount = $self->{ ARGCOUNT }->{ $variable };
- $argcount = AppConfig::ARGCOUNT_ONE unless defined $argcount;
-
- if ($argcount eq AppConfig::ARGCOUNT_LIST) {
- # push value onto the end of the list
- push(@{ $self->{ VARIABLE }->{ $variable } }, $value);
- }
- elsif ($argcount eq AppConfig::ARGCOUNT_HASH) {
- # insert "<key>=<value>" data into hash
- my ($k, $v) = split(/\s*=\s*/, $value, 2);
- # strip quoting
- $v =~ s/^(['"])(.*)\1$/$2/ if defined $v;
- $self->{ VARIABLE }->{ $variable }->{ $k } = $v;
- }
- else {
- # set simple variable
- $self->{ VARIABLE }->{ $variable } = $value;
- }
-
-
- # call any ACTION function bound to this variable
- return &{ $self->{ ACTION }->{ $variable } }($self, $variable, $value)
- if (exists($self->{ ACTION }->{ $variable }));
-
- # ...or just return 1 (ok)
- return 1;
- }
-
-
- #------------------------------------------------------------------------
- # varlist($criteria, $filter)
- #
- # Returns a hash array of all variables and values whose real names
- # match the $criteria regex pattern passed as the first parameter.
- # If $filter is set to any true value, the keys of the hash array
- # (variable names) will have the $criteria part removed. This allows
- # the caller to specify the variables from one particular [block] and
- # have the "block_" prefix removed, for example.
- #
- # TODO: This should be changed to varhash(). varlist() should return a
- # list. Also need to consider specification by list rather than regex.
- #
- #------------------------------------------------------------------------
-
- sub varlist {
- my $self = shift;
- my $criteria = shift;
- my $strip = shift;
-
- $criteria = "" unless defined $criteria;
-
- # extract relevant keys and slice out corresponding values
- my @keys = grep(/$criteria/, keys %{ $self->{ VARIABLE } });
- my @vals = @{ $self->{ VARIABLE } }{ @keys };
- my %set;
-
- # clean off the $criteria part if $strip is set
- @keys = map { s/$criteria//; $_ } @keys if $strip;
-
- # slice values into the target hash
- @set{ @keys } = @vals;
- return %set;
- }
-
-
- #------------------------------------------------------------------------
- # AUTOLOAD
- #
- # Autoload function called whenever an unresolved object method is
- # called. If the method name relates to a defined VARIABLE, we patch
- # in $self->get() and $self->set() to magically update the varaiable
- # (if a parameter is supplied) and return the previous value.
- #
- # Thus the function can be used in the folowing ways:
- # $state->variable(123); # set a new value
- # $foo = $state->variable(); # get the current value
- #
- # Returns the current value of the variable, taken before any new value
- # is set. Prints a warning if the variable isn't defined (i.e. doesn't
- # exist rather than exists with an undef value) and returns undef.
- #------------------------------------------------------------------------
-
- sub AUTOLOAD {
- my $self = shift;
- my ($variable, $attrib);
-
-
- # splat the leading package name
- ($variable = $AUTOLOAD) =~ s/.*:://;
-
- # ignore destructor
- $variable eq 'DESTROY' && return;
-
-
- # per-variable attributes and internal flags listed as keys in
- # %METHFLAGS and %METHVARS respectively can be accessed by a
- # method matching the attribute or flag name in lower case with
- # a leading underscore_
- if (($attrib = $variable) =~ s/_//g) {
- $attrib = uc $attrib;
-
- if (exists $METHFLAGS{ $attrib }) {
- return $self->{ $attrib };
- }
-
- if (exists $METHVARS{ $attrib }) {
- # next parameter should be variable name
- $variable = shift;
- $variable = $self->_varname($variable);
-
- # check we've got a valid variable
- # $self->_error("$variable: no such variable or method"),
- # return undef
- # unless exists($self->{ VARIABLE }->{ $variable });
-
- # return attribute
- return $self->{ $attrib }->{ $variable };
- }
- }
-
- # set a new value if a parameter was supplied or return the old one
- return defined($_[0])
- ? $self->set($variable, shift)
- : $self->get($variable);
- }
-
-
-
- #========================================================================
- # ----- PRIVATE METHODS -----
- #========================================================================
-
- #------------------------------------------------------------------------
- # _configure(\%cfg)
- #
- # Sets the various configuration options using the values passed in the
- # hash array referenced by $cfg.
- #------------------------------------------------------------------------
-
- sub _configure {
- my $self = shift;
- my $cfg = shift || return;
-
- # construct a regex to match values which are ok to be found in GLOBAL
- my $global_ok = join('|', @GLOBAL_OK);
-
- foreach my $opt (keys %$cfg) {
-
- # GLOBAL must be a hash ref
- $opt =~ /^GLOBALS?$/i && do {
- unless (ref($cfg->{ $opt }) eq 'HASH') {
- $self->_error("\U$opt\E parameter is not a hash ref");
- next;
- }
-
- # we check each option is ok to be in GLOBAL, but we don't do
- # any error checking on the values they contain (but should?).
- foreach my $global ( keys %{ $cfg->{ $opt } } ) {
-
- # continue if the attribute is ok to be GLOBAL
- next if ($global =~ /(^$global_ok$)/io);
-
- $self->_error( "\U$global\E parameter cannot be GLOBAL");
- }
- $self->{ GLOBAL } = $cfg->{ $opt };
- next;
- };
-
-
- # CASE, CREATE and PEDANTIC are stored as they are
- $opt =~ /^CASE|CREATE|PEDANTIC$/i && do {
- $self->{ uc $opt } = $cfg->{ $opt };
- next;
- };
-
- # ERROR triggers $self->_ehandler()
- $opt =~ /^ERROR$/i && do {
- $self->_ehandler($cfg->{ $opt });
- next;
- };
-
- # DEBUG triggers $self->_debug()
- $opt =~ /^DEBUG$/i && do {
- $self->_debug($cfg->{ $opt });
- next;
- };
-
- # warn about invalid options
- $self->_error("\U$opt\E is not a valid configuration option");
- }
- }
-
-
- #------------------------------------------------------------------------
- # _varname($variable, \$negated)
- #
- # Variable names are treated case-sensitively or insensitively, depending
- # on the value of $self->{ CASE }. When case-insensitive ($self->{ CASE }
- # != 0), all variable names are converted to lower case. Variable values
- # are not converted. This function simply converts the parameter
- # (variable) to lower case if $self->{ CASE } isn't set. _varname() also
- # expands a variable alias to the name of the target variable.
- #
- # Variables with an ARGCOUNT of ARGCOUNT_ZERO may be specified as
- # "no<var>" in which case, the intended value should be negated. The
- # leading "no" part is stripped from the variable name. A reference to
- # a scalar value can be passed as the second parameter and if the
- # _varname() method identified such a variable, it will negate the value.
- # This allows the intended value or a simple negate flag to be passed by
- # reference and be updated to indicate any negation activity taking place.
- #
- # The (possibly modified) variable name is returned.
- #------------------------------------------------------------------------
-
- sub _varname {
- my $self = shift;
- my $variable = shift;
- my $negated = shift;
-
- # convert to lower case if case insensitive
- $variable = $self->{ CASE } ? $variable : lc $variable;
-
- # get the actual name if this is an alias
- $variable = $self->{ ALIAS }->{ $variable }
- if (exists($self->{ ALIAS }->{ $variable }));
-
- # if the variable doesn't exist, we can try to chop off a leading
- # "no" and see if the remainder matches an ARGCOUNT_ZERO variable
- unless (exists($self->{ VARIABLE }->{ $variable })) {
- # see if the variable is specified as "no<var>"
- if ($variable =~ /^no(.*)/) {
- # see if the real variable (minus "no") exists and it
- # has an ARGOUNT of ARGCOUNT_NONE (or no ARGCOUNT at all)
- my $novar = $self->_varname($1);
- if (exists($self->{ VARIABLE }->{ $novar })
- && ! $self->{ ARGCOUNT }->{ $novar }) {
- # set variable name and negate value
- $variable = $novar;
- $$negated = ! $$negated if defined $negated;
- }
- }
- }
-
- # return the variable name
- $variable;
- }
-
-
- #------------------------------------------------------------------------
- # _default($variable)
- #
- # Sets the variable specified to the default value or undef if it doesn't
- # have a default. The default value is returned.
- #------------------------------------------------------------------------
-
- sub _default {
- my $self = shift;
- my $variable = shift;
-
- # _varname returns variable name after aliasing and case conversion
- $variable = $self->_varname($variable);
-
- # check the variable exists
- if (exists($self->{ VARIABLE }->{ $variable })) {
- # set variable value to the default scalar, an empty list or empty
- # hash array, depending on its ARGCOUNT value
- my $argcount = $self->{ ARGCOUNT }->{ $variable };
- $argcount = AppConfig::ARGCOUNT_ONE unless defined $argcount;
-
- if ($argcount == AppConfig::ARGCOUNT_NONE) {
- return $self->{ VARIABLE }->{ $variable }
- = $self->{ DEFAULT }->{ $variable } || 0;
- }
- elsif ($argcount == AppConfig::ARGCOUNT_LIST) {
- my $deflist = $self->{ DEFAULT }->{ $variable };
- return $self->{ VARIABLE }->{ $variable } =
- [ ref $deflist eq 'ARRAY' ? @$deflist : ( ) ];
-
- }
- elsif ($argcount == AppConfig::ARGCOUNT_HASH) {
- my $defhash = $self->{ DEFAULT }->{ $variable };
- return $self->{ VARIABLE }->{ $variable } =
- { ref $defhash eq 'HASH' ? %$defhash : () };
- }
- else {
- return $self->{ VARIABLE }->{ $variable }
- = $self->{ DEFAULT }->{ $variable };
- }
- }
- else {
- $self->_error("$variable: no such variable");
- return 0;
- }
- }
-
-
- #------------------------------------------------------------------------
- # _exists($variable)
- #
- # Returns 1 if the variable specified exists or 0 if not.
- #------------------------------------------------------------------------
-
- sub _exists {
- my $self = shift;
- my $variable = shift;
-
-
- # _varname returns variable name after aliasing and case conversion
- $variable = $self->_varname($variable);
-
- # check the variable has been defined
- return exists($self->{ VARIABLE }->{ $variable });
- }
-
-
- #------------------------------------------------------------------------
- # _validate($variable, $value)
- #
- # Uses any validation rules or code defined for the variable to test if
- # the specified value is acceptable.
- #
- # Returns 1 if the value passed validation checks, 0 if not.
- #------------------------------------------------------------------------
-
- sub _validate {
- my $self = shift;
- my $variable = shift;
- my $value = shift;
- my $validator;
-
-
- # _varname returns variable name after aliasing and case conversion
- $variable = $self->_varname($variable);
-
- # return OK unless there is a validation function
- return 1 unless defined($validator = $self->{ VALIDATE }->{ $variable });
-
- #
- # the validation performed is based on the validator type;
- #
- # CODE ref: code executed, returning 1 (ok) or 0 (failed)
- # SCALAR : a regex which should match the value
- #
-
- # CODE ref
- ref($validator) eq 'CODE' && do {
- # run the validation function and return the result
- return &$validator($variable, $value);
- };
-
- # non-ref (i.e. scalar)
- ref($validator) || do {
- # not a ref - assume it's a regex
- return $value =~ /$validator/;
- };
-
- # validation failed
- return 0;
- }
-
-
- #------------------------------------------------------------------------
- # _error($format, @params)
- #
- # Checks for the existence of a user defined error handling routine and
- # if defined, passes all variable straight through to that. The routine
- # is expected to handle a string format and optional parameters as per
- # printf(3C). If no error handler is defined, the message is formatted
- # and passed to warn() which prints it to STDERR.
- #------------------------------------------------------------------------
-
- sub _error {
- my $self = shift;
- my $format = shift;
-
- # user defined error handler?
- if (ref($self->{ EHANDLER }) eq 'CODE') {
- &{ $self->{ EHANDLER } }($format, @_);
- }
- else {
- warn(sprintf("$format\n", @_));
- }
- }
-
-
- #------------------------------------------------------------------------
- # _ehandler($handler)
- #
- # Allows a new error handler to be installed. The current value of
- # the error handler is returned.
- #
- # This is something of a kludge to allow other AppConfig::* modules to
- # install their own error handlers to format error messages appropriately.
- # For example, AppConfig::File appends a message of the form
- # "at $file line $line" to each error message generated while parsing
- # configuration files. The previous handler is returned (and presumably
- # stored by the caller) to allow new error handlers to chain control back
- # to any user-defined handler, and also restore the original handler when
- # done.
- #------------------------------------------------------------------------
-
- sub _ehandler {
- my $self = shift;
- my $handler = shift;
-
- # save previous value
- my $previous = $self->{ EHANDLER };
-
- # update internal reference if a new handler vas provide
- if (defined $handler) {
- # check this is a code reference
- if (ref($handler) eq 'CODE') {
- $self->{ EHANDLER } = $handler;
-
- # DEBUG
- print STDERR "installed new ERROR handler: $handler\n" if $DEBUG;
- }
- else {
- $self->_error("ERROR handler parameter is not a code ref");
- }
- }
-
- return $previous;
- }
-
-
- #------------------------------------------------------------------------
- # _debug($debug)
- #
- # Sets the package debugging variable, $AppConfig::State::DEBUG depending
- # on the value of the $debug parameter. 1 turns debugging on, 0 turns
- # debugging off.
- #
- # May be called as an object method, $state->_debug(1), or as a package
- # function, AppConfig::State::_debug(1). Returns the previous value of
- # $DEBUG, before any new value was applied.
- #------------------------------------------------------------------------
-
- sub _debug {
- # object reference may not be present if called as a package function
- my $self = shift if ref($_[0]);
- my $newval = shift;
-
- # save previous value
- my $oldval = $DEBUG;
-
- # update $DEBUG if a new value was provided
- $DEBUG = $newval if defined $newval;
-
- # return previous value
- $oldval;
- }
-
-
- #------------------------------------------------------------------------
- # _dump_var($var)
- #
- # Displays the content of the specified variable, $var.
- #------------------------------------------------------------------------
-
- sub _dump_var {
- my $self = shift;
- my $var = shift;
-
- return unless defined $var;
-
- # $var may be an alias, so we resolve the real variable name
- my $real = $self->_varname($var);
- if ($var eq $real) {
- print STDERR "$var\n";
- }
- else {
- print STDERR "$real ('$var' is an alias)\n";
- $var = $real;
- }
-
- # for some bizarre reason, the variable VALUE is stored in VARIABLE
- # (it made sense at some point in time)
- printf STDERR " VALUE => %s\n",
- defined($self->{ VARIABLE }->{ $var })
- ? $self->{ VARIABLE }->{ $var }
- : "<undef>";
-
- # the rest of the values can be read straight out of their hashes
- foreach my $param (qw( DEFAULT ARGCOUNT VALIDATE ACTION EXPAND )) {
- printf STDERR " %-12s => %s\n", $param,
- defined($self->{ $param }->{ $var })
- ? $self->{ $param }->{ $var }
- : "<undef>";
- }
-
- # summarise all known aliases for this variable
- print STDERR " ALIASES => ",
- join(", ", @{ $self->{ ALIASES }->{ $var } }), "\n"
- if defined $self->{ ALIASES }->{ $var };
- }
-
-
- #------------------------------------------------------------------------
- # _dump()
- #
- # Dumps the contents of the Config object and all stored variables.
- #------------------------------------------------------------------------
-
- sub _dump {
- my $self = shift;
- my $var;
-
- print STDERR "=" x 71, "\n";
- print STDERR
- "Status of AppConfig::State (version $VERSION) object:\n\t$self\n";
-
-
- print STDERR "- " x 36, "\nINTERNAL STATE:\n";
- foreach (qw( CREATE CASE PEDANTIC EHANDLER ERROR )) {
- printf STDERR " %-12s => %s\n", $_,
- defined($self->{ $_ }) ? $self->{ $_ } : "<undef>";
- }
-
- print STDERR "- " x 36, "\nVARIABLES:\n";
- foreach $var (keys %{ $self->{ VARIABLE } }) {
- $self->_dump_var($var);
- }
-
- print STDERR "- " x 36, "\n", "ALIASES:\n";
- foreach $var (keys %{ $self->{ ALIAS } }) {
- printf(" %-12s => %s\n", $var, $self->{ ALIAS }->{ $var });
- }
- print STDERR "=" x 72, "\n";
- }
-
-
-
- 1;
-
- __END__
-
- =head1 NAME
-
- AppConfig::State - application configuration state
-
- =head1 SYNOPSIS
-
- use AppConfig::State;
-
- my $state = AppConfig::State->new(\%cfg);
-
- $state->define("foo"); # very simple variable definition
- $state->define("bar", \%varcfg); # variable specific configuration
- $state->define("foo|bar=i@"); # compact format
-
- $state->set("foo", 123); # trivial set/get examples
- $state->get("foo");
-
- $state->foo(); # shortcut variable access
- $state->foo(456); # shortcut variable update
-
- =head1 OVERVIEW
-
- AppConfig::State is a Perl5 module to handle global configuration variables
- for perl programs. It maintains the state of any number of variables,
- handling default values, aliasing, validation, update callbacks and
- option arguments for use by other AppConfig::* modules.
-
- AppConfig::State is distributed as part of the AppConfig bundle.
-
- =head1 DESCRIPTION
-
- =head2 USING THE AppConfig::State MODULE
-
- To import and use the AppConfig::State module the following line should
- appear in your Perl script:
-
- use AppConfig::State;
-
- The AppConfig::State module is loaded automatically by the new()
- constructor of the AppConfig module.
-
- AppConfig::State is implemented using object-oriented methods. A
- new AppConfig::State object is created and initialised using the
- new() method. This returns a reference to a new AppConfig::State
- object.
-
- my $state = AppConfig::State->new();
-
- This will create a reference to a new AppConfig::State with all
- configuration options set to their default values. You can initialise
- the object by passing a reference to a hash array containing
- configuration options:
-
- $state = AppConfig::State->new( {
- CASE => 1,
- ERROR => \&my_error,
- } );
-
- The new() constructor of the AppConfig module automatically passes all
- parameters to the AppConfig::State new() constructor. Thus, any global
- configuration values and variable definitions for AppConfig::State are
- also applicable to AppConfig.
-
- The following configuration options may be specified.
-
- =over 4
-
- =item CASE
-
- Determines if the variable names are treated case sensitively. Any non-zero
- value makes case significant when naming variables. By default, CASE is set
- to 0 and thus "Variable", "VARIABLE" and "VaRiAbLe" are all treated as
- "variable".
-
- =item CREATE
-
- By default, CREATE is turned off meaning that all variables accessed via
- set() (which includes access via shortcut such as
- C<$state-E<gt>variable($value)> which delegates to set()) must previously
- have been defined via define(). When CREATE is set to 1, calling
- set($variable, $value) on a variable that doesn't exist will cause it
- to be created automatically.
-
- When CREATE is set to any other non-zero value, it is assumed to be a
- regular expression pattern. If the variable name matches the regex, the
- variable is created. This can be used to specify configuration file
- blocks in which variables should be created, for example:
-
- $state = AppConfig::State->new( {
- CREATE => '^define_',
- } );
-
- In a config file:
-
- [define]
- name = fred # define_name gets created automatically
-
- [other]
- name = john # other_name doesn't - warning raised
-
- Note that a regex pattern specified in CREATE is applied to the real
- variable name rather than any alias by which the variables may be
- accessed.
-
- =item PEDANTIC
-
- The PEDANTIC option determines what action the configuration file
- (AppConfig::File) or argument parser (AppConfig::Args) should take
- on encountering a warning condition (typically caused when trying to set an
- undeclared variable). If PEDANTIC is set to any true value, the parsing
- methods will immediately return a value of 0 on encountering such a
- condition. If PEDANTIC is not set, the method will continue to parse the
- remainder of the current file(s) or arguments, returning 0 when complete.
-
- If no warnings or errors are encountered, the method returns 1.
-
- In the case of a system error (e.g. unable to open a file), the method
- returns undef immediately, regardless of the PEDANTIC option.
-
- =item ERROR
-
- Specifies a user-defined error handling routine. When the handler is
- called, a format string is passed as the first parameter, followed by
- any additional values, as per printf(3C).
-
- =item DEBUG
-
- Turns debugging on or off when set to 1 or 0 accordingly. Debugging may
- also be activated by calling _debug() as an object method
- (C<$state-E<gt>_debug(1)>) or as a package function
- (C<AppConfig::State::_debug(1)>), passing in a true/false value to
- set the debugging state accordingly. The package variable
- $AppConfig::State::DEBUG can also be set directly.
-
- The _debug() method returns the current debug value. If a new value
- is passed in, the internal value is updated, but the previous value is
- returned.
-
- Note that any AppConfig::File or App::Config::Args objects that are
- instantiated with a reference to an App::State will inherit the
- DEBUG (and also PEDANTIC) values of the state at that time. Subsequent
- changes to the AppConfig::State debug value will not affect them.
-
- =item GLOBAL
-
- The GLOBAL option allows default values to be set for the DEFAULT, ARGCOUNT,
- EXPAND, VALIDATE and ACTION options for any subsequently defined variables.
-
- $state = AppConfig::State->new({
- GLOBAL => {
- DEFAULT => '<undef>', # default value for new vars
- ARGCOUNT => 1, # vars expect an argument
- ACTION => \&my_set_var, # callback when vars get set
- }
- });
-
- Any attributes specified explicitly when a variable is defined will
- override any GLOBAL values.
-
- See L<DEFINING VARIABLES> below which describes these options in detail.
-
- =back
-
- =head2 DEFINING VARIABLES
-
- The C<define()> function is used to pre-declare a variable and specify
- its configuration.
-
- $state->define("foo");
-
- In the simple example above, a new variable called "foo" is defined. A
- reference to a hash array may also be passed to specify configuration
- information for the variable:
-
- $state->define("foo", {
- DEFAULT => 99,
- ALIAS => 'metavar1',
- });
-
- Any variable-wide GLOBAL values passed to the new() constructor in the
- configuration hash will also be applied. Values explicitly specified
- in a variable's define() configuration will override the respective GLOBAL
- values.
-
- The following configuration options may be specified
-
- =over 4
-
- =item DEFAULT
-
- The DEFAULT value is used to initialise the variable.
-
- $state->define("drink", {
- DEFAULT => 'coffee',
- });
-
- print $state->drink(); # prints "coffee"
-
- =item ALIAS
-
- The ALIAS option allows a number of alternative names to be specified for
- this variable. A single alias should be specified as a string. Multiple
- aliases can be specified as a reference to an array of alternatives or as
- a string of names separated by vertical bars, '|'. e.g.:
-
- $state->define("name", {
- ALIAS => 'person',
- });
- or
- $state->define("name", {
- ALIAS => [ 'person', 'user', 'uid' ],
- });
- or
- $state->define("name", {
- ALIAS => 'person|user|uid',
- });
-
- $state->user('abw'); # equivalent to $state->name('abw');
-
- =item ARGCOUNT
-
- The ARGCOUNT option specifies the number of arguments that should be
- supplied for this variable. By default, no additional arguments are
- expected for variables (ARGCOUNT_NONE).
-
- The ARGCOUNT_* constants can be imported from the AppConfig module:
-
- use AppConfig ':argcount';
-
- $state->define('foo', { ARGCOUNT => ARGCOUNT_ONE });
-
- or can be accessed directly from the AppConfig package:
-
- use AppConfig;
-
- $state->define('foo', { ARGCOUNT => AppConfig::ARGCOUNT_ONE });
-
- The following values for ARGCOUNT may be specified.
-
- =over 4
-
- =item ARGCOUNT_NONE (0)
-
- Indicates that no additional arguments are expected. If the variable is
- identified in a confirguration file or in the command line arguments, it
- is set to a value of 1 regardless of whatever arguments follow it.
-
- =item ARGCOUNT_ONE (1)
-
- Indicates that the variable expects a single argument to be provided.
- The variable value will be overwritten with a new value each time it
- is encountered.
-
- =item ARGCOUNT_LIST (2)
-
- Indicates that the variable expects multiple arguments. The variable
- value will be appended to the list of previous values each time it is
- encountered.
-
- =item ARGCOUNT_HASH (3)
-
- Indicates that the variable expects multiple arguments and that each
- argument is of the form "key=value". The argument will be split into
- a key/value pair and inserted into the hash of values each time it
- is encountered.
-
- =back
-
- =item ARGS
-
- The ARGS option can also be used to specify advanced command line options
- for use with AppConfig::Getopt, which itself delegates to Getopt::Long.
- See those two modules for more information on the format and meaning of
- these options.
-
- $state->define("name", {
- ARGS => "=i@",
- });
-
- =item EXPAND
-
- The EXPAND option specifies how the AppConfig::File processor should
- expand embedded variables in the configuration file values it reads.
- By default, EXPAND is turned off (EXPAND_NONE) and no expansion is made.
-
- The EXPAND_* constants can be imported from the AppConfig module:
-
- use AppConfig ':expand';
-
- $state->define('foo', { EXPAND => EXPAND_VAR });
-
- or can be accessed directly from the AppConfig package:
-
- use AppConfig;
-
- $state->define('foo', { EXPAND => AppConfig::EXPAND_VAR });
-
- The following values for EXPAND may be specified. Multiple values should
- be combined with vertical bars , '|', e.g. C<EXPAND_UID | EXPAND_VAR>).
-
- =over 4
-
- =item EXPAND_NONE
-
- Indicates that no variable expansion should be attempted.
-
- =item EXPAND_VAR
-
- Indicates that variables embedded as $var or $(var) should be expanded
- to the values of the relevant AppConfig::State variables.
-
- =item EXPAND_UID
-
- Indicates that '~' or '~uid' patterns in the string should be
- expanded to the current users ($<), or specified user's home directory.
-
- =item EXPAND_ENV
-
- Inidicates that variables embedded as ${var} should be expanded to the
- value of the relevant environment variable.
-
- =item EXPAND_ALL
-
- Equivalent to C<EXPAND_VARS | EXPAND_UIDS | EXPAND_ENVS>).
-
- =item EXPAND_WARN
-
- Indicates that embedded variables that are not defined should raise a
- warning. If PEDANTIC is set, this will cause the read() method to return 0
- immediately.
-
- =back
-
- =item VALIDATE
-
- Each variable may have a sub-routine or regular expression defined which
- is used to validate the intended value for a variable before it is set.
-
- If VALIDATE is defined as a regular expression, it is applied to the
- value and deemed valid if the pattern matches. In this case, the
- variable is then set to the new value. A warning message is generated
- if the pattern match fails.
-
- VALIDATE may also be defined as a reference to a sub-routine which takes
- as its arguments the name of the variable and its intended value. The
- sub-routine should return 1 or 0 to indicate that the value is valid
- or invalid, respectively. An invalid value will cause a warning error
- message to be generated.
-
- If the GLOBAL VALIDATE variable is set (see GLOBAL in L<DESCRIPTION>
- above) then this value will be used as the default VALIDATE for each
- variable unless otherwise specified.
-
- $state->define("age", {
- VALIDATE => '\d+',
- });
-
- $state->define("pin", {
- VALIDATE => \&check_pin,
- });
-
- =item ACTION
-
- The ACTION option allows a sub-routine to be bound to a variable as a
- callback that is executed whenever the variable is set. The ACTION is
- passed a reference to the AppConfig::State object, the name of the
- variable and the value of the variable.
-
- The ACTION routine may be used, for example, to post-process variable
- data, update the value of some other dependant variable, generate a
- warning message, etc.
-
- Example:
-
- $state->define("foo", { ACTION => \&my_notify });
-
- sub my_notify {
- my $state = shift;
- my $var = shift;
- my $val = shift;
-
- print "$variable set to $value";
- }
-
- $state->foo(42); # prints "foo set to 42"
-
- Be aware that calling C<$state-E<gt>set()> to update the same variable
- from within the ACTION function will cause a recursive loop as the
- ACTION function is repeatedly called.
-
- =item
-
- =back
-
- =head2 DEFINING VARIABLES USING THE COMPACT FORMAT
-
- Variables may be defined in a compact format which allows any ALIAS and
- ARGS values to be specified as part of the variable name. This is designed
- to mimic the behaviour of Johan Vromans' Getopt::Long module.
-
- Aliases for a variable should be specified after the variable name,
- separated by vertical bars, '|'. Any ARGS parameter should be appended
- after the variable name(s) and/or aliases.
-
- The following examples are equivalent:
-
- $state->define("foo", {
- ALIAS => [ 'bar', 'baz' ],
- ARGS => '=i',
- });
-
- $state->define("foo|bar|baz=i");
-
- =head2 READING AND MODIFYING VARIABLE VALUES
-
- AppConfig::State defines two methods to manipulate variable values:
-
- set($variable, $value);
- get($variable);
-
- Both functions take the variable name as the first parameter and
- C<set()> takes an additional parameter which is the new value for the
- variable. C<set()> returns 1 or 0 to indicate successful or
- unsuccessful update of the variable value. If there is an ACTION
- routine associated with the named variable, the value returned will be
- passed back from C<set()>. The C<get()> function returns the current
- value of the variable.
-
- Once defined, variables may be accessed directly as object methods where
- the method name is the same as the variable name. i.e.
-
- $state->set("verbose", 1);
-
- is equivalent to
-
- $state->verbose(1);
-
- Without parameters, the current value of the variable is returned. If
- a parameter is specified, the variable is set to that value and the
- result of the set() operation is returned.
-
- $state->age(29); # sets 'age' to 29, returns 1 (ok)
-
- =head2 INTERNAL METHODS
-
- The interal (private) methods of the AppConfig::State class are listed
- below.
-
- They aren't intended for regular use and potential users should consider
- the fact that nothing about the internal implementation is guaranteed to
- remain the same. Having said that, the AppConfig::State class is
- intended to co-exist and work with a number of other modules and these
- are considered "friend" classes. These methods are provided, in part,
- as services to them. With this acknowledged co-operation in mind, it is
- safe to assume some stability in this core interface.
-
- The _varname() method can be used to determine the real name of a variable
- from an alias:
-
- $varname->_varname($alias);
-
- Note that all methods that take a variable name, including those listed
- below, can accept an alias and automatically resolve it to the correct
- variable name. There is no need to call _varname() explicitly to do
- alias expansion. The _varname() method will fold all variables names
- to lower case unless CASE sensititvity is set.
-
- The _exists() method can be used to check if a variable has been
- defined:
-
- $state->_exists($varname);
-
- The _default() method can be used to reset a variable to its default value:
-
- $state->_default($varname);
-
- The _expand() method can be used to determine the EXPAND value for a
- variable:
-
- print "$varname EXPAND: ", $state->_expand($varname), "\n";
-
- The _argcount() method returns the value of the ARGCOUNT attribute for a
- variable:
-
- print "$varname ARGCOUNT: ", $state->_argcount($varname), "\n";
-
- The _validate() method can be used to determine if a new value for a variable
- meets any validation criteria specified for it. The variable name and
- intended value should be passed in. The methods returns a true/false value
- depending on whether or not the validation succeeded:
-
- print "OK\n" if $state->_validate($varname, $value);
-
- The _pedantic() method can be called to determine the current value of the
- PEDANTIC option.
-
- print "pedantic mode is ", $state->_pedantic() ? "on" ; "off", "\n";
-
- The _debug() method can be used to turn debugging on or off (pass 1 or 0
- as a parameter). It can also be used to check the debug state,
- returning the current internal value of $AppConfig::State::DEBUG. If a
- new debug value is provided, the debug state is updated and the previous
- state is returned.
-
- $state->_debug(1); # debug on, returns previous value
-
- The _dump_var($varname) and _dump() methods may also be called for
- debugging purposes.
-
- $state->_dump_var($varname); # show variable state
- $state->_dump(); # show internal state and all vars
-
- =head1 AUTHOR
-
- Andy Wardley, E<lt>abw@wardley.orgE<gt>
-
- =head1 REVISION
-
- $Revision: 1.61 $
-
- =head1 COPYRIGHT
-
- Copyright (C) 1997-2003 Andy Wardley. All Rights Reserved.
-
- Copyright (C) 1997,1998 Canon Research Centre Europe Ltd.
-
- This module is free software; you can redistribute it and/or modify it
- under the same terms as Perl itself.
-
- =head1 SEE ALSO
-
- AppConfig, AppConfig::File, AppConfig::Args, AppConfig::Getopt
-
- =cut
-