I can find no Perl module that implements config inheritance.
But it isn't that complicated to write a simple filter that works out the inheritance. Given a HoH, we can resolve the inheritance like below, if we see each entry in the config as a delta:
use Algorithm::C3; # must be installed from CPAN
sub resolve_inheritance {
my $input = shift;
my %output;
# hash of arrays where the arrays contain all parents
my %child_parent_relations =
map { my ($c, @p) = split /\s*:\s*/; $c => [\@p, $input->{$_}] } keys %$input;
my $get_parents = sub { @{ $child_parent_relations{shift()}[0] } };
my $get_data = sub { $child_parent_relations{shift()}[1] };
# prepare stuff for C3 resolution
my $resolution_cache = {};
my $resolve = sub {
my $child = shift;
Algorithm::C3::merge($child, $get_parents, $resolution_cache);
};
# now we go through all childs, and build temporary hashes from the C3 linearization.
for my $child (keys %child_parent_relations) {
my @linearization = $resolve->($child);
my %temp;
for my $delta_name (reverse @linearization) {
my $delta = $get_data->($delta_name);
@temp{keys %$delta} = values %$delta;
}
# save the data in the output:
$output{$child} = \%temp;
}
return \%output;
}
Test:
my $input = {
's1' => { A => 1 },
's2:s1' => { A => 2, B => 2 },
's3:s1' => { B => 3, C => 3 },
's4:s3:s2' => { }
};
my $expected = {
's1' => { A => 1 },
's2' => { A => 2, B => 2 },
's3' => { A => 1, B => 3, C => 3 },
's4' => { A => 2, B => 3, C => 3 },
};
use Test::More tests => 1;
is_deeply resolve_inheritance($input), $expected, 'C3 resolution';
As you can see, this gives the :
-operator right associativity (combines right-to-left).
If you want depth-first resolution, i.e.:
my $expected = {
's1' => { A => 1 },
's2' => { A => 2, B => 2 },
's3' => { A => 1, B => 3, C => 3 },
's4' => { A => 1, B => 3, C => 3 }, # s3 completely overwrites s2
};
then you get a different result. This can be done by first inheriting each parent, and then combining only the immediate parents, but not the whole hierarchy. In cases of single inheritance, the result of depth-first linearization and C3 resolution is equivalent.
To support depth-first resolution, we swap out the $resolve
function in above code, and change it to
my $resolve; $resolve = sub {
my $child = shift;
return $child, map { $resolve->($_) } $get_parents->($child);
};
This is the minimal change, but of course this could be made more efficient by keeping only the leftmost occurrence of each parent:
use List::MoreUtils 'uniq';
my $resolve; $resolve = sub {
my $child = shift;
return uniq $child, map { $resolve->($_) } $get_parents->($child);
};
If the data structure used for input could remember the order of sections, then this depth-first solution would get even easier. However, hashes are unordered.