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

Reply via email to