App-reposdb
view release on metacpan or search on metacpan
script/_reposdb-inline view on Meta::CPAN
# }
# $res;
#}
#
#sub add_prefix {
# my ($self, $hash_key, $mode) = @_;
# die "Hash key not a string" if ref($hash_key);
# my $dis = $self->config->disable_modes;
# if (defined($dis) && ref($dis) ne 'ARRAY') {
# die "Invalid config value `disable_modes`: must be an array";
# }
# if ($dis && $self->_in($mode, $dis)) {
# $self->push_error("Can't add prefix for currently disabled mode `$mode`");
# return $hash_key;
# }
# my $mh = $self->modes->{$mode} or die "Unknown mode: $mode";
# $mh->add_prefix($hash_key);
#}
#
#sub remove_prefix {
# my ($self, $hash_key) = @_;
# die "Hash key not a string" if ref($hash_key);
# my $dis = $self->config->disable_modes;
# if (defined($dis) && ref($dis) ne 'ARRAY') {
# die "Invalid config value `disable_modes`: must be an array";
# }
# for my $mh (sort { $b->precedence_level <=> $a->precedence_level }
# grep { !$dis || !$self->_in($_->name, $dis) }
# values %{ $self->modes }) {
# if ($mh->check_prefix($hash_key)) {
# my $r = $mh->remove_prefix($hash_key);
# if (wantarray) { return ($r, $mh->name) }
# else { return $r }
# }
# }
# if (wantarray) { return ($hash_key, $self->config->default_mode) }
# else { return $hash_key }
#}
#
#sub remove_prefix_on_hash {
# my ($self, $hash) = @_;
# die "Not a hash" unless ref($hash) eq 'HASH';
# for (keys %$hash) {
# my $old = $_;
# $_ = $self->remove_prefix($_);
# next unless $old ne $_;
# die "Conflict when removing prefix on hash: $old -> $_ but $_ already exists"
# if exists $hash->{$_};
# $hash->{$_} = $hash->{$old};
# delete $hash->{$old};
# }
# $hash;
#}
#
#sub merge {
# my ($self, $l, $r) = @_;
# $self->path([]);
# $self->errors([]);
# $self->mem({});
# $self->cur_mem_key(undef);
# my ($key, $res, $backup) = $self->_merge(undef, $l, $r);
# {
# success => !@{ $self->errors },
# error => (@{ $self->errors } ?
# join(", ",
# map { sprintf("/%s: %s", join("/", @{ $_->[0] }), $_->[1]) }
# @{ $self->errors }) : ''),
# result => $res,
# backup => $backup,
# };
#}
#
## handle circular refs: process todo's
#sub _process_todo {
# my ($self) = @_;
# if ($self->cur_mem_key) {
# for my $mk (keys %{ $self->mem }) {
# my $res = $self->mem->{$mk}{res};
# if (defined($res) && @{ $self->mem->{$mk}{todo} }) {
# #print "DEBUG: processing todo for mem<$mk>\n";
# for (@{ $self->mem->{$mk}{todo} }) {
# $_->(@$res);
# return if @{ $self->errors };
# }
# $self->mem->{$mk}{todo} = [];
# }
# }
# }
#}
#
#sub _merge {
# my ($self, $key, $l, $r, $mode) = @_;
# my $c = $self->config;
# $mode //= $c->default_mode;
#
# my $mh = $self->modes->{$mode};
# die "Can't find handler for mode $mode" unless $mh;
#
# # determine which merge method we will call
# my $rl = ref($l);
# my $rr = ref($r);
# my $tl = $rl eq 'HASH' ? 'HASH' : $rl eq 'ARRAY' ? 'ARRAY' : $rl eq 'CODE' ? 'CODE' : !$rl ? 'SCALAR' : '';
# my $tr = $rr eq 'HASH' ? 'HASH' : $rr eq 'ARRAY' ? 'ARRAY' : $rr eq 'CODE' ? 'CODE' : !$rr ? 'SCALAR' : '';
# if (!$tl) { $self->push_error("Unknown type in left side: $rl"); return }
# if (!$tr) { $self->push_error("Unknown type in right side: $rr"); return }
# if (!$c->allow_create_array && $tl ne 'ARRAY' && $tr eq 'ARRAY') {
# $self->push_error("Not allowed to create array"); return;
# }
# if (!$c->allow_create_hash && $tl ne 'HASH' && $tr eq 'HASH') {
# $self->push_error("Not allowed to create hash"); return;
# }
# if (!$c->allow_destroy_array && $tl eq 'ARRAY' && $tr ne 'ARRAY') {
# $self->push_error("Not allowed to destroy array"); return;
# }
# if (!$c->allow_destroy_hash && $tl eq 'HASH' && $tr ne 'HASH') {
# $self->push_error("Not allowed to destroy hash"); return;
# }
# my $meth = "merge_${tl}_${tr}";
# if (!$mh->can($meth)) { $self->push_error("No merge method found for $tl + $tr (mode $mode)"); return }
#
# #$self->_process_todo;
# # handle circular refs: add to todo if necessary
# my $memkey;
# if ($rl || $rr) {
# $memkey = sprintf "%s%s %s%s %s %s",
# (defined($l) ? ($rl ? 2 : 1) : 0),
# (defined($l) ? "$l" : ''),
# (defined($r) ? ($rr ? 2 : 1) : 0),
# (defined($r) ? "$r" : ''),
# $mode,
# $self->config;
# #print "DEBUG: number of keys in mem = ".scalar(keys %{ $self->mem })."\n";
# #print "DEBUG: mem keys = \n".join("", map { " $_\n" } keys %{ $self->mem }) if keys %{ $self->mem };
# #print "DEBUG: calculating memkey = <$memkey>\n";
# }
# if ($memkey) {
# if (exists $self->mem->{$memkey}) {
# $self->_process_todo;
# if (defined $self->mem->{$memkey}{res}) {
# #print "DEBUG: already calculated, using cached result\n";
# return @{ $self->mem->{$memkey}{res} };
# } else {
# #print "DEBUG: detecting circular\n";
# return ($key, undef, undef, 1);
# }
# } else {
# $self->mem->{$memkey} = {res=>undef, todo=>[]};
# $self->cur_mem_key($memkey);
# #print "DEBUG: invoking ".$mh->name."'s $meth(".dmp($key).", ".dmp($l).", ".dmp($r).")\n";
# my ($newkey, $res, $backup) = $mh->$meth($key, $l, $r);
# #print "DEBUG: setting res for mem<$memkey>\n";
# $self->mem->{$memkey}{res} = [$newkey, $res, $backup];
# $self->_process_todo;
# return ($newkey, $res, $backup);
# }
# } else {
# $self->_process_todo;
# #print "DEBUG: invoking ".$mh->name."'s $meth(".dmp($key).", ".dmp($l).", ".dmp($r).")\n";
# return $mh->$meth($key, $l, $r);
# }
#}
#
## returns 1 if a is included in b (e.g. [user => "jajang"] in included in [user
## => jajang => "quota"], but [user => "paijo"] is not)
#sub _path_is_included {
# my ($self, $p1, $p2) = @_;
# my $res = 1;
# for my $i (0..@$p1-1) {
# do { $res = 0; last } if !defined($p2->[$i]) || $p1->[$i] ne $p2->[$i];
# }
# #print "_path_is_included([".join(", ", @$p1)."], [".join(", ", @$p2)."])? $res\n";
# $res;
#}
#
#1;
## ABSTRACT: Merge two nested data structures, with merging modes and options
#
#__END__
#
#=pod
#
#=encoding UTF-8
#
#=head1 NAME
#
#Data::ModeMerge - Merge two nested data structures, with merging modes and options
#
#=head1 VERSION
#
#This document describes version 0.35 of Data::ModeMerge (from Perl distribution Data-ModeMerge), released on 2016-07-22.
#
#=head1 SYNOPSIS
#
# use Data::ModeMerge;
#
# my $hash1 = { a=>1, c=>1, d=>{ da =>[1]} };
# my $hash2 = { a=>2, "-c"=>2, d=>{"+da"=>[2]} };
#
#
# # if you want Data::ModeMerge to behave like many other merging
# # modules (e.g. Hash::Merge or Data::Merger), turn off modes
# # (prefix) parsing and options key parsing.
#
# my $mm = Data::ModeMerge->new(config => {parse_prefix=>0, options_key=>undef});
# my $res = $mm->merge($hash1, $hash2);
# die $res->{error} if $res->{error};
# # $res->{result} -> { a=>2, c=>1, "-c"=>2, d=>{da=>[1], "+da"=>[2]} }
#
#
# # otherwise Data::ModeMerge will parse prefix as well as options
# # key
#
# my $res = $mm->merge($hash1, $hash2);
# die $res->{error} if $res->{error};
script/_reposdb-inline view on Meta::CPAN
#method for more details.
#
#=head1 ATTRIBUTES
#
#=head2 config
#
#A hashref for config. See L<Data::ModeMerge::Config>.
#
#=head2 modes
#
#=head2 combine_rules
#
#=head2 path
#
#=head2 errors
#
#=head2 mem
#
#=head2 cur_mem_key
#
#=head1 METHODS
#
#For typical usage, you only need merge().
#
#=head2 push_error($errmsg)
#
#Used by mode handlers to push error when doing merge. End users
#normally should not need this.
#
#=head2 register_mode($name_or_package_or_obj)
#
#Register a mode. Will die if mode with the same name already exists.
#
#=head2 check_prefix($hash_key)
#
#Check whether hash key has prefix for certain mode. Return the name of
#the mode, or undef if no prefix is detected.
#
#=head2 check_prefix_on_hash($hash)
#
#This is like C<check_prefix> but performed on every key of the
#specified hash. Return true if any of the key contain a merge prefix.
#
#=head2 add_prefix($hash_key, $mode)
#
#Return hash key with added prefix with specified mode. Log merge error
#if mode is unknown or is disabled.
#
#=head2 remove_prefix($hash_key)
#
#Return hash key will any prefix removed.
#
#=head2 remove_prefix_on_hash($hash)
#
#This is like C<remove_prefix> but performed on every key of the
#specified hash. Return the same hash but with prefixes removed.
#
#=head2 merge($l, $r)
#
#Merge two nested data structures. Returns the result hash: {
#success=>0|1, error=>'...', result=>..., backup=>... }. The 'error'
#key is set to contain an error message if there is an error. The merge
#result is in the 'result' key. The 'backup' key contains replaced
#elements from the original hash/array.
#
#=head1 FAQ
#
#=head2 What is this module good for? Why would I want to use this module instead of the other hash merge modules?
#
#If you just need to (deeply) merge two hashes, chances are you do not
#need this module. Use, for example, L<Hash::Merge>, which is also
#flexible enough because it allows you to set merging behaviour for
#merging different types (e.g. SCALAR vs ARRAY).
#
#You might need this module if your data is recursive/self-referencing
#(which, last time I checked, is not handled well by Hash::Merge), or
#if you want to be able to merge differently (i.e. apply different
#merging B<modes>) according to different prefixes on the key, or
#through special key. In other words, you specify merging modes from
#inside the hash itself.
#
#I originally wrote Data::ModeMerge this for L<Data::Schema> and
#L<Config::Tree>. I want to reuse the "parent" schema (or
#configuration) in more ways other than just override conflicting
#keys. I also want to be able to allow the parent to protect certain
#keys from being overriden. I found these two features lacking in all
#merging modules that I've evaluated prior to writing Data::ModeMerge.
#
#=head1 HOMEPAGE
#
#Please visit the project's homepage at L<https://metacpan.org/release/Data-ModeMerge>.
#
#=head1 SOURCE
#
#Source repository is at L<https://github.com/perlancar/perl-Data-ModeMerge>.
#
#=head1 BUGS
#
#Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-ModeMerge>
#
#When submitting a bug or request, please include a test-file or a
#patch to an existing test-file that illustrates the bug or desired
#feature.
#
#=head1 SEE ALSO
#
#L<Data::ModeMerge::Config>
#
#Other merging modules on CPAN: L<Data::Merger> (from Data-Utilities),
#L<Hash::Merge>, L<Hash::Merge::Simple>
#
#L<Data::Schema> and L<Config::Tree> (among others, two modules which
#use Data::ModeMerge)
#
#=head1 AUTHOR
#
#perlancar <perlancar@cpan.org>
#
#=head1 COPYRIGHT AND LICENSE
#
#This software is copyright (c) 2016 by perlancar@cpan.org.
#
#This is free software; you can redistribute it and/or modify it under
script/_reposdb-inline view on Meta::CPAN
#
#sub name {
# die "Subclass must provide name()";
#}
#
#sub precedence_level {
# die "Subclass must provide precedence_level()";
#}
#
#sub default_prefix {
# die "Subclass must provide default_prefix()";
#}
#
#sub default_prefix_re {
# die "Subclass must provide default_prefix_re()";
#}
#
#sub BUILD {
# my ($self) = @_;
# $self->prefix($self->default_prefix);
# $self->prefix_re($self->default_prefix_re);
#}
#
#sub check_prefix {
# my ($self, $hash_key) = @_;
# if ($self->check_prefix_sub) {
# $self->check_prefix_sub->($hash_key);
# } else {
# $hash_key =~ $self->prefix_re;
# }
#}
#
#sub add_prefix {
# my ($self, $hash_key) = @_;
# if ($self->add_prefix_sub) {
# $self->add_prefix_sub->($hash_key);
# } else {
# $self->prefix . $hash_key;
# }
#}
#
#sub remove_prefix {
# my ($self, $hash_key) = @_;
# if ($self->remove_prefix_sub) {
# $self->remove_prefix_sub->($hash_key);
# } else {
# my $re = $self->prefix_re;
# $hash_key =~ s/$re//;
# $hash_key;
# }
#}
#
#sub merge_ARRAY_ARRAY {
# my ($self, $key, $l, $r) = @_;
# my $mm = $self->merger;
# my $c = $mm->config;
# return $self->merge_SCALAR_SCALAR($key, $l, $r) unless $c->recurse_array;
# return if $c->wanted_path && !$mm->_path_is_included($mm->path, $c->wanted_path);
#
# my @res;
# my @backup;
# my $la = @$l;
# my $lb = @$r;
# push @{ $mm->path }, -1;
# for my $i (0..($la > $lb ? $la : $lb)-1) {
# #print "DEBUG: merge_A_A: #$i: a->[$i]=".Data::Dumper->new([$l->[$i]])->Indent(0)->Terse(1)->Dump.", b->[$i]=".Data::Dumper->new([$r->[$i]])->Indent(0)->Terse(1)->Dump."\n";
# $mm->path->[-1] = $i;
# if ($i < $la && $i < $lb) {
# push @backup, $l->[$i];
# my ($subnewkey, $subres, $subbackup, $is_circular) = $mm->_merge($i, $l->[$i], $r->[$i], $c->default_mode);
# last if @{ $mm->errors };
# if ($is_circular) {
# push @res, undef;
# #print "DEBUG: pushing todo to mem<".$mm->cur_mem_key.">\n";
# push @{ $mm->mem->{ $mm->cur_mem_key }{todo} }, sub {
# my ($subnewkey, $subres, $subbackup) = @_;
# #print "DEBUG: Entering todo subroutine (i=$i)\n";
# $res[$i] = $subres;
# }
# } else {
# push @res, $subres;# if defined($newkey); = we allow DELETE on array?
# }
# } elsif ($i < $la) {
# push @res, $l->[$i];
# } else {
# push @res, $r->[$i];
# }
# }
# pop @{ $mm->path };
# ($key, \@res, \@backup);
#}
#
#sub _prefilter_hash {
# my ($self, $h, $desc, $sub) = @_;
# my $mm = $self->merger;
#
# if (ref($sub) ne 'CODE') {
# $mm->push_error("$desc failed: filter must be a coderef");
# return;
# }
#
# my $res = {};
# for (keys %$h) {
# my @r = $sub->($_, $h->{$_});
# while (my ($k, $v) = splice @r, 0, 2) {
# next unless defined $k;
# if (exists $res->{$k}) {
# $mm->push_error("$desc failed; key conflict: ".
# "$_ -> $k, but key $k already exists");
# return;
# }
# $res->{$k} = $v;
# }
# }
#
# $res;
#}
#
## turn {[prefix]key => val, ...} into { key => [MODE, val], ...}, push
## error if there's conflicting key
#sub _gen_left {
# my ($self, $l, $mode, $esub, $ep, $ip, $epr, $ipr) = @_;
# my $mm = $self->merger;
# my $c = $mm->config;
#
# #print "DEBUG: Entering _gen_left(".dmp($l).", $mode, ...)\n";
#
# if ($c->premerge_pair_filter) {
# $l = $self->_prefilter_hash($l, "premerge filter left hash",
# $c->premerge_pair_filter);
# return if @{ $mm->errors };
# }
#
# my $hl = {};
# if ($c->parse_prefix) {
# for (keys %$l) {
# my $do_parse = 1;
# $do_parse = 0 if $do_parse && $ep && $mm->_in($_, $ep);
# $do_parse = 0 if $do_parse && $ip && !$mm->_in($_, $ip);
# $do_parse = 0 if $do_parse && $epr && /$epr/;
# $do_parse = 0 if $do_parse && $ipr && !/$ipr/;
#
# if ($do_parse) {
# my $old = $_;
# my $m2;
# ($_, $m2) = $mm->remove_prefix($_);
# next if $esub && !$esub->($_);
# if ($old ne $_ && exists($l->{$_})) {
# $mm->push_error("Conflict when removing prefix on left-side ".
# "hash key: $old -> $_ but $_ already exists");
script/_reposdb-inline view on Meta::CPAN
## push error if there's conflicting key+MODE
#sub _gen_right {
# my ($self, $r, $mode, $esub, $ep, $ip, $epr, $ipr) = @_;
# my $mm = $self->merger;
# my $c = $mm->config;
#
# #print "DEBUG: Entering _gen_right(".dmp($r).", $mode, ...)\n";
#
# if ($c->premerge_pair_filter) {
# $r = $self->_prefilter_hash($r, "premerge filter right hash",
# $c->premerge_pair_filter);
# return if @{ $mm->errors };
# }
#
# my $hr = {};
# if ($c->parse_prefix) {
# for (keys %$r) {
# my $do_parse = 1;
# $do_parse = 0 if $do_parse && $ep && $mm->_in($_, $ep);
# $do_parse = 0 if $do_parse && $ip && !$mm->_in($_, $ip);
# $do_parse = 0 if $do_parse && $epr && /$epr/;
# $do_parse = 0 if $do_parse && $ipr && !/$ipr/;
#
# if ($do_parse) {
# my $old = $_;
# my $m2;
# ($_, $m2) = $mm->remove_prefix($_);
# next if $esub && !$esub->($_);
# if (exists $hr->{$_}{$m2}) {
# $mm->push_error("Conflict when removing prefix on right-side ".
# "hash key: $old($m2) -> $_ ($m2) but $_ ($m2) ".
# "already exists");
# return;
# }
# $hr->{$_}{$m2} = $r->{$old};
# } else {
# next if $esub && !$esub->($_);
# $hr->{$_} = {$mode => $r->{$_}};
# }
# }
# } else {
# for (keys %$r) {
# next if $esub && !$esub->($_);
# $hr->{$_} = {$mode => $r->{$_}}
# }
# }
# #print "DEBUG: Leaving _gen_right, result = ".dmp($hr)."\n";
# $hr;
#}
#
## merge two hashes which have been prepared by _gen_left and
## _gen_right, will result in { key => [final_mode, val], ... }
#sub _merge_gen {
# my ($self, $hl, $hr, $mode, $em, $im, $emr, $imr) = @_;
# my $mm = $self->merger;
# my $c = $mm->config;
#
# #print "DEBUG: Entering _merge_gen(".dmp($hl).", ".dmp($hr).", $mode, ...)\n";
#
# my $res = {};
# my $backup = {};
#
# my %k = map {$_=>1} keys(%$hl), keys(%$hr);
# push @{ $mm->path }, "";
# K:
# for my $k (keys %k) {
# my @o;
# $mm->path->[-1] = $k;
# my $do_merge = 1;
# $do_merge = 0 if $do_merge && $em && $mm->_in($k, $em);
# $do_merge = 0 if $do_merge && $im && !$mm->_in($k, $im);
# $do_merge = 0 if $do_merge && $emr && $k =~ /$emr/;
# $do_merge = 0 if $do_merge && $imr && $k !~ /$imr/;
#
# if (!$do_merge) {
# $res->{$k} = $hl->{$k} if $hl->{$k};
# next K;
# }
#
# $backup->{$k} = $hl->{$k}[1] if $hl->{$k} && $hr->{$k};
# if ($hl->{$k}) {
# push @o, $hl->{$k};
# }
# if ($hr->{$k}) {
# my %m = map {$_=>$mm->modes->{$_}->precedence_level} keys %{ $hr->{$k} };
# #print "DEBUG: \\%m=".Data::Dumper->new([\%m])->Indent(0)->Terse(1)->Dump."\n";
# push @o, map { [$_, $hr->{$k}{$_}] } sort { $m{$b} <=> $m{$a} } keys %m;
# }
# my $final_mode;
# my $is_circular;
# my $v;
# #print "DEBUG: k=$k, o=".Data::Dumper->new([\@o])->Indent(0)->Terse(1)->Dump."\n";
# for my $i (0..$#o) {
# if ($i == 0) {
# my $mh = $mm->modes->{$o[$i][0]};
# if (@o == 1 &&
# (($hl->{$k} && $mh->can("merge_left_only")) ||
# ($hr->{$k} && $mh->can("merge_right_only")))) {
# # there's only left-side or right-side
# my $meth = $hl->{$k} ? "merge_left_only" : "merge_right_only";
# my ($subnewkey, $v, $subbackup, $is_circular, $newmode) = $mh->$meth($k, $o[$i][1]); # XXX handle circular?
# next K unless defined($subnewkey);
# $final_mode = $newmode;
# $v = $res;
# } else {
# $final_mode = $o[$i][0];
# $v = $o[$i][1];
# }
# } else {
# my $m = $mm->combine_rules->{"$final_mode+$o[$i][0]"}
# or do {
# $mm->push_error("Can't merge $final_mode + $o[$i][0]");
# return;
# };
# #print "DEBUG: merge $final_mode+$o[$i][0] = $m->[0], $m->[1]\n";
# my ($subnewkey, $subbackup);
# ($subnewkey, $v, $subbackup, $is_circular) = $mm->_merge($k, $v, $o[$i][1], $m->[0]);
# return if @{ $mm->errors };
# if ($is_circular) {
# if ($i < $#o) {
# $mm->push_error("Can't handle circular at $i of $#o merges (mode $m->[0]): not the last merge");
# return;
# }
# #print "DEBUG: pushing todo to mem<".$mm->cur_mem_key.">\n";
# push @{ $mm->mem->{ $mm->cur_mem_key }{todo} }, sub {
# my ($subnewkey, $subres, $subbackup) = @_;
# #print "DEBUG: Entering todo subroutine (k=$k)\n";
# my $final_mode = $m->[1];
# #XXX return unless defined($subnewkey);
# $res->{$k} = [$m->[1], $subres];
# if ($c->readd_prefix) {
# # XXX if there is a conflict error in
# # _readd_prefix, how to adjust path?
# $self->_readd_prefix($res, $k, $c->default_mode);
# } else {
# $res->{$k} = $res->{$k}[1];
# }
# };
# delete $res->{$k};
# }
# next K unless defined $subnewkey;
# $final_mode = $m->[1];
# }
# }
# $res->{$k} = [$final_mode, $v] unless $is_circular;
# }
# pop @{ $mm->path };
# #print "DEBUG: Leaving _merge_gen, res = ".dmp($res)."\n";
# ($res, $backup);
#}
#
## hh is {key=>[MODE, val], ...} which is the format returned by _merge_gen
#sub _readd_prefix {
# my ($self, $hh, $k, $defmode) = @_;
# my $mm = $self->merger;
# my $c = $mm->config;
#
# my $m = $hh->{$k}[0];
# if ($m eq $defmode) {
# $hh->{$k} = $hh->{$k}[1];
# } else {
# my $kp = $mm->modes->{$m}->add_prefix($k);
# if (exists $hh->{$kp}) {
# $mm->push_error("BUG: conflict when re-adding prefix after merge: $kp");
# return;
# }
# $hh->{$kp} = $hh->{$k}[1];
# delete $hh->{$k};
# }
#}
#
#sub merge_HASH_HASH {
# my ($self, $key, $l, $r, $mode) = @_;
# my $mm = $self->merger;
# my $c = $mm->config;
# $mode //= $c->default_mode;
# #print "DEBUG: entering merge_H_H(".dmp($l).", ".dmp($r).", $mode), config=($c)=",dmp($c),"\n";
# #$log->trace("using config($c)");
#
# return $self->merge_SCALAR_SCALAR($key, $l, $r) unless $c->recurse_hash;
# return if $c->wanted_path && !$mm->_path_is_included($mm->path, $c->wanted_path);
#
# # STEP 1. MERGE LEFT & RIGHT OPTIONS KEY
# my $config_replaced;
# my $orig_c = $c;
# my $ok = $c->options_key;
# {
# last unless defined $ok;
#
# my $okl = $self->_gen_left ($l, $mode, sub {$_[0] eq $ok});
# return if @{ $mm->errors };
#
# my $okr = $self->_gen_right($r, $mode, sub {$_[0] eq $ok});
# return if @{ $mm->errors };
#
# push @{ $mm->path }, $ok;
# my ($res, $backup);
# {
# local $c->{readd_prefix} = 0;
# ($res, $backup) = $self->_merge_gen($okl, $okr, $mode);
# }
# pop @{ $mm->path };
# return if @{ $mm->errors };
#
# #print "DEBUG: merge options key (".dmp($okl).", ".dmp($okr).") = ".dmp($res)."\n";
#
# $res = $res->{$ok} ? $res->{$ok}[1] : undef;
# if (defined($res) && ref($res) ne 'HASH') {
# $mm->push_error("Invalid options key after merge: value must be hash");
# return;
# }
# last unless keys %$res;
# #$log->tracef("cloning config ...");
# # Data::Clone by default does *not* deep-copy object
# #my $c2 = clone($c);
# my $c2 = bless({ %$c }, ref($c));
#
# for (keys %$res) {
# if ($c->allow_override) {
# my $re = $c->allow_override;
# if (!/$re/) {
# $mm->push_error("Configuration in options key `$_` not allowed by allow_override $re");
# return;
# }
# }
# if ($c->disallow_override) {
# my $re = $c->disallow_override;
# if (/$re/) {
# $mm->push_error("Configuration in options key `$_` not allowed by disallow_override $re");
# return;
# }
# }
# if ($mm->_in($_, $c->_config_config)) {
# $mm->push_error("Configuration not allowed in options key: $_");
# return;
# }
# if ($_ ne $ok && !$mm->_in($_, $c->_config_ok)) {
# $mm->push_error("Unknown configuration in options key: $_");
# return;
# }
# $c2->$_($res->{$_}) unless $_ eq $ok;
# }
# $mm->config($c2);
# $config_replaced++;
# $c = $c2;
# #$log->trace("config now changed to $c2");
# }
#
# my $sp = $c->set_prefix;
# my $saved_prefixes;
# if (defined($sp)) {
# if (ref($sp) ne 'HASH') {
# $mm->push_error("Invalid config value `set_prefix`: must be a hash");
# return;
# }
# $saved_prefixes = {};
# for my $mh (values %{ $mm->modes }) {
# my $n = $mh->name;
# if ($sp->{$n}) {
# $saved_prefixes->{$n} = {
script/_reposdb-inline view on Meta::CPAN
# return;
# }
#
# my $epr = $c->exclude_parse_regex;
# my $ipr = $c->include_parse_regex;
# if (defined($epr)) {
# eval { $epr = qr/$epr/ };
# if ($@) {
# $mm->push_error("Invalid config value `exclude_parse_regex`: invalid regex: $@");
# return;
# }
# }
# if (defined($ipr)) {
# eval { $ipr = qr/$ipr/ };
# if ($@) {
# $mm->push_error("Invalid config value `include_parse_regex`: invalid regex: $@");
# return;
# }
# }
#
# # STEP 2. PREPARE LEFT HASH
# my $hl = $self->_gen_left ($l, $mode, sub {defined($ok) ? $_[0] ne $ok : 1}, $ep, $ip, $epr, $ipr);
# return if @{ $mm->errors };
#
# # STEP 3. PREPARE RIGHT HASH
# my $hr = $self->_gen_right($r, $mode, sub {defined($ok) ? $_[0] ne $ok : 1}, $ep, $ip, $epr, $ipr);
# return if @{ $mm->errors };
#
# #print "DEBUG: hl=".Data::Dumper->new([$hl])->Indent(0)->Terse(1)->Dump."\n";
# #print "DEBUG: hr=".Data::Dumper->new([$hr])->Indent(0)->Terse(1)->Dump."\n";
#
# my $em = $c->exclude_merge;
# my $im = $c->include_merge;
# if (defined($em) && ref($em) ne 'ARRAY') {
# $mm->push_error("Invalid config value `exclude_marge`: must be an array");
# return;
# }
# if (defined($im) && ref($im) ne 'ARRAY') {
# $mm->push_error("Invalid config value `include_merge`: must be an array");
# return;
# }
#
# my $emr = $c->exclude_merge_regex;
# my $imr = $c->include_merge_regex;
# if (defined($emr)) {
# eval { $emr = qr/$emr/ };
# if ($@) {
# $mm->push_error("Invalid config value `exclude_merge_regex`: invalid regex: $@");
# return;
# }
# }
# if (defined($imr)) {
# eval { $imr = qr/$imr/ };
# if ($@) {
# $mm->push_error("Invalid config value `include_merge_regex`: invalid regex: $@");
# return;
# }
# }
#
# # STEP 4. MERGE LEFT & RIGHT
# my ($res, $backup) = $self->_merge_gen($hl, $hr, $mode, $em, $im, $emr, $imr);
# return if @{ $mm->errors };
#
# #print "DEBUG: intermediate res(5) = ".Data::Dumper->new([$res])->Indent(0)->Terse(1)->Dump."\n";
#
# # STEP 5. TURN BACK {key=>[MODE=>val]}, ...} INTO {(prefix)key => val, ...}
# if ($c->readd_prefix) {
# for my $k (keys %$res) {
# $self->_readd_prefix($res, $k, $c->default_mode);
# }
# } else {
# $res->{$_} = $res->{$_}[1] for keys %$res;
# }
#
# if ($saved_prefixes) {
# for (keys %$saved_prefixes) {
# my $mh = $mm->modes->{$_};
# my $s = $saved_prefixes->{$_};
# $mh->prefix($s->{prefix});
# $mh->prefix_re($s->{prefix_re});
# $mh->check_prefix_sub($s->{check_prefix_sub});
# $mh->add_prefix_sub($s->{add_prefix_sub});
# $mh->remove_prefix_sub($s->{remove_prefix_sub});
# }
# }
#
# # restore config
# if ($config_replaced) {
# $mm->config($orig_c);
# #print "DEBUG: Restored config, config=", dmp($mm->config), "\n";
# }
#
# #print "DEBUG: backup = ".Data::Dumper->new([$backup])->Indent(0)->Terse(1)->Dump."\n";
# #print "DEBUG: leaving merge_H_H, result = ".dmp($res)."\n";
# ($key, $res, $backup);
#}
#
#1;
## ABSTRACT: Base class for Data::ModeMerge mode handler
#
#__END__
#
#=pod
#
#=encoding UTF-8
#
#=head1 NAME
#
#Data::ModeMerge::Mode::Base - Base class for Data::ModeMerge mode handler
#
#=head1 VERSION
#
#This document describes version 0.35 of Data::ModeMerge::Mode::Base (from Perl distribution Data-ModeMerge), released on 2016-07-22.
#
#=head1 SYNOPSIS
#
# use Data::ModeMerge;
#
#=head1 DESCRIPTION
#
#This is the base class for mode type handlers.
#
#=for Pod::Coverage ^(BUILD|merge_.+)$
#
#=head1 ATTRIBUTES
#
#=head2 merger
#
#=head2 prefix
#
#=head2 prefix_re
#
#=head2 check_prefix_sub
#
#=head2 add_prefix_sub
#
#=head2 remove_prefix_sub
#
#=head1 METHODS
#
#=head2 name
#
#Return name of mode. Subclass must override this method.
#
#=head2 precedence_level
#
#Return precedence level, which is a number. The greater the number,
#the higher the precedence. Subclass must override this method.
#
#=head2 default_prefix
#
#Return default prefix. Subclass must override this method.
#
#=head2 default_prefix_re
#
script/_reposdb-inline view on Meta::CPAN
#=head1 SYNOPSIS
#
# use Data::ModeMerge;
#
#=head1 DESCRIPTION
#
#This is the class to handle CONCAT merge mode.
#
#=for Pod::Coverage ^merge_.*
#
#=head1 HOMEPAGE
#
#Please visit the project's homepage at L<https://metacpan.org/release/Data-ModeMerge>.
#
#=head1 SOURCE
#
#Source repository is at L<https://github.com/perlancar/perl-Data-ModeMerge>.
#
#=head1 BUGS
#
#Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-ModeMerge>
#
#When submitting a bug or request, please include a test-file or a
#patch to an existing test-file that illustrates the bug or desired
#feature.
#
#=head1 AUTHOR
#
#perlancar <perlancar@cpan.org>
#
#=head1 COPYRIGHT AND LICENSE
#
#This software is copyright (c) 2016 by perlancar@cpan.org.
#
#This is free software; you can redistribute it and/or modify it under
#the same terms as the Perl 5 programming language system itself.
#
#=cut
### Data/ModeMerge/Mode/DELETE.pm ###
#package Data::ModeMerge::Mode::DELETE;
#
#our $DATE = '2016-07-22'; # DATE
#our $VERSION = '0.35'; # VERSION
#
#use 5.010;
#use strict;
#use warnings;
#use Mo qw(build default);
#extends 'Data::ModeMerge::Mode::Base';
#
#sub name { 'DELETE' }
#
#sub precedence_level { 1 }
#
#sub default_prefix { '!' }
#
#sub default_prefix_re { qr/^!/ }
#
## merge_left_only and merge_right_only are a bit different: they are
## called with $l only or $r only instead of both, and should return an
## extra argument $mode, i.e. ($key, $result, $backup, $is_circular,
## $mode)
#sub merge_left_only {
# my ($self, $key, $l) = @_;
# return;
#}
#
#sub merge_right_only {
# my ($self, $key, $r) = @_;
# return;
#}
#
#sub merge_SCALAR_SCALAR {
# return;
#}
#
#sub merge_SCALAR_ARRAY {
# return;
#}
#
#sub merge_SCALAR_HASH {
# return;
#}
#
#sub merge_ARRAY_SCALAR {
# return;
#}
#
#sub merge_ARRAY_ARRAY {
# my ($self, $key, $l, $r) = @_;
# $self->merger->config->allow_destroy_array or
# $self->merger->push_error("Now allowed to destroy array via DELETE mode");
# return;
#}
#
#sub merge_ARRAY_HASH {
# return;
#}
#
#sub merge_HASH_SCALAR {
# return;
#}
#
#sub merge_HASH_ARRAY {
# return;
#}
#
#sub merge_HASH_HASH {
# my ($self, $key, $l, $r) = @_;
# $self->merger->config->allow_destroy_hash or
# $self->merger->push_error("Now allowed to destroy hash via DELETE mode");
# return;
#}
#
#1;
## ABSTRACT: Handler for Data::ModeMerge DELETE merge mode
#
#__END__
#
#=pod
#
( run in 1.558 second using v1.01-cache-2.11-cpan-39bf76dae61 )