Hi,
> On Friday 25 May 2007 15:19, Adrian Howard wrote:
> ...
> You might want to mention and compare to Test::Inline and
> Test::Pod::Snippets :-)
Test::Inline was already metioned in the "See Also" section. I've added
Test::Pod::Snippets for the next release.
IMHO, POD::Tested is more fun to use than those excellent modules because you
get something more than tests out of it. I've attached a real world example.
Sometime in the near future POD::Tested will also capture the output of the
program and insert it in the POD if directed to.
I'll also release Test::Cookbook that let you write:
<begin file t/some_test.t>
use Test::Cookbook ;
=Head 1
same thing as a POD::Tested here
=cut
<end file t/some_test.t>
The module name could be better but I haven't found any, suggestions welcome.
Now we have real test run by "make test". What I want is to also generate some
man pages from the test runs.
Yet another usage would be to use the module as a "collaborative" module
design tool (I use it to collaborate with myself :) I have
run "documentation first" projects and requirement collection quite a few
time. What I missed most was a practical way to make the requirements/design
executable.
Cheers, Nadim
PS: It's nice to see there is still (faint) life in this mailling list :)
# this should be processed by POD::Tested first
=head1 Config::Hierarchical cookbook
=head2 Simple usage
=head3 Creating a Config::Hierarchical configuration container
=begin common
use Config::Hierarchical ;
# give the config a name to get user friendly errors
my $config = new Config::Hierarchical(NAME => 'Simple usage example
config') ;
=end common
=begin test
# Config::Hierarchical allows us to get its generated warning and errors
my ($warnings, $die) ;
$config->{INTERACTION}{WARN} = sub{$warnings = join('', @_) ; use Carp
;carp $warnings; } ;
$config->{INTERACTION}{DIE} = sub{$die= join('', @_) ; use Carp ;croak
$die} ;
=end test
This will create a container with default values. You can modify the container
behavior by passing options
to the constructor. See the L<Config::Hierarchical> manual for all
initialization options.
=head3 Setting and getting configuration variables
=begin common
$config->Set(NAME => 'CC', VALUE => 'gcc') ;
$config->Set(NAME => 'LD', VALUE => 'ld') ;
print "Value for 'CC' is '" . $config->Get(NAME => 'CC') . "'.\n" ;
print "'LD' exists.\n" if $config->Exists(NAME => 'LD') ;
=end common
Would display:
=begin test
my $cc_value = $config->Get(NAME => 'CC') ;
is($cc_value, 'gcc', 'Get returns right value') ;
generate_pod("\tValue for 'CC' is '$cc_value'.\n") ; # note the \t in
the string so the pod is presented properly
generate_pod("\t'LD' exists.\n") if $config->Exists(NAME => 'LD') ;
generate_pod("\n") ;
=end test
=head3 Set the same variable multiple time with different values
=begin common
$config->Set(NAME => 'CC', VALUE => 'gcc') ;
$config->Set(NAME => 'CC', VALUE => 'cl') ;
print "Value for 'CC' is '" . $config->Get(NAME => 'CC') . "'.\n\n" ;
=end common
Would display:
=begin test
$cc_value = $config->Get(NAME => 'CC') ;
is($cc_value, 'cl', 'Get returns right value') ;
generate_pod("\tValue for 'CC' is '$cc_value'.\n\n") ;
=end test
B<Config::Hierarchical> does not generate any warning when you override a
variable's value. If you would like to get an error,
lock the variable.
=head4 Locking variables
$config->Set(NAME => 'CC', VALUE => 'gcc') ;
$config->Lock(NAME => 'CC') ;
$config->Set(NAME => 'CC', VALUE => 'cl') ;
This would generate the following error followed by a stack trace.
=begin test
# the code above generates an error and dies so we can't run it directly in a
common section
dies_ok
{
$config->Set(NAME => 'CC', VALUE => 'gcc') ;
$config->Lock(NAME => 'CC') ;
$config->Set(NAME => 'CC', VALUE => 'cl') ;
} 'Setting locked variable' ;
$die =~ s/^/\t/gm ;
generate_pod($die . "\n") ;
$cc_value = $config->Get(NAME => 'CC') ;
is($cc_value, 'gcc', 'Get returns right value') ;
=end test
=head4 Setting Locked variables
=begin common
$config->Set(FORCE_LOCK => 1, NAME => 'CC', VALUE => 'cl') ;
print "Value for 'CC' is '" . $config->Get(NAME => 'CC') . "'.\n\n" ;
=end common
Would display:
=begin test
$cc_value = $config->Get(NAME => 'CC') ;
is($cc_value, 'cl', 'Get returns right value') ;
generate_pod("\t$warnings\n") ;
generate_pod("\tValue for 'CC' is '$cc_value'.\n\n") ;
=end test
=head4 Getting the lock state
print "'CC' is locked.\n" if $config->IsLocked(NAME => 'CC') ;
print "'LD' is locked.\n" if $config->IsLocked(NAME => 'LD') ;
Would display:
=begin test
is($config->IsLocked(NAME => 'LD'), 0, 'config not locked') ;
is($config->IsLocked(NAME => 'CC'), 1, 'config locked') ;
generate_pod("\t'CC' is locked.\n") if $config->IsLocked(NAME => 'CC') ;
generate_pod("\t'LD' is locked.\n") if $config->IsLocked(NAME => 'LD') ;
generate_pod("\n") ;
=end test
=for POD::Tested reset
=head2 Setting variable in the constructor
=begin common
use Config::Hierarchical ;
my $config = new Config::Hierarchical
(
NAME => 'Config initialized in constructor',
INITIAL_VALUES =>
[
{NAME => 'CC', VALUE => 1},
{NAME => 'CC', VALUE => 2},
{NAME => 'LD', VALUE => 3, LOCK => 1},
{NAME => 'AS', VALUE => 4, LOCK => 1},
],
) ;
=end common
=begin test
my ($warnings, $die) ;
$config->{INTERACTION}{WARN} = sub{$warnings = join('', @_) ; use Carp
;carp $warnings; } ;
$config->{INTERACTION}{DIE} = sub{$die= join('', @_) ; use Carp ;croak
$die} ;
is($config->IsLocked(NAME => 'CC'), 0, 'config not locked') ;
is($config->IsLocked(NAME => 'LD'), 1, 'config locked') ;
is($config->Get(NAME => 'CC'), '2', 'initialized ok') or diag
$config->GetDump();
is($config->Get(NAME => 'LD'), '3', 'initialized ok') ;
is($config->Get(CATEGORY => 'CURRENT', NAME => 'AS'), 4, 'initialized
ok') ;
is($config->Exists(NAME => 'AS'), 1, 'exist') ;
=end test
=head2 Getting a non existing variable value
=begin common
my $value = $config->Get(NAME => 'NON_EXISTING') ;
print "Value for 'NON_EXISTING' is defined\n" if defined $value ;
=end common
Would display:
=begin test
generate_pod("\t$warnings\n") ;
generate_pod("Value for 'NON_EXISTING' is defined\n") if defined $value
;
=end test
=head2 Getting multiple variable values
=begin common
my @variables = qw(CC LD AS) ;
my %values ;
@[EMAIL PROTECTED] = $config->GetMultiple(@variables) ;
use Data::TreeDumper ;
my $dump = DumpTree \%values, 'Variables', INDENTATION => "\t",
DISPLAY_ADDRESS => 0 ;
print $dump ;
=end common
Would display:
=begin test
is_deeply(\%values, {CC => 2, LD => 3, AS => 4}, 'GetMultiple OK') ;
generate_pod("$dump\n") ;
generate_pod("\n") ;
=end test
=head2 history and comments
B<Config::Hierarchical> will keep an history for each variable in you config.
=begin common
print $config->GetHistoryDump(NAME => 'CC') ;
=end common
=begin test
generate_pod($config->GetHistoryDump(NAME => 'CC',
DATA_TREEDUMPER_OPTIONS => [INDENTATION => "\t"])) ;
generate_pod("\n") ;
=end test
The reference manual describes a L<Data::TreeDumper> filter that you can use to
generate a history in the
following format:
=begin test
sub Compact
{
my ($s, $level, $path, $keys, $setup, $arg) = @_ ;
if('ARRAY' eq ref $s)
{
my ($index, @replacement, @keys) = (0) ;
for my $entry( @$s)
{
if(exists $entry->{EVENT})
{
push @replacement, $entry->{EVENT} ; #. 'time:
' . $entry->{TIME};
[EMAIL PROTECTED], $index++ ;
}
else
{
my ($aliased_history_name) = grep {$_ ne
'TIME'} keys %$entry ;
push @replacement,
$entry->{$aliased_history_name} ;
[EMAIL PROTECTED], [$index, "$index =
$aliased_history_name"] ;
$index++ ;
}
}
return('ARRAY', [EMAIL PROTECTED], @keys) ;
}
}
my $compact_dump = DumpTree $config->GetHistory( NAME => 'CC'), 'CC',
DISPLAY_ADDRESS => 0, FILTER => \&Compact, INDENTATION => "\t" ;
generate_pod($compact_dump) ;
generate_pod("\n") ;
=end test
You can also add a comment to the history when you manipulate variables.
=begin common
$config->Set(NAME => 'LD', VALUE => 'new LD value', FORCE_LOCK => 1,
COMMENT => 'why we forced the lock') ;
=end common
Would give this history:
=begin test
generate_pod($config->GetHistoryDump(NAME => 'LD',
DATA_TREEDUMPER_OPTIONS => [INDENTATION => "\t"])) ;
generate_pod("\n") ;
=end test
See I<GetHistory> in the manual if you want to handle the history data directly.
=head2 Validators
You can assign validators to variables. If a validator return B<false>,
B<Config::Hierarchical> will generate and error.
Validators can be defined in the B<Config::Hierarchical> constructor or can be
local in a I<Set> call.
=begin common
sub PositiveValueValidator
{
my ($value) = @_;
return($value >= 0)
} ;
=end common
$config->Set
(
NAME => 'CC',
VALUE => -1,
VALIDATORS => {positive_value => \&PositiveValueValidator,},
) ;
Will generate the following error:
=begin test
throws_ok
{
$config->Set
(
NAME => 'CC',
VALUE => -1,
VALIDATORS => {positive_value =>
\&PositiveValueValidator,},
) ;
} qr/Invalid value for variable 'CC'. Local validator
'positive_value' defined at .*/, "local validator" ;
$die =~ s/^/\t/gm ;
generate_pod($die . "\n") ;
=end test
=head2 GetKeys
You can get a list of all the variable names.
=begin common
my @variable_names = $config->GetKeys() ;
print 'The config contains the following variables: ' . join(', ',
@variable_names) . ".\n" ;
=end common
Would display:
=begin test
generate_pod("\tThe config contains the following variables: " .
join(', ', @variable_names) . ".\n\n") ;
=end test
=head2 Key and value tuples
You can also get a list containing a tuple for each of the config variable. The
Tuple is a hash reference. This lets you
write code like :
map
{
my $name = $_->{NAME} ;
my $value = $_->{VALUE} ;
# your code here
} $config->GetKeyValueTuples() ;
=head2 Categories
=for POD::Tested reset
my $config = new Config::Hierarchical
(
NAME => 'config with categories',
CATEGORY_NAMES => ['A', 'B'],
DEFAULT_CATEGORY => 'B',
INITIAL_VALUES =>
[
{CATEGORY => 'A', NAME => 'CC', VALUE
=> 'A_CC'},
{CATEGORY => 'B', NAME => 'CC', VALUE
=> 'B_CC'},
{CATEGORY => 'A', NAME => 'LD', VALUE
=> 'A_LD'},
{CATEGORY => 'B', NAME => 'LD', VALUE
=> 'B_LD'},
{CATEGORY => 'A', NAME => 'AS', VALUE
=> 'A_AS'},
] ,
) ;
=begin test
# initialisation above generates warnings we need to catch
my ($warnings, $die) ;
my $config = new Config::Hierarchical
(
NAME => 'config with categories',
CATEGORY_NAMES => ['A', 'B'],
DEFAULT_CATEGORY => 'B',
INITIAL_VALUES =>
[
{CATEGORY => 'A', NAME => 'CC', VALUE
=> 'A_CC'},
{CATEGORY => 'B', NAME => 'CC', VALUE
=> 'B_CC'},
{CATEGORY => 'A', NAME => 'LD', VALUE
=> 'A_LD'},
{CATEGORY => 'B', NAME => 'LD', VALUE
=> 'B_LD'},
{CATEGORY => 'A', NAME => 'AS', VALUE
=> 'A_AS'},
] ,
INTERACTION =>
{
WARN => sub{$warnings .= "\t" .
join('', @_) . "\n"; use Carp ;carp join('', @_); },
DIE => sub{$die .= join('', @_) ; use
Carp ;croak join('', @_)},
} ,
) ;
=end test
Would generate the following warnings:
=begin test
generate_pod("$warnings\n") ;
$warnings = '' ;
=end test
And the config would be:
=begin test
my $hash = $config->GetHashRef() ;
generate_pod(DumpTree($hash, 'Config contains:', DISPLAY_ADDRESS => 0 ,
INDENTATION => "\t")) ;
generate_pod("\n") ;
=end test
B<Config::Hierarchical> will display a warning anytime you set a variable and
that a higher level configuration takes precedence.
=head3 Lower categories warning
By default, no warning are displayed when a lower category value will be
ignored. You can make B<Config::Hierarchical> check
lower categories this way:
=begin common
$config->Set(CATEGORY => 'A', NAME => 'CC', VALUE => 'A_CC_2',
CHECK_LOWER_LEVEL_CATEGORIES => 1) ;
=end common
Would generate the following warnings:
=begin test
generate_pod("$warnings\n") ;
$warnings = '' ;
=end test
The config is now:
=begin test
$hash = $config->GetHashRef() ;
generate_pod(DumpTree($hash, 'Config contains:', DISPLAY_ADDRESS => 0 ,
INDENTATION => "\t")) ;
generate_pod("\n") ;
=end test
=head3 Overriding a higher level category
Is done this way:
=begin common
$config->Set(CATEGORY => 'B', NAME => 'CC', VALUE => 'B_CC_2', OVERRIDE => 1)
;
=end common
And would generate the following warnings:
=begin test
generate_pod("$warnings\n") ;
$warnings = '' ;
=end test
The config is now:
=begin test
$hash = $config->GetHashRef() ;
generate_pod(DumpTree($hash, 'Config contains:', DISPLAY_ADDRESS => 0 ,
INDENTATION => "\t")) ;
generate_pod("\n") ;
=end test
=head3 History from multiple categories
=begin common
print $config->GetHistoryDump(NAME => 'CC') ;
=end common
=begin test
generate_pod($config->GetHistoryDump(NAME => 'CC',
DATA_TREEDUMPER_OPTIONS => [INDENTATION => "\t"])) ;
generate_pod("\n") ;
=end test
=head2 Tie::Readonly
You can tie your configuration to a read only hash. this lets you manipulate
your config like a normal hash. Interpolation
in strings is also much easier.
=begin common
my %hash ;
tie %hash, 'Config::Hierarchical::Tie::ReadOnly' => $config ;
my $keys = join(', ', sort keys %hash) ;
print "The config variables are: $keys\n" ;
print "CC's value is '$hash{CC}'\n" ;
=end common
Would display:
=begin test
generate_pod("\tThe config variables are: $keys\n") ;
generate_pod("\tCC's value is '$hash{CC}'\n") ;
generate_pod("\n") ;
=end test
Remember that the hash is read only. Trying to modify a variable is not allowed:
$hash{CC} = 2 ;
Would generate this error:
=begin test
dies_ok
{
$hash{CC} = 2 ;
} 'setting a read only variable' ;
$die =~ s/^/\t/gm ;
generate_pod($die . "\n") ;
=end test
=head2 Copying data from another config
Use the code below to initialized a category from data copied from another
category:
=begin common
my $config_2 = new Config::Hierarchical
(
NAME => 'config initialized from
another config',
CATEGORY_NAMES => ['PARENT',
'CURRENT'],
DEFAULT_CATEGORY => 'CURRENT',
INITIAL_VALUES =>
[
# Initializing a category from
another config
map
({
{
NAME =>
$_->{NAME},
VALUE =>
$_->{VALUE},
CATEGORY =>
'PARENT',
LOCK => 1,
HISTORY =>
$config->GetHistory(NAME => $_->{NAME}),
}
}
$config->GetKeyValueTuples()),
{NAME =>
'VALUE_IN_CURRENT_CATEGORY', VALUE => 1,},
],
) ;
=end common
And the config would be:
=begin test
$hash = $config_2->GetHashRef() ;
generate_pod(DumpTree($hash, 'Config 2 contains:', DISPLAY_ADDRESS => 0
, INDENTATION => "\t")) ;
generate_pod("\n") ;
=end test
=head2 Aliasing other configurations
=begin common
my $config_3 = new Config::Hierarchical
(
NAME => 'config with aliases',
CATEGORY_NAMES => ['PARENT',
'CURRENT'],
DEFAULT_CATEGORY => 'CURRENT',
INITIAL_VALUES =>
[
{
CATEGORY => 'PARENT',
ALIAS => $config,
},
# more initialization if
necessary
],
) ;
=end common
=begin test
my ($warnings_3, $die_3) ;
$config_3->{INTERACTION}{WARN} = sub{$warnings_3 = join('', @_) ; use
Carp ;carp $warnings_3; } ;
$config_3->{INTERACTION}{DIE} = sub{$die_3 = join('', @_) ; use Carp
;croak $die_3} ;
=end test
=begin common
$config_3->Set(NAME => 'LD', VALUE => 'new LD') ;
=end common
Would generate this warning:
=begin test
generate_pod("\t$warnings_3\n") ;
=end test
And the config would be:
=begin test
$hash = $config_3->GetHashRef() ;
generate_pod(DumpTree($hash, 'Config 3 contains:', DISPLAY_ADDRESS => 0
, INDENTATION => "\t")) ;
generate_pod("\n") ;
=end test
=head3 History from aliased configuration
B<Config::Hierarchical> will, display aliased categories history.
=begin common
print $config_3->GetHistoryDump(NAME => 'LD') ;
=end common
=begin test
generate_pod($config_3->GetHistoryDump(NAME => 'LD',
DATA_TREEDUMPER_OPTIONS => [INDENTATION => "\t"])) ;
generate_pod("\n") ;
=end test
=cut
=head1 Config::Hierarchical cookbook
=head2 Simple usage
=head3 Creating a Config::Hierarchical configuration container
use Config::Hierarchical ;
# give the config a name to get user friendly errors
my $config = new Config::Hierarchical(NAME => 'Simple usage example
config') ;
This will create a container with default values. You can modify the container
behavior by passing options
to the constructor. See the L<Config::Hierarchical> manual for all
initialization options.
=head3 Setting and getting configuration variables
$config->Set(NAME => 'CC', VALUE => 'gcc') ;
$config->Set(NAME => 'LD', VALUE => 'ld') ;
print "Value for 'CC' is '" . $config->Get(NAME => 'CC') . "'.\n" ;
print "'LD' exists.\n" if $config->Exists(NAME => 'LD') ;
Would display:
Value for 'CC' is 'gcc'.
'LD' exists.
=head3 Set the same variable multiple time with different values
$config->Set(NAME => 'CC', VALUE => 'gcc') ;
$config->Set(NAME => 'CC', VALUE => 'cl') ;
print "Value for 'CC' is '" . $config->Get(NAME => 'CC') . "'.\n\n" ;
Would display:
Value for 'CC' is 'cl'.
B<Config::Hierarchical> does not generate any warning when you override a
variable's value. If you would like to get an error,
lock the variable.
=head4 Locking variables
$config->Set(NAME => 'CC', VALUE => 'gcc') ;
$config->Lock(NAME => 'CC') ;
$config->Set(NAME => 'CC', VALUE => 'cl') ;
This would generate the following error followed by a stack trace.
Simple usage example config: Variable 'CURRENT::CC' was locked and
couldn't be set at ''Cookbook.pod':97'.
=head4 Setting Locked variables
$config->Set(FORCE_LOCK => 1, NAME => 'CC', VALUE => 'cl') ;
print "Value for 'CC' is '" . $config->Get(NAME => 'CC') . "'.\n\n" ;
Would display:
Simple usage example config: Forcing locked variable 'CURRENT::CC' at
''Cookbook.pod':112'.
Value for 'CC' is 'cl'.
=head4 Getting the lock state
print "'CC' is locked.\n" if $config->IsLocked(NAME => 'CC') ;
print "'LD' is locked.\n" if $config->IsLocked(NAME => 'LD') ;
Would display:
'CC' is locked.
=head2 Setting variable in the constructor
use Config::Hierarchical ;
my $config = new Config::Hierarchical
(
NAME => 'Config initialized in constructor',
INITIAL_VALUES =>
[
{NAME => 'CC', VALUE => 1},
{NAME => 'CC', VALUE => 2},
{NAME => 'LD', VALUE => 3, LOCK => 1},
{NAME => 'AS', VALUE => 4, LOCK => 1},
],
) ;
=head2 Getting a non existing variable value
my $value = $config->Get(NAME => 'NON_EXISTING') ;
print "Value for 'NON_EXISTING' is defined\n" if defined $value ;
Would display:
Config initialized in constructor: Variable 'NON_EXISTING' doesn't
exist in categories [CURRENT]at ''Cookbook.pod':189'. Returning undef!
=head2 Getting multiple variable values
my @variables = qw(CC LD AS) ;
my %values ;
@[EMAIL PROTECTED] = $config->GetMultiple(@variables) ;
use Data::TreeDumper ;
my $dump = DumpTree \%values, 'Variables', INDENTATION => "\t",
DISPLAY_ADDRESS => 0 ;
print $dump ;
Would display:
Variables
|- AS = 4
|- CC = 2
`- LD = 3
=head2 history and comments
B<Config::Hierarchical> will keep an history for each variable in you config.
print $config->GetHistoryDump(NAME => 'CC') ;
History for variable 'CC' from config 'Config initialized in
constructor' created at ''Cookbook.pod':154':
|- 0
| |- EVENT = CREATE AND SET. value = '1', category = 'CURRENT' at
''Cookbook.pod':154', status = OK.
| `- TIME = 0
`- 1
|- EVENT = SET. value = '2', category = 'CURRENT' at
''Cookbook.pod':154', status = OK.
`- TIME = 1
The reference manual describes a L<Data::TreeDumper> filter that you can use to
generate a history in the
following format:
CC
|- 0 = CREATE AND SET. value = '1', category = 'CURRENT' at
''Cookbook.pod':154', status = OK.
`- 1 = SET. value = '2', category = 'CURRENT' at ''Cookbook.pod':154',
status = OK.
You can also add a comment to the history when you manipulate variables.
$config->Set(NAME => 'LD', VALUE => 'new LD value', FORCE_LOCK => 1,
COMMENT => 'why we forced the lock') ;
Would give this history:
History for variable 'LD' from config 'Config initialized in
constructor' created at ''Cookbook.pod':154':
|- 0
| |- EVENT = CREATE AND SET. value = '3', LOCK(1), category =
'CURRENT' at ''Cookbook.pod':154', status = OK.
| `- TIME = 2
`- 1
|- COMMENT = why we forced the lock
|- EVENT = SET. value = 'new LD value', FORCE_LOCK, category =
'CURRENT' at ''Cookbook.pod':292', status = OK, forced lock.
`- TIME = 4
See I<GetHistory> in the manual if you want to handle the history data directly.
=head2 Validators
You can assign validators to variables. If a validator return B<false>,
B<Config::Hierarchical> will generate and error.
Validators can be defined in the B<Config::Hierarchical> constructor or can be
local in a I<Set> call.
sub PositiveValueValidator
{
my ($value) = @_;
return($value >= 0)
} ;
$config->Set
(
NAME => 'CC',
VALUE => -1,
VALIDATORS => {positive_value => \&PositiveValueValidator,},
) ;
Will generate the following error:
Config initialized in constructor: Invalid value for variable 'CC'.
Local validator 'positive_value' defined at ''Cookbook.pod':336'.
=head2 GetKeys
You can get a list of all the variable names.
my @variable_names = $config->GetKeys() ;
print 'The config contains the following variables: ' . join(', ',
@variable_names) . ".\n" ;
Would display:
The config contains the following variables: CC, AS, LD.
=head2 Key and value tuples
You can also get a list containing a tuple for each of the config variable. The
Tuple is a hash reference. This lets you
write code like :
map
{
my $name = $_->{NAME} ;
my $value = $_->{VALUE} ;
# your code here
} $config->GetKeyValueTuples() ;
=head2 Categories
my $config = new Config::Hierarchical
(
NAME => 'config with categories',
CATEGORY_NAMES => ['A', 'B'],
DEFAULT_CATEGORY => 'B',
INITIAL_VALUES =>
[
{CATEGORY => 'A', NAME => 'CC', VALUE
=> 'A_CC'},
{CATEGORY => 'B', NAME => 'CC', VALUE
=> 'B_CC'},
{CATEGORY => 'A', NAME => 'LD', VALUE
=> 'A_LD'},
{CATEGORY => 'B', NAME => 'LD', VALUE
=> 'B_LD'},
{CATEGORY => 'A', NAME => 'AS', VALUE
=> 'A_AS'},
] ,
) ;
Would generate the following warnings:
config with categories: Setting 'B::CC' at ''Cookbook.pod':431':
'A::CC' takes precedence .
config with categories: Setting 'B::LD' at ''Cookbook.pod':431':
'A::LD' takes precedence .
And the config would be:
Config contains:
|- AS = A_AS
|- CC = A_CC
`- LD = A_LD
B<Config::Hierarchical> will display a warning anytime you set a variable and
that a higher level configuration takes precedence.
=head3 Lower categories warning
By default, no warning are displayed when a lower category value will be
ignored. You can make B<Config::Hierarchical> check
lower categories this way:
$config->Set(CATEGORY => 'A', NAME => 'CC', VALUE => 'A_CC_2',
CHECK_LOWER_LEVEL_CATEGORIES => 1) ;
Would generate the following warnings:
config with categories: Setting 'A::CC' at ''Cookbook.pod':464':
Takes Precedence over lower category 'B::CC'
The config is now:
Config contains:
|- AS = A_AS
|- CC = A_CC_2
`- LD = A_LD
=head3 Overriding a higher level category
Is done this way:
$config->Set(CATEGORY => 'B', NAME => 'CC', VALUE => 'B_CC_2', OVERRIDE => 1)
;
And would generate the following warnings:
config with categories: Setting 'B::CC' at ''Cookbook.pod':494':
Overriding 'A::CC'
The config is now:
Config contains:
|- AS = A_AS
|- CC = B_CC_2
`- LD = A_LD
=head3 History from multiple categories
print $config->GetHistoryDump(NAME => 'CC') ;
History for variable 'CC' from config 'config with categories' created
at ''Cookbook.pod':431':
|- 0
| |- EVENT = CREATE AND SET. value = 'A_CC', category = 'A' at
''Cookbook.pod':431', status = OK.
| `- TIME = 0
|- 1
| |- EVENT = CREATE AND SET. value = 'B_CC', category = 'B' at
''Cookbook.pod':431', status = 'A::CC' takes precedence .OK.
| `- TIME = 1
|- 2
| |- EVENT = SET. value = 'A_CC_2', category = 'A' at
''Cookbook.pod':464', status = Takes Precedence over lower category 'B::CC'OK.
| `- TIME = 5
`- 3
|- EVENT = SET. value = 'B_CC_2', OVERRIDE, category = 'B' at
''Cookbook.pod':494', status = Overriding 'A::CC' (existed, value
| was different).OK.
`- TIME = 6
=head2 Tie::Readonly
You can tie your configuration to a read only hash. this lets you manipulate
your config like a normal hash. Interpolation
in strings is also much easier.
my %hash ;
tie %hash, 'Config::Hierarchical::Tie::ReadOnly' => $config ;
my $keys = join(', ', sort keys %hash) ;
print "The config variables are: $keys\n" ;
print "CC's value is '$hash{CC}'\n" ;
Would display:
The config variables are: AS, CC, LD
CC's value is 'B_CC_2'
Remember that the hash is read only. Trying to modify a variable is not allowed:
$hash{CC} = 2 ;
Would generate this error:
This hash is read only at ''Cookbook.pod':572'!
=head2 Copying data from another config
Use the code below to initialized a category from data copied from another
category:
my $config_2 = new Config::Hierarchical
(
NAME => 'config initialized from
another config',
CATEGORY_NAMES => ['PARENT',
'CURRENT'],
DEFAULT_CATEGORY => 'CURRENT',
INITIAL_VALUES =>
[
# Initializing a category from
another config
map
({
{
NAME =>
$_->{NAME},
VALUE =>
$_->{VALUE},
CATEGORY =>
'PARENT',
LOCK => 1,
HISTORY =>
$config->GetHistory(NAME => $_->{NAME}),
}
}
$config->GetKeyValueTuples()),
{NAME =>
'VALUE_IN_CURRENT_CATEGORY', VALUE => 1,},
],
) ;
And the config would be:
Config 2 contains:
|- AS = A_AS
|- CC = B_CC_2
|- LD = A_LD
`- VALUE_IN_CURRENT_CATEGORY = 1
=head2 Aliasing other configurations
my $config_3 = new Config::Hierarchical
(
NAME => 'config with aliases',
CATEGORY_NAMES => ['PARENT',
'CURRENT'],
DEFAULT_CATEGORY => 'CURRENT',
INITIAL_VALUES =>
[
{
CATEGORY => 'PARENT',
ALIAS => $config,
},
# more initialization if
necessary
],
) ;
$config_3->Set(NAME => 'LD', VALUE => 'new LD') ;
Would generate this warning:
config with aliases: Setting 'CURRENT::LD' at ''Cookbook.pod':659':
'PARENT::LD' takes precedence .
And the config would be:
Config 3 contains:
|- AS = A_AS
|- CC = B_CC_2
`- LD = A_LD
=head3 History from aliased configuration
B<Config::Hierarchical> will, display aliased categories history.
print $config_3->GetHistoryDump(NAME => 'LD') ;
History for variable 'LD' from config 'config with aliases' created at
''Cookbook.pod':629':
|- 0
| |- HISTORY FROM ALIASED CATEGORY 'config with categories'
| | |- 0
| | | |- EVENT = CREATE AND SET. value = 'A_LD', category = 'A' at
''Cookbook.pod':431', status = OK.
| | | `- TIME = 2
| | `- 1
| | |- EVENT = CREATE AND SET. value = 'B_LD', category = 'B' at
''Cookbook.pod':431', status = 'A::LD' takes precedence .OK.
| | `- TIME = 3
| `- TIME = 3
`- 1
|- EVENT = CREATE AND SET. value = 'new LD', category = 'CURRENT' at
''Cookbook.pod':659', status = 'PARENT::LD' takes
| precedence .OK.
`- TIME = 4
=cut