Re: POD-Tested

[email protected] (Nadim Khemir)
Newsgroups perl.pod-people
Message-ID <[email protected]>
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 :)
Cookbook.pod (text/plain, 14.8 KB)
# 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 ;
	
	@values{@variables} = $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};
				push@keys, $index++ ;
				}
			else
				{
				my ($aliased_history_name) = grep {$_ ne 'TIME'} keys %$entry ;
				
				push @replacement, $entry->{$aliased_history_name} ;
				push@keys, [$index, "$index = $aliased_history_name"] ;
				$index++ ;
				}
			}
		
		return('ARRAY', \@replacement, @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
Cookbook.pod.tested.pod (text/plain, 11.3 KB)
=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 ;
	
	@values{@variables} = $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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.