File Coverage

blib/lib/SHARYANTO/Data/Util.pm
Criterion Covered Total %
statement 26 46 56.5
branch 14 30 46.6
condition n/a
subroutine 6 7 85.7
pod 2 2 100.0
total 48 85 56.4


line stmt bran cond sub pod time code
1             package SHARYANTO::Data::Util;
2              
3 1     1   27336 use 5.010001;
  1         5  
  1         40  
4 1     1   6 use strict;
  1         2  
  1         34  
5 1     1   5 use warnings;
  1         1  
  1         570  
6             #use experimental 'smartmatch';
7              
8             require Exporter;
9             our @ISA = qw(Exporter);
10             our @EXPORT_OK = qw(clone_circular_refs has_circular_ref);
11              
12             our $VERSION = '0.57'; # VERSION
13              
14             our %SPEC;
15              
16             $SPEC{clone_circular_refs} = {
17             v => 1.1,
18             summary => 'Remove circular references by deep-copying them',
19             description => <<'_',
20              
21             For example, this data:
22              
23             $x = [1];
24             $data = [$x, 2, $x];
25              
26             contains circular references by referring to `$x` twice. After
27             `clone_circular_refs`, data will become:
28              
29             $data = [$x, 2, [1]];
30              
31             that is, the subsequent circular references will be deep-copied. This makes it
32             safe to transport to JSON, for example.
33              
34             Sometimes it doesn't work, for example:
35              
36             $data = [1];
37             push @$data, $data;
38              
39             Cloning will still create circular references.
40              
41             This function modifies the data structure in-place, and return true for success
42             and false upon failure.
43              
44             _
45             args_as => 'array',
46             args => {
47             data => {
48             schema => "any",
49             pos => 0,
50             req => 1,
51             },
52             },
53             result_naked => 1,
54             };
55             sub clone_circular_refs {
56 1     1 1 2084 require Data::Structure::Util;
57 0         0 require Data::Clone;
58              
59 0         0 my ($data) = @_;
60 0         0 my %refs;
61             my $doit;
62             $doit = sub {
63 0     0   0 my $x = shift;
64 0         0 my $r = ref($x);
65 0 0       0 return if !$r;
66 0 0       0 if ($r eq 'ARRAY') {
    0          
67 0         0 for (@$x) {
68 0 0       0 next unless ref($_);
69 0 0       0 if ($refs{"$_"}++) {
70 0         0 $_ = Data::Clone::clone($_);
71             } else {
72 0         0 $doit->($_);
73             }
74             }
75             } elsif ($r eq 'HASH') {
76 0         0 for (keys %$x) {
77 0 0       0 next unless ref($x->{$_});
78 0 0       0 if ($refs{"$x->{$_}"}++) {
79 0         0 $x->{$_} = Data::Clone::clone($x->{$_});
80             } else {
81 0         0 $doit->($_);
82             }
83             }
84             }
85 0         0 };
86 0         0 $doit->($data);
87 0         0 !Data::Structure::Util::has_circular_ref($data);
88             }
89              
90             $SPEC{has_circular_ref} = {
91             v => 1.1,
92             summary => 'Check whether data item contains circular references',
93             description => <<'_',
94              
95             Does not deal with weak references.
96              
97             _
98             args_as => 'array',
99             args => {
100             data => {
101             schema => "any",
102             pos => 0,
103             req => 1,
104             },
105             },
106             result_naked => 1,
107             };
108             sub has_circular_ref {
109 7     7 1 995 my ($data) = @_;
110 7         10 my %refs;
111             my $check;
112             $check = sub {
113 14     14   16 my $x = shift;
114 14         16 my $r = ref($x);
115 14 100       31 return 0 if !$r;
116 12 100       48 return 1 if $refs{"$x"}++;
117 9 100       17 if ($r eq 'ARRAY') {
    50          
118 8         13 for (@$x) {
119 7 100       15 next unless ref($_);
120 5 100       18 return 1 if $check->($_);
121             }
122             } elsif ($r eq 'HASH') {
123 1         3 for (values %$x) {
124 2 50       4 next unless ref($_);
125 2 100       5 return 1 if $check->($_);
126             }
127             }
128 6         29 0;
129 7         28 };
130 7         16 $check->($data);
131             }
132              
133             1;
134             # ABSTRACT: Data utilities
135              
136             __END__