The original version may fail to merge keys if the merged hash is a
sibling of the merging hash and itself merges a sibling. Success or
failure then depends on the order in which hash values are returned by
keys()!
key1: &key1
a: a
b: b
c: c
key2: &key2
<<: *key1
d: d
e: e
f: f
key3:
<<: *key2
g: g
h: h
i: i
...
If key2 is pushed onto @_ before key3 all is well, but if not there will
still be a merge key pointing at key1 after key2 has been merged into key3.
Replacing the check for the existence of a mergekey with a while loop
fixes that, because then a new merge will be performed and the outer
while loop traversing @_ will not move on while there is still/again a
merge key.
sub mergekeys_loop {
my ( $orig ) = @_;
while ( my $ref = shift ) {
my $type = ref $ref;
if ( $type eq 'HASH' ) {
# my $tmphref = $ref->{'<<'};
# if ( $tmphref ) {
while ( my $tmphref = $ref->{'<<'} ) {
die "Merge key does not support merging non-hashmaps"
unless ( ref $tmphref eq 'HASH' );
my %tmphash = %$tmphref;
delete $ref->{'<<'};
%$ref = ( %tmphash, %$ref );
}
push @_, grep { ref eq 'HASH' or ref eq 'ARRAY' } values %
+$ref;
}
elsif ( $type eq 'ARRAY' ) {
push @_, grep { ref eq 'HASH' or ref eq 'ARRAY' } @$ref;
}
}
return $orig;
}