NAME
Config::apiLayers - Auto-prototyping object properties in multiple configuration layers.
SYNOPSIS
use Config::apiLayers;
my $cfg = new Config::apiLayers({
autoproto => 1,
attributes => [qw(length, width, area)]
});
# Set the default values
$cfg->config({
data => {
'length' => 6,
'width' => 10,
'area' => sub {
my $cfg = shift;
return ($cfg->apicall('length') * $cfg->apicall('width'))
}
}
});
# This prints with an area of 30
$cfg->length( 3 );
print ("This item is a ".$cfg->length." length by ".$cfg->width." width, with an area of ".$cfg->area.".");
# This prints with an area of 12
$cfg->width( 4 );
print ("This item is a ".$cfg->length." length by ".$cfg->width." width, with an area of ".$cfg->area.".");
DESCRIPTION
Used as a base module or on its own to manage configuration properties of an application. Its default behavior is to auto-prototype property attributes. Validators can be used to validate values to be set. Configuration can be used with Getopt::Long
and Getopt::LongUsage
for obtaining configuration.
Properties that are imported or directly configured can be stored in one or multiple layers, and do not immediately affect each other. When retrieved, the values of properties are obtained from the layers in a top-down fashion.
The values of properties can also be functions. When the value is retrieved, the function is executed which can be used to combine multiple property values together, or do smoething entirely different.
REQUIREMENTS
None. This module is Pure Perl. This module does not use AUTOLOAD.
METHODS
new
- attributes - configure the allowed attributes. Three styles are available.
- autoproto - 1 or 0 ; Default is 1; Set to 1 for the attributes to be functions of the object.
# Style 1 - Only set the attributes, and each attribute stores a value. Order is retained.
my $cfg = new Config::apiLayers({
autoproto => 1,
attributes => [qw(length, width, area)]
});
# Style 2 - Only set the attributes with a validator. Attributes with undef valdiator store a provided values without validation. Order is NOT retained.
my $val_greater_than_zero = sub {
my $cfg = shift;
my $attribute_name = shift;
my $value = shift;
return undef unless $value > 0;
return $value;
};
my $cfg = new Config::apiLayers({
autoproto => 1,
attributes => {
'length' => $val_greater_than_zero,
'width' => $val_greater_than_zero,
'area' => sub { return undef }, # do not allow storing any value
'store_any' => undef
}
});
# Style 3 - Same as Style 2, but retain the order of the attributes in configuration.
my $cfg = new Config::apiLayers({
autoproto => 1,
attributes => [
{'length' => $val_greater_than_zero},
{'width' => $val_greater_than_zero},
{'area' => sub { return undef }}, # do not allow storing any value
{'store_any' => undef}
]
});
# Style 3 gives the ability to provide additional attribute configuration.
# Configure optional information for use with C<Getopt::Long> and
# C<Getopt::LongUsage> modules, while retaining order of the attributes.
my $cfg = new Config::apiLayers({
autoproto => 1,
attributes => [
{ name => 'length',
validator => $val_greater_than_zero,
getoptlong => 'length|l:i',
description => "The length of a rectangle"
},
{ name => 'width',
validator => $val_greater_than_zero,
getoptlong => 'width|w:i',
description => "The width of a rectangle"
},
{ name => 'area',
validator => sub { return undef }, # do not allow storing any value
getoptlong => 'area|a',
description => "The area of the rectangle, length times width"
},
{ name => 'store_any',
validator => undef,
getoptlong => 'store_any_value:s',
description => "Store a value of your choosing, unvalidated"
}
]
});
# Example use of configuration
# Set the default values (the first layer is layer number 0)
$cfg->config({
data => {
'length' => 6,
'width' => 10,
'area' => sub {
my $cfg = shift;
return ($cfg->apicall('length') * $cfg->apicall('width'))
}
}
});
# Perform the computation
$cfg->length(5);
$cfg->width(8);
my $area = $cfg->area; # $area == 40
config
Add configuration data to either the given index
layer, or the highest layer if index
is not provided.
- index - the index number of the top most configuration data layer to add.
- data - the configuration data to add to the top most layer.
# Configure the 2nd layer (layer numbering starts at 0)
$cfg->config({
index => 1,
data => {
'length' => 6,
'width' => 10,
'area' => sub {
my $cfg = shift;
return ($cfg->apicall('length') * $cfg->apicall('width'))
}
}
});
importdata
Import the data, performing validation as available. This method is will import each attribute individually with apicall
, which performs the validation if a validator was configured (see new
for configuring a validator for an attribute).
$cfg->importdata({ data => { length => 4, width => 2 } });
exportdata
Export the data or specific configuration information.
- cfg - the type of configuration to export. Provide this is two formats.
-
- cfg => 'getoptlong' - return an array of GetoptLong config, as configured in
new
. This can be used with theGetopt::Long
module. Or you can parse it for a custom purpose usingGetopt::LongUsage::ParseGetoptLongConfig
method. - cfg => 'descriptions' - return a hash of attribute and descriptions, as configured in
new
. This can be used with theGetopt::LongUsage
module.
- cfg => 'getoptlong' - return an array of GetoptLong config, as configured in
- data - the configuration data to export. Provide this in three formats:
-
- data => 'undef' - (this is the default action) to export each attribute among all layers
- data => 'layerNumber' - to export attributes only from the given layer number
- data => ['startingLayer','endingLayer'] - to export attributes only from the given layer range
- data => /invalid or unrecognized value/ - preform the default action
my $getoptlong_config = $cfg->export({ cfg => 'getoptlong' });
my %options;
GetOptions( \%options, @$getoptlong_config );
$cfg->importdata({ data => \%options });
add_layer
Add a new configuration layer. If index
is provided, then configuration data layers are added until index
number of layers exist. If data
is provided, then the given configuration data is added to the highest configuration data layer. If both index
and data
are provided, the layers are added first, then the configuration data is added.
- index - the index number of the top most configuration data layer to add.
- data - the configuration data to add to the top most layer.
# Add one more layer to the existing layers.
$cfg->add_layer();
# Add one more layer and set the data for that layer.
$cfg->add_layer({
data => {
'length' => 6,
'width' => 10,
'area' => sub {
my $cfg = shift;
return ($cfg->apicall('length') * $cfg->apicall('width'))
}
}
});
# Add 3 layers (layer numbering starts at 0)
$cfg->add_layer({ 'index' => 2 });
apican
Determine if the apiname is available to be called. Returns undef if false, or the referenced function if true.
my $subref = $cfg->apican("apiname");
apicall
Call the apiname to get or set the attribute.
- apiname - the name of the property/api
- arguments - any arguments required by the apicall, i.e. like a value to set
# Both of these calls do the same thing
my $res = $cfg->apicall("apiname" [, arguments ]);
my $res = $cfg->apiname([ arguments ]);
VALIDATORS
VALIDATOR FORMAT
The format of the validator is to return the value to store if the value is validated successfully, or return undef is not validated successfully.
Validators must be a function, or a string that can be evaluated as a regex. Validators that are hashes or arrays are invalid.
An undefined validator will allow any value passed in to be stored.
VALIDATOR AS A FUNCTION
This allows a function that is used as the validator to change the value, as the stored value is whatever the function returns.
Validator Function Call Format:
# The Validator is called as follows:
$validator->( <Config::apiLayers Object>, <attribute_name>, <attribute_value> )
Validator Function Example:
# Accept any value that is not a reference
[{ "FirstName" => sub{ return $_[2] if !ref $_[2] } }]
VALIDATOR AS A STRING
If a string/scalar is provided as the validator, it is used as a regex to test the value. The value is considered to be validated successfully if the regex match test is true.
Validator Format: # The Validator is called similar to: if ($value =~ /$validator_string/) { <store the value> }
Validator Example:
# Accept any value that is matches a word
[{ "FirstName" => "\w" }]
[{ name => "FirstName", validator => "\w" }]
[{ name => "FirstName", validator => "\w", description => "The first name" }]
EXAMPLES
Use Config::apiLayers with Getopt::Long and Getopt::LongUsage
use Getopt::Long;
use Getopt::LongUsage;
use Config::apiLayers;
# Note the missing getoptlong configuration and description for area attribute
my $cfg = new Config::apiLayers({
autoproto => 1,
attributes => [
{ name => 'length',
validator => sub { return $_[2] > 0 ? $_[2] : undef },
getoptlong => 'length|l:i',
description => "The length of a rectangle"
},
{ name => 'width',
validator => sub { return $_[2] > 0 ? $_[2] : undef },
getoptlong => 'width|w:i',
description => "The width of a rectangle"
},
{ name => 'area',
validator => sub { return undef } # do not allow storing any value
}
]
});
# Set the default values
$cfg->config({
data => {
'length' => 6,
'width' => 10,
'area' => sub {
my $cfg = shift;
return ($cfg->apicall('length') * $cfg->apicall('width'))
}
}
});
my $getoptlong_config = $cfg->exportdata({ cfg => 'getoptlong' });
my $attr_descriptions = $cfg->exportdata({ cfg => 'descriptions' });
my %options;
my @getoptconf = ( \%options,
@{$getoptlong_config},
'verbose|v',
'help|h'
);
my $usage = sub {
my @getopt_long_configuration = @_;
GetLongUsage (
'cli_use' => ($0 ." [options]"),
'descriptions' =>
[ @{$attr_descriptions},
'verbose' => "verbose",
'help' => "this help message"
],
'Getopt_Long' => \@getopt_long_configuration,
);
};
GetOptions( @getoptconf ) || die ($usage->( @getoptconf ),"\n");
$cfg->add_layer();
$cfg->importdata({ data => \%options }) || die ($usage->( @getoptconf ),"\n");
print "Area is: ",$cfg->area,"\n";
Example outputs:
linux$ ./test_it.pl --not-an-option
Unknown option: not-an-option
./test_it.pl [options]
--length The length
--width The width
-v, --verbose verbose
-h, --help this help message
linux$ ./test_it.pl --width=101
Area is: 606
AUTHOR
Russell E Glaue, http://russ.glaue.org
COPYRIGHT AND LICENSE
Copyright (c) 2015 Russell E Glaue, All rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.