File Coverage

blib/lib/JSON/Patch.pm
Criterion Covered Total %
statement 51 61 83.6
branch 23 34 67.6
condition 5 6 83.3
subroutine 10 10 100.0
pod 2 2 100.0
total 91 113 80.5


line stmt bran cond sub pod time code
1             package JSON::Patch;
2              
3 3     3   198068 use 5.006;
  3         32  
4 3     3   15 use strict;
  3         5  
  3         72  
5 3     3   15 use warnings FATAL => 'all';
  3         4  
  3         122  
6 3     3   1369 use parent 'Exporter';
  3         872  
  3         16  
7              
8 3     3   170 use Carp qw(croak);
  3         6  
  3         147  
9 3     3   1766 use Struct::Diff 0.96;
  3         36938  
  3         213  
10 3     3   1423 use Struct::Path 0.82 qw(path);
  3         6417  
  3         240  
11 3     3   1559 use Struct::Path::JsonPointer 0.04 qw(path2str str2path);
  3         3678  
  3         1943  
12              
13             our @EXPORT_OK = qw(
14             diff
15             patch
16             );
17              
18             =head1 NAME
19              
20             JSON::Patch - JSON Patch (L) for
21             perl structures
22              
23             =begin html
24              
25             Travis CI
26             Coverage Status
27             CPAN version
28              
29             =end html
30              
31             =head1 VERSION
32              
33             Version 0.02
34              
35             =cut
36              
37             our $VERSION = '0.02';
38              
39             =head1 SYNOPSIS
40              
41             use Test::More tests => 2;
42             use JSON::Patch qw(diff patch);
43              
44             my $old = {foo => ['bar']};
45             my $new = {foo => ['bar', 'baz']};
46              
47             my $patch = diff($old, $new);
48             is_deeply(
49             $patch,
50             [
51             {op => 'add', path => '/foo/1', value => 'baz'}
52             ]
53             );
54              
55             patch($old, $patch);
56             is_deeply($old, $new);
57              
58             =head1 EXPORT
59              
60             Nothing is exported by default.
61              
62             =head1 SUBROUTINES
63              
64             =head2 diff
65              
66             Calculate patch.
67              
68             $patch = diff($old, $new);
69              
70             =cut
71              
72             sub diff($$) {
73 1     1 1 98 my @stask = Struct::Diff::list_diff
74             Struct::Diff::diff($_[0], $_[1], noO => 1, noU => 1, trimR => 1);
75              
76 1         596 my ($hunk, @patch, $path);
77              
78 1         5 while (@stask) {
79 1         3 ($path, $hunk) = splice @stask, -2, 2;
80              
81 1 50       2 if (exists ${$hunk}->{A}) {
  1 0       4  
    0          
82 1         4 push @patch, {op => 'add', value => ${$hunk}->{A}};
  1         4  
83 0         0 } elsif (exists ${$hunk}->{N}) {
84 0         0 push @patch, {op => 'replace', value => ${$hunk}->{N}};
  0         0  
85 0         0 } elsif (exists ${$hunk}->{R}) {
86 0         0 push @patch, {op => 'remove'};
87             } else {
88 0         0 next;
89             }
90              
91 1         6 $patch[-1]->{path} = path2str($path);
92             }
93              
94 1         59 return \@patch;
95             }
96              
97             =head2 patch
98              
99             Apply patch.
100              
101             patch($target, $patch);
102              
103             =cut
104              
105             sub patch($$) {
106 10     10 1 8082 for my $hunk (@{$_[1]}) {
  10         35  
107 10 100       239 croak "Undefined op value" unless (defined $hunk->{op});
108 9 100       123 croak "Path parameter missing" unless (exists $hunk->{path});
109              
110 8 100       14 my $path = eval { str2path($hunk->{path}) }
  8         28  
111             or croak "Failed to parse 'path' pointer";
112              
113 7 100 66     277 if ($hunk->{op} eq 'add' or $hunk->{op} eq 'replace') {
    100 100        
    100          
    100          
114 2 100       101 croak "Value parameter missing" unless (exists $hunk->{value});
115             path(
116             $_[0],
117             $path,
118             assign => $hunk->{value},
119             expand => 1,
120 1         7 insert => $hunk->{op} eq 'add',
121             strict => 1,
122             );
123              
124             } elsif ($hunk->{op} eq 'remove') {
125 1 50       3 eval { path($_[0], $path, delete => 1) } or
  1         5  
126             croak "Path does not exist";
127              
128             } elsif ($hunk->{op} eq 'move' or $hunk->{op} eq 'copy') {
129 2 100       3 my $from = eval { str2path($hunk->{from}) } or
  2         6  
130             croak "Failed to parse 'from' pointer";
131             my @found = path(
132             $_[0],
133             $from,
134 1         24 delete => $hunk->{op} eq 'move',
135             deref => 1
136             );
137 1 50       127 croak "Source path does not exist" unless (@found);
138              
139 0         0 path($_[0], $path, assign => $found[0], expand => 1);
140              
141             } elsif ($hunk->{op} eq 'test') {
142 1 50       18 croak "Value parameter missing" unless (exists $hunk->{value});
143 1 50       5 my @found = path($_[0], $path, deref => 1) or
144             croak "Path does not exist";
145 0         0 my $diff = Struct::Diff::diff($found[0], $hunk->{value}, noU => 1);
146 0 0       0 croak "Test failed" if (keys %{$diff});
  0         0  
147              
148             } else {
149 1         256 croak "Unsupported op '$hunk->{op}'";
150             }
151             }
152             }
153              
154             =head1 AUTHOR
155              
156             Michael Samoglyadov, C<< >>
157              
158             =head1 BUGS
159              
160             Please report any bugs or feature requests to C,
161             or through the web interface at
162             L. I will be
163             notified, and then you'll automatically be notified of progress on your bug as
164             I make changes.
165              
166             =head1 SUPPORT
167              
168             You can find documentation for this module with the perldoc command.
169              
170             perldoc JSON::Patch
171              
172             You can also look for information at:
173              
174             =over 4
175              
176             =item * RT: CPAN's request tracker (report bugs here)
177              
178             L
179              
180             =item * AnnoCPAN: Annotated CPAN documentation
181              
182             L
183              
184             =item * CPAN Ratings
185              
186             L
187              
188             =item * Search CPAN
189              
190             L
191              
192             =back
193              
194             =head1 LICENSE AND COPYRIGHT
195              
196             Copyright 2018 Michael Samoglyadov.
197              
198             This program is free software; you can redistribute it and/or modify it under
199             the terms of either: the GNU General Public License as published by the Free
200             Software Foundation; or the Artistic License.
201              
202             See L for more information.
203              
204             =cut
205              
206             1; # End of JSON::Patch