Skip to content

Commit

Permalink
ndproc: Patch (nested diff) module added
Browse files Browse the repository at this point in the history
  • Loading branch information
mr-mixas committed Oct 11, 2018
1 parent effa9b8 commit 2112e5f
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 0 deletions.
11 changes: 11 additions & 0 deletions MANIFEST
Expand Up @@ -8,6 +8,7 @@ lib/App/NDTools/NDProc/Module.pm
lib/App/NDTools/NDProc/Module/Insert.pm
lib/App/NDTools/NDProc/Module/JsonMergePatch.pm
lib/App/NDTools/NDProc/Module/Merge.pm
lib/App/NDTools/NDProc/Module/Patch.pm
lib/App/NDTools/NDProc/Module/Pipe.pm
lib/App/NDTools/NDProc/Module/Remove.pm
lib/App/NDTools/NDQuery.pm
Expand Down Expand Up @@ -215,6 +216,16 @@ t/bin/ndproc-Merge.d/source_self.rules.json
t/bin/ndproc-Merge.d/strict_disabled.exp
t/bin/ndproc-Merge.d/style.exp
t/bin/ndproc-Merge.t
t/bin/ndproc-Patch.d/_bool.a.json
t/bin/ndproc-Patch.d/_bool.b.json
t/bin/ndproc-Patch.d/path.exp
t/bin/ndproc-Patch.d/path.patch
t/bin/ndproc-Patch.d/path_absent.patch
t/bin/ndproc-Patch.d/path_empty.patch
t/bin/ndproc-Patch.d/preserve.exp
t/bin/ndproc-Patch.d/preserve.patch
t/bin/ndproc-Patch.d/source_broken.patch
t/bin/ndproc-Patch.t
t/bin/ndproc-Pipe.d/_cfg.alpha.json
t/bin/ndproc-Pipe.d/path.exp
t/bin/ndproc-Pipe.d/path_absent.exp
Expand Down
1 change: 1 addition & 0 deletions lib/App/NDTools.pm
Expand Up @@ -48,6 +48,7 @@ Available modules:
Insert Insert value into structure
JsonMergePatch Apply JSON Merge Patch (rfc7396) patches
Merge Merge structures according provided rules
Patch Apply nested diff to the structure
Pipe Modify structure using external process
Remove Remove specified parts from structure
Expand Down
92 changes: 92 additions & 0 deletions lib/App/NDTools/NDProc/Module/Patch.pm
@@ -0,0 +1,92 @@
package App::NDTools::NDProc::Module::Patch;

use strict;
use warnings FATAL => 'all';
use parent 'App::NDTools::NDProc::Module';

use Log::Log4Cli;
use Struct::Diff qw(patch);
use Struct::Path 0.80 qw(path);

our $VERSION = '0.01';

sub MODINFO { "Apply nested diff to the structure" }

sub arg_opts {
my $self = shift;

return (
$self->SUPER::arg_opts(),
'source=s' => \$self->{OPTS}->{source},
'strict!' => \$self->{OPTS}->{strict},
)
}

sub check_rule {
my ($self, $rule) = @_;

die_fatal "Source file should be specified", 1
unless ($rule->{source});

push @{$rule->{path}}, '' unless (@{$rule->{path}});

return $self;
}

sub configure {
my $self = shift;

# to prevent source resolve to target (ndproc defaults)
delete $self->{OPTS}->{source}
unless (defined $self->{OPTS}->{source});
}

sub process_path {
my ($self, $data, $path, $spath, $opts, $source) = @_;

my @refs = eval { path(${$data}, $spath, strict => $opts->{strict}) };
die_fatal "Failed to lookup path '$path'", 4 if ($@);

map { patch(${$_}, $source) } @refs;
}

1; # End of App::NDTools::NDProc::Module::Patch

__END__
=head1 NAME
Patch - Apply nested diff to the structure
=head1 OPTIONS
=over 4
=item B<--[no]blame>
Blame calculation toggle. Enabled by default.
=item B<--path> E<lt>pathE<gt>
Path in the structure to patch. May be used several times. Whole structure
will be patched if omitted or empty.
=item B<--preserve> E<lt>pathE<gt>
Preserve specified substructure. May be used several times.
=item B<--source> E<lt>uriE<gt>
Source containing patch.
=item B<--strict>
Fail if path specified for patch doesn't exist.
=back
=head1 SEE ALSO
L<ndproc>, L<ndproc-modules>
L<nddiff>, L<ndquery>, L<Struct::Path::PerlStyle>
1 change: 1 addition & 0 deletions ndproc
Expand Up @@ -93,6 +93,7 @@ Print version and exit.
Insert Insert value into structure
JsonMergePatch Apply JSON Merge Patch (rfc7396) patches
Merge Merge structures according provided rules
Patch Apply nested diff to the structure
Pipe Modify structure using external process
Remove Remove specified parts from structure
Expand Down
14 changes: 14 additions & 0 deletions t/bin/ndproc-Patch.d/_bool.a.json
@@ -0,0 +1,14 @@
{
"false" : false,
"list" : [
true,
false,
true,
false
],
"same" : {
"false" : false,
"true" : true
},
"true" : true
}
14 changes: 14 additions & 0 deletions t/bin/ndproc-Patch.d/_bool.b.json
@@ -0,0 +1,14 @@
{
"false" : true,
"list" : [
true,
true,
false,
false
],
"same" : {
"false" : false,
"true" : true
},
"true" : false
}
14 changes: 14 additions & 0 deletions t/bin/ndproc-Patch.d/path.exp
@@ -0,0 +1,14 @@
{
"false" : false,
"list" : [
true,
false,
true,
false
],
"same" : {
"false" : false,
"true" : true
},
"true" : false
}
3 changes: 3 additions & 0 deletions t/bin/ndproc-Patch.d/path.patch
@@ -0,0 +1,3 @@
{
"N" : false
}
22 changes: 22 additions & 0 deletions t/bin/ndproc-Patch.d/path_absent.patch
@@ -0,0 +1,22 @@
{
"D" : {
"false" : {
"N" : true
},
"list" : {
"D" : [
{
"I" : 1,
"R" : false
},
{
"A" : false,
"I" : 3
}
]
},
"true" : {
"N" : false
}
}
}
22 changes: 22 additions & 0 deletions t/bin/ndproc-Patch.d/path_empty.patch
@@ -0,0 +1,22 @@
{
"D" : {
"false" : {
"N" : true
},
"list" : {
"D" : [
{
"I" : 1,
"R" : false
},
{
"A" : false,
"I" : 3
}
]
},
"true" : {
"N" : false
}
}
}
14 changes: 14 additions & 0 deletions t/bin/ndproc-Patch.d/preserve.exp
@@ -0,0 +1,14 @@
{
"false" : true,
"list" : [
true,
true,
false,
false
],
"same" : {
"false" : false,
"true" : true
},
"true" : true
}
22 changes: 22 additions & 0 deletions t/bin/ndproc-Patch.d/preserve.patch
@@ -0,0 +1,22 @@
{
"D" : {
"false" : {
"N" : true
},
"list" : {
"D" : [
{
"I" : 1,
"R" : false
},
{
"A" : false,
"I" : 3
}
]
},
"true" : {
"N" : false
}
}
}
1 change: 1 addition & 0 deletions t/bin/ndproc-Patch.d/source_broken.patch
@@ -0,0 +1 @@
garbage
76 changes: 76 additions & 0 deletions t/bin/ndproc-Patch.t
@@ -0,0 +1,76 @@
use strict;
use warnings FATAL => 'all';

use File::Copy qw(copy);
use Test::File::Contents;
use Test::More tests => 8;

use App::NDTools::Test;

chdir t_dir or die "Failed to change test dir";

my $test;
my $mod = 'App::NDTools::NDProc';
my @cmd = ($mod, '--module', 'Patch');

require_ok($mod) || BAIL_OUT("Failed to load $mod");

$test = "path";
run_ok(
name => $test,
pre => sub { copy("_bool.a.json", "$test.got") },
cmd => [ @cmd, '--source', "$test.patch", '--path', '{true}', "$test.got" ],
test => sub { files_eq_or_diff("$test.exp", "$test.got", $test) },
);

$test = "path_absent";
run_ok(
name => $test,
pre => sub { copy("_bool.a.json", "$test.got") },
cmd => [ @cmd, '--source', "$test.patch", "$test.got" ],
test => sub { files_eq_or_diff("_bool.b.json", "$test.got", $test) },
);

$test = "path_empty"; # full doc patched
run_ok(
name => $test,
pre => sub { copy("_bool.a.json", "$test.got") },
cmd => [ @cmd, '--source', "$test.patch", '--path', '', "$test.got" ],
test => sub { files_eq_or_diff("_bool.b.json", "$test.got", $test) },
);

$test = "path_strict";
run_ok(
name => $test,
pre => sub { copy("_bool.a.json", "$test.got") },
cmd => [ @cmd, '--source', '_bool.b.json', '--path', '{some}{absent}{path}', '--strict', "$test.got" ],
stderr => qr/ FATAL] Failed to lookup path /,
exit => 4,
);

$test = "preserve";
run_ok(
name => $test,
pre => sub { copy("_bool.a.json", "$test.got") },
cmd => [ @cmd, '--source', "$test.patch", '--preserve', '{true}', "$test.got" ],
test => sub { files_eq_or_diff("$test.exp", "$test.got", $test) },
);

$test = "source_absent";
run_ok(
name => $test,
pre => sub { copy("_bool.a.json", "$test.got") },
cmd => [ @cmd, '--path', '{true}', "$test.got" ],
stderr => qr/ FATAL] Source file should be specified/,
exit => 1,
);

$test = "source_broken";
run_ok(
name => $test,
pre => sub { copy("_bool.a.json", "$test.got") },
cmd => [ @cmd, '--source', "$test.patch", "$test.got" ],
stderr => qr/ FATAL] Failed to decode 'JSON'/,
exit => 4,
);

0 comments on commit 2112e5f

Please sign in to comment.