NAME

Config::General::Hierarchical - Hierarchical Generic Config Module

SYNOPSIS

Simple use

use Config::General::Hierarchical;
#
my $cfg = Config::General::Hierarchical->new( file => $filename );
my $value = $cfg->_ConfigurationVariableName;

Full use

package MyConfig;
#
use base 'Config::General::Hierarchical';
#
sub syntax {
 ...
}

DESCRIPTION

This module provides easy ways to achieve three goals: to read configuration values that are organized in complex structures and stored in a hierarchical structure of files, to access them, and to define syntax and structure constraints.

HOW CONFIGURATION DATA ARE ORGANIZED

To make the structure constraints easy to be managed, a good way is to force the configuration structure to a tree of named nodes; each one can be either a parent node or a value node, if a value can be a string or an array of strings then this structure can be aesily stored in an perl hash where for each key can be stored a reference to another hash, a reference to an array of scalars or a scalar.

This configuration example

<db>
  <*>
    tout 300
  </*>
  <customers>
    host customersdb.${DBServersDomain}
    name customersdb
    user customerslogin
    pass customerspwd
  </customers>
  <products>
    host productsdb.${DBServersDomain}
    name productsdb
    user productslogin
    pass productspwd
    tout 600
  </products>
  <users>
    host usersdb.${DBServersDomain}
    name usersdb
    user userslogin
    pass userspwd
  </users>
</db>
DBServersDomain my.domain

is equivalent to this code

my $cfg = {
    db => {
        customers => {
            host => 'customersdb.my.domain',
            name => 'customersdb',
            user => 'customerslogin',
            pass => 'customerspwd',
            tout => 300,
        },
        products  => {
            host => 'productsdb.my.domain',
            name => 'productsdb',
            user => 'productslogin',
            pass => 'productspwd',
            tout => 600,
        },
        users     => {
            host => 'usersdb.my.domain',
            name => 'usersdb',
            user => 'userslogin',
            pass => 'userspwd',
            tout => 300,
        },
    },
    DBServersDomain => 'my.domain',
};

HOW CONFIGURATION FILES ARE READ

For the purpose to read and to parse configuration files Config::General is used, so it is better if you introduce yourself to that module before going on reading this chapter: it is written assuming that the reader knows how Config::General reads and parses files.

This is how Config::General::Hierarchical inizializes the Config::General object.

Config::General->new(
  '-AllowMultiOptions'     => 1,
  '-AutoTrue'              => 0,
  '-BackslashEscape'       => 0,
  '-CComments'             => 0,
  '-ConfigFile'            => $filename,
  '-ExtendedAccess'        => 0,
  '-InterPolateEnv'        => 0,
  '-InterPolateVars'       => 0,
  '-MergeDuplicateBlocks'  => 1,
  '-MergeDuplicateOptions' => 0,
  '-SlashIsDirectory'      => 0,
  '-UseApacheInclude'      => 0,
);

Inizializing the Config::General module with both the parameters -MergeDuplicateBlocks and -AllowMultiOptions to true and -MergeDuplicateOptions to false, it reads and parses the file in a structure respecting the structure constraint. Beeing this module written for configuration file, neithr -ConfigHash nor -String are used, but -ConfigFile is used for each file to read. The parameters -AutoLaunder, -CComments, -LowerCaseNames, -SplitDelimiter and -SplitPolicy are presetted or unsetted, but left at your control: new() methot proxies theese parameters.

An overview on other Config::General parameters:

-AutoTrue

0: this module provides its own way to normalize and check theese values

-BackslashEscape

0: this module provides its own way to interpolate backslashes

-DefaultConfig

not used: I think this is not a usefull option for the purpose to read more than one file

-ExtendedAccess

0: this module provides its own easy way to access data

-FlagBits

not used yet: TODO

-InterPolateEnv -InterPolateVars

0: this module provides its own way to interpolate values

-SlashIsDirectory

0: we don't need to be compliant with apache

-Tie

not used yet: TODO

-UseApacheInclude

0: this module provides its own way to include other files: the inherits directive; other parameters changing the include beheavure of Config::General are simply not used and not mentioned as well

SUBROUTINES/METHODS

All theese methods (execpt for the new() one) are for internal use, but having you probably to write a class that hinerits this one, it can be a good thing if you know how the module works.

new()

synopsis
$cfg = Config::General::Hierarchical->new( %options );
return value

Returns a new constructed Config::General::Hierarchical object.

description

Some %options can be specified by and hash.

-AutoLaunder -CComments -LowerCaseNames -SplitDelimiter -SplitPolicy

Proxied to Config::General.

check 1

This make the new() method to implicitally call the check() method as well.

file <string>

This make the new() method to implicitally call the read() method as well.

inherits <string>

Redefines the default inherits syntax of the same directive.

undefined <string>

Redefines the default undefined syntax of the same directive.

wild <string>

It defines the wild string. By default it values '*'. If used as key of any node (etiher in configuration or in the syntax constraint), the relative value is used as default value (or syntax) for every key requested for that node.

check()

synopsis
$cfg->check;
return value

If it does not die, returns the node itself.

description

This mothod calls the get() method for each key of the node with two effects: if the method returns, all the variables for that node respect the syntax constraint, all the values are now cached.

import()

synopsis
use Config::General::Hierarchical;
description

This mothod performs the checks on the correct usage of the syntax method. This means that if there is an error in the syntax constraint it is notifeied to the developer at compile time.

get()

synopsis
my $value = $cfg->get( 'VariableName' );
# alias
my $value = $cfg->_VariableName;

my $value = $cfg->get( 'VariableName', 'SubNode' );
# alias
my $value = $cfg->_VariableName->get( 'SubNode' );
# alias
my $value = $cfg->_VariableName->_SubNode;

my $value = $cfg->get( 'VariableName', ... );
# alias
my $value = $cfg->_VariableName( ... );
return value

Returns the value of the configuration variable VariableName.

description

Accessing configuration data by this method you can be sure that the returned value respects the syntax constraint, if this is not the case, a die() is called and any value is returned. You can be sure as well that the returned value has the appropriate type defined by the syntax constraint, this means that when a configuration variable is defined as a node getting its value you will obtain a reference to a Config::General::Hierarchical object, when it is defined as an array you will obtain a reference to an ARRAY (even if empty), otherwise you will get a scalar.

A quicker to write way to access data is provided with AUTOLOAD mothod: you can get the value of a variable by calling that method called as the name of the variable prependend by an underscore.

getk()

synopsis
my @keys = $cfg->getk;
description

This mothod returns the array with all the keys configured in the configuration files for the node.

read()

synopsis
$cfg->read( $filename );
return value

Returns the Config::General::Hierarchical object itself.

description

Reads and parses all the file structure, beginning from $filename and following its hierarchical structure. It dies on error or if called twice. By this method the Config::General::Hierarchical object becames a node: the configuration root node, which is the only one without a name: you can reference it by the object.

syntax()

synopsis
package MyConfig;
#
use base 'Config::General::Hierarchical';
#
sub syntax {
  my ( $self ) = @_;
  my %constraint = ( ... );
  return $self->merge_values( \%constraint, $self->SUPER::syntax );
}
return value

It must return the reference to the hash describing the syntax constraint.

description

This method is called by get(), import() and read() methods in order to check the struscture syntax of the red configuration.

DIRECTIVES

There are some configuration variable which Config::General::Hierarchical handles as directives. This means that will there be some keywords that will can not be used neither as configuration variable name nor as node name. Anyway, if you strongly need to use a configuration variable name which is a directive name, you can redefine the keyword for each directive by this way:

# This make the include keyword now be handled as the inherits directive
my $cfg = Config::General::Hierarchical->new( inherits => 'include' );

The same directive can be used more than once in the same configuration file.

inherits

It specifies a file to inherit. It take one argument: the name of the file to inherit. If used twice (or more) undefined configuration variables are inherited from the last file in order to create a temporary configuration tree which inherits the file specified by previous inherits directive, an so on...

undefined

It forces a variable to have an undefined value even if it have some value defined in the inherited structure. It can be specified more than once and it can be used as key of a node with the same purpose.

The undefined directive takes precedence on the value. In the following exaple undef is returned.

# configuration
<node>
 key 1
 undefined key
</node>

# code
$cfg->_node->_key;

wild

It's default value is '*'. It can be used to specify default value or syntax constraint for all the other key not specified and requested for the same node where a wild key is defined. For example it can be used to specify that every database timeout must be and integer and that its default value is 300: in the following example both $cfg->_db->_customers->_tout and $cfg->_db->_users->_tout will return 300 while $cfg->_db->_products->_tout will returns 600 and $cfg->_db->_example->_tout will die.

# configuration
<db>
  <*>
    tout 300
  </*>
  <customers>
    host customersdb.${DBServersDomain}
    name customersdb
    user customerslogin
    pass customerspwd
  </customers>
  <example>
    tout alphanumeric
  </example>
  <products>
    host productsdb.${DBServersDomain}
    name productsdb
    user productslogin
    pass productspwd
    tout 600
  </products>
  <users>
    host usersdb.${DBServersDomain}
    name usersdb
    user userslogin
    pass userspwd
  </users>
</db>
DBServersDomain my.domain

# code
package MyConfig;
#
use base 'Config::General::Hierarchical';
#
sub syntax {
  my ( $self ) = @_;
  my %constraint = ( db => {
    '*' => {
      tout => 'I'
    }
  } );
  return $self->merge_values( \%constraint, $self->SUPER::syntax );
}

SYNTAX CONSTRAINT

The syntax constraint specifies the option variable tree and the syntax that variables must respect. To specify the structure and the syntax this module uses an hash for each node where each key is the name of a configuration variable and values can be either a string defining the variable syntax or a reference to an other hash if the configuration variable is a node. A variable syntax can contains an uppercase letter to specify the type of the configuration variable and/or some lowercase letter to specify some flags. It is not mandatory to specify every key! When there is any specification for a configuration variable requested by get any check is performed if it is a node otherwise the value must be simply defined. The syntax constraint is checked when the get method is called: if the configuration variable doesn't respect the syntax a die is called.

TYPES

The type is specified by an uppercase letter, if not specified the default is string.

datetime

A - a date and time value: 'YYYY-mm-dd HH:MM:SS'

boolean

B - a boolean value

date

D - a date: 'YYYY-mm-dd'

e-mail

E - an e-mail address

integer

I - an integer number

number

N - a floating point numer

string

S - a string even if empty

time

T - a time: 'HH:MM:SS'

FLAGS

The flags are specified by a lowercase letter.

array

a - the variable is an array: when the value is getted a reference to an ARRAY is returned

merge

m - the hinerited value is merged instead of overwritten; it can be used only for strings and arrays

undefined

u - the value can be undefined

MULTIPLE FLAGS

There are a few of thing to pay attention when many flags are specified.

am

This is the tipical m use.

au

A reference to an ARRAY is returned, empty if the value is undefined.

BACKSLASH ESCAPEING

The get method before performing the syntax constraint check parses the value in order to escape the backslashes. The following backslasch sequences are recognised.

\\ backslash
\$ dollar
\a beel
\b backspace
\f form feed
\n new line
\r carriage return
\t horizontal tabulator
\v vertical tabulator

A backslash at the end of the line makes the following line to be concatenated with the current one, this is a Config::General feature. In the following example $value contains the value 'valuecontinued'.

# config file
variable value\
continued

#code
my $value = $cfg->variable;

INLINE VARIABLE SUBSTITUTION

If a value contains the following syntax

${variable_name}

this token is substituted with the value of variable_name. The inline variable substitution is made at get time, so the value substituted is the final one of the variable. The value of variable_name is obtained by a get() call, so the syntax constraiant check is performed on it before the substitution.

To do the inline variable substituition is necessary that a reference to the root node is still alive, otherwise a die() is called. Anyway, it is possible to call the check() method on the node before loosing the root node reference in order to cache all the values. It can be called explicitally on a node of implicitally by the new() methed using the check parameter with a true value.

# config.conf file
<node>
 key ${var}
</node>
var value

In the following exaple a die() is called

my $node = get_node;
$node->_key; # this generates a die call
#
sub get_node {
  my $cfg = Config::General::Hierarchical->new( file => 'config.conf' );
  return $cfg->_node;
}

This can be prevented by this way

my $node = get_node;
$node->_key;
#
sub get_node {
  my $cfg = Config::General::Hierarchical->new( file => 'config.conf', check => 1 );
  return $cfg->_node;
}

or by this way (more efficient than previous).

my $node = get_node;
$node->_key;
#
sub get_node {
  my $cfg  = Config::General::Hierarchical->new( file => 'config.conf' );
  return $cfg->_node->check;
}

When an undef variable is requested during inline variable substitution, its value is substituted with an empty string.

The syntax to access the value of a subkey while in inline variable substitution is -> ; in the following example $cfg->_var will return 'abc' .

# configuration
<node>
 key b
<node>
var a${node->key}c

DUMPING CONFIGURATION

The module Config::General::Hierachical::Dump offers a simple and usefull way to dump configurration files.

EXAMPLE

Using many of the features of Config::General::Hierarchical it is possible to do so.

$ cat MyConfig.pm
package MyConfig;
use base 'Config::General::Hierarchical';
sub syntax {
    my ( $self ) = @_;
    my %constraint = (
        GMTOffsett => 'I',
        IdString   => 'm',
    );
    return $self->merge_values( \%constraint, $self->SUPER::syntax );
}
1;

$ cat MyConfigDump.pm
package MyConfigDump;
use base 'Config::General::Hierarchical::Dump';
use MyConfig;
sub parser { return 'MyConfig' };
1;

$ cat base.conf
#!/usr/bin/perl -MMyConfigDump
GMTOffsett N/A
IdString MyApp
LogString MyFacility-${IdString}

$ cat eu.conf
#!/usr/bin/perl -MMyConfigDump
inherits base.conf
GMTOffsett -1
IdString Eu
Rate UER

$ cat fr.conf
#!/usr/bin/perl -MMyConfigDump
inherits eu.conf
IdString Fr

$ cat gb.conf
#!/usr/bin/perl -MMyConfigDump
inherits eu.conf
GMTOffsett 0
IdString GB
Rate GBP

$ cat it.conf
#!/usr/bin/perl -MMyConfigDump
inherits eu.conf
IdString It

$ cat pt.conf
#!/usr/bin/perl -MMyConfigDump
inherits eu.conf
GMTOffsett 0
IdString Pt

$ cat us.conf
#!/usr/bin/perl -MMyConfigDump
inherits base.conf
IdString US
Rate USD

$ ./base.conf
GMTOffsett = error;
IdString = 'MyApp';
LogString = 'MyFacility-MyApp';

$ ./eu.conf
GMTOffsett = '-1';
IdString = 'MyAppEu';
LogString = 'MyFacility-MyAppEu';
Rate = 'UER';

$ ./fr.conf
GMTOffsett = '-1';
IdString = 'MyAppEuFr';
LogString = 'MyFacility-MyAppEuFr';
Rate = 'UER';

$ ./gb.conf
GMTOffsett = '0';
IdString = 'MyAppEuGB';
LogString = 'MyFacility-MyAppEuGB';
Rate = 'GBP';

$ ./it.conf
GMTOffsett = '-1';
IdString = 'MyAppEuIt';
LogString = 'MyFacility-MyAppEuIt';
Rate = 'UER';

$ ./pt.conf
GMTOffsett = '0';
IdString = 'MyAppEuPt';
LogString = 'MyFacility-MyAppEuPt';
Rate = 'UER';

$ ./us.conf
GMTOffsett = error;
IdString = 'MyAppUS';
LogString = 'MyFacility-MyAppUS';
Rate = 'USD';

BUGS AND INCOMPATIBILITIES

Some perl versions has a bug which give a message like following one:

Attempt to free unreferenced scalar: SV 0xe7411a0, Perl interpreter: 0xe160010 at t/99_dump.t line 2 during global destruction.

If it is possible to upgrade perl version, this is the best solution, otherwise an installation workaround can be used:

export EXCLUDE_WEAKEN=1
cpan Config::General::Hierarchical

Please report here https://rt.cpan.org/Dist/Display.html?Name=Config-General-Hierarchical any other one.

SEE ALSO

I strongly recommend you to read the following documentations:

Config::General         The way this module reads configuration files

LICENSE AND COPYRIGHT

Copyright (c) 2007-2009 Daniele Ricci

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

AUTHOR

Daniele Ricci <icc |AT| cpan.org>

CREDITS

A special thanks to Dada S.p.A. (Italy) for giving authorization to publish this module.

VERSION

0.07