File Coverage

blib/lib/OPP/Proc.pm
Criterion Covered Total %
statement 18 129 13.9
branch 0 62 0.0
condition 0 8 0.0
subroutine 6 15 40.0
pod 0 9 0.0
total 24 223 10.7


line stmt bran cond sub pod time code
1             #
2             # $Id: Proc.pm,v cfbea05b0bc4 2025/01/28 15:06:19 gomor $
3             #
4             package OPP::Proc;
5 1     1   770 use strict;
  1         2  
  1         41  
6 1     1   5 use warnings;
  1         2  
  1         132  
7              
8             our $VERSION = '1.00';
9              
10 1     1   7 use base qw(OPP);
  1         2  
  1         166  
11              
12             our @AS = qw(
13             idx
14             options
15             nested
16             state
17             output
18             );
19             __PACKAGE__->cgBuildIndices;
20             __PACKAGE__->cgBuildAccessorsScalar(\@AS);
21              
22 1     1   7 use Carp;
  1         2  
  1         78  
23 1     1   8 use Data::Dumper;
  1         2  
  1         44  
24 1     1   6 use Storable;
  1         2  
  1         2285  
25              
26             #
27             # Always return values as ARRAY, undef when no value found:
28             #
29             sub value {
30 0     0 0   my $self = shift;
31 0           my ($flat, $field) = @_;
32              
33 0 0         croak("value: need flat argument") unless defined($flat);
34 0 0         croak("value: need field argument") unless defined($field);
35              
36 0           my @value = ();
37              
38             #delete $flat->{data};
39             #print "$field: ".Data::Dumper::Dumper($flat)."\n";
40              
41             # Handle nested fields:
42 0 0         if (my $split = $self->is_nested($field)) {
43             #print "*** is_nested ".$split->[0]." $field\n";
44             #print "*** is_nested ".$split->[1]." $field\n";
45 0           my $root = $split->[0];
46 0           my $leaf = $split->[1];
47 0 0         if (defined($leaf)) {
48 0           for (@{$flat->{$root}}) {
  0            
49 0 0         if (defined($_->{$leaf})) {
50 0 0         my $ary = ref($_->{$leaf}) ? $_->{$leaf} : [ $_->{$leaf} ];
51 0           push @value, @$ary;
52             }
53             }
54             }
55             }
56             # Handle standard fields:
57             else {
58 0 0         if (defined($flat->{$field})) {
59 0 0         my $ary = ref($flat->{$field}) eq 'ARRAY' ? $flat->{$field} : [ $flat->{$field} ];
60 0           push @value, @$ary;
61             }
62             }
63              
64             #print "value: ".Data::Dumper::Dumper(\@value)."\n";
65              
66 0 0         return @value ? \@value : undef;
67             }
68              
69             sub fields {
70 0     0 0   my $self = shift;
71 0           my ($flat) = @_;
72              
73 0 0         croak("fields: need flat argument") unless defined($flat);
74              
75 0           my @fields = ();
76              
77 0           my $flat_fields = [ map { $_ } keys %$flat ];
  0            
78 0           for my $field (@$flat_fields) {
79 0 0         if ($self->is_nested($field)) {
80 0 0         my $ary = ref($flat->{$field}) eq 'ARRAY' ? $flat->{$field} : [ $flat->{$field} ];
81 0           for (@$ary) {
82 0           for my $leaf (keys %$_) {
83 0           push @fields, "$field.$leaf";
84             }
85             }
86             }
87             else {
88 0           push @fields, $field;
89             }
90             }
91              
92 0           return \@fields;
93             }
94              
95             sub values {
96 0     0 0   my $self = shift;
97 0           my ($flat) = @_;
98              
99 0 0         croak("values: need flat argument") unless defined($flat);
100              
101 0           my @values = ();
102 0           my $fields = $self->fields($flat);
103              
104 0           for (@$fields) {
105 0           push @values, $flat->{$_};
106             }
107              
108 0           return \@values;
109             }
110              
111             sub dumper {
112 0     0 0   my $self = shift;
113 0           my ($arg) = @_;
114              
115 0           return Data::Dumper::Dumper($arg)."\n";
116             }
117              
118             #
119             # $self->delete($flat, "domain");
120             # $self->delete($flat, "app.http.component");
121             # $self->delete($flat, "app.http.component.product");
122             #
123             sub delete {
124 0     0 0   my $self = shift;
125 0           my ($flat, $field) = @_;
126              
127 0 0         croak("delete: need flat argument") unless defined($flat);
128 0 0         croak("delete: need field argument") unless defined($field);
129              
130             # Handle nested fields:
131 0 0         if (my $split = $self->is_nested($field)) {
    0          
132 0           my $root = $split->[0];
133 0           my $leaf = $split->[1];
134             # Delete at the leaf level:
135 0 0 0       if ($root !~ m{^_} && defined($leaf)) {
    0          
136 0           my @keep = ();
137 0           for my $this (@{$flat->{$root}}) {
  0            
138 0           delete $this->{$leaf};
139 0 0         push @keep, $this if keys %$this; # Keep the final object only when not empty
140             }
141             # Keep the final array only when not empty
142 0 0         if (@keep > 0) {
143 0           $flat->{$root} = \@keep;
144             }
145             # And when empty, completly remove the root field:
146             else {
147 0           delete $flat->{$root};
148             }
149             }
150             # Or the complete root field when asked for:
151             elsif ($root !~ m{^_}) {
152 0           delete $flat->{$root};
153             }
154             }
155             # Handle standard fields:
156             elsif ($field !~ m{^_}) {
157 0           delete $flat->{$field};
158             }
159              
160 0           return $flat;
161             }
162              
163             #
164             # $self->set($flat, "domain", "example.com");
165             # $self->set($flat, "app.http.component.product", "HTTP Server");
166             #
167             sub set {
168 0     0 0   my $self = shift;
169 0           my ($flat, $field, $value, $asarray) = @_;
170              
171 0 0         croak("set: need flat argument") unless defined($flat);
172 0 0         croak("set: need field argument") unless defined($field);
173 0 0         croak("set: need value argument") unless defined($value);
174              
175             # Handle nested fields:
176 0 0         if (my $split = $self->is_nested($field)) {
177 0           my $root = $split->[0];
178 0           my $leaf = $split->[1];
179             # Set at the leaf level:
180 0 0         if (defined($leaf)) {
181 0           $flat->{$root} = [ { $leaf => $value } ];
182             }
183             }
184             # Handle standard fields:
185             else {
186 0 0         if ($asarray) {
187 0   0       $flat->{$field} ||= [];
188             $flat->{$field} = ref($flat->{$field}) eq 'ARRAY'
189 0 0         ? $flat->{$field} : [ $flat->{$field} ];
190 0           push @{$flat->{$field}}, $value;
  0            
191             #print STDERR Data::Dumper::Dumper($flat->{$field})."\n";
192 0           my %h = map { $_ => 1 } @{$flat->{$field}};
  0            
  0            
193 0           $flat->{$field} = [ sort { $a cmp $b } keys %h ]; # Make uniq
  0            
194             }
195             else {
196 0           $flat->{$field} = $value;
197             }
198             }
199              
200 0           return $flat;
201             }
202              
203             #
204             # Clone given doc so we can duplicate it and modify on a new one:
205             #
206             sub clone {
207 0     0 0   my $self = shift;
208 0           my ($doc) = @_;
209              
210 0 0         croak("clone: need doc argument") unless defined($doc);
211              
212 0           return Storable::dclone($doc);
213             }
214              
215             #
216             # Will return $arg parsed as usable arguments and also original $arg value:
217             #
218             sub parse {
219 0     0 0   my $self = shift;
220 0           my ($args) = @_;
221              
222 0           my @a = Text::ParseWords::quotewords('\s+', 0, $args);
223              
224             # Also keep original value, for use with placeholders, for instance:
225 0           my $parsed = {
226             args => $args,
227             };
228 0           my $idx = 0;
229 0           for (@a) {
230 0           my ($k, $v) = split(/\s*[=:]\s*/, $_, 2);
231 0 0 0       if (defined($k) && defined($v)) {
    0          
232 0           $parsed->{$k} = [ sort { $a cmp $b } split(/\s*,\s*/, $v) ];
  0            
233             }
234             elsif (defined($k)) {
235 0           $parsed->{$idx++} = $k;
236             }
237             }
238              
239 0           return $parsed;
240             }
241              
242             sub placeholder {
243 0     0 0   my $self = shift;
244 0           my ($query, $flat) = @_;
245              
246             # Copy original to not modify it:
247 0           my $copy = $query;
248 0           my (@holders) = $query =~ m{[\w\.]+\s*:\s*\$([\w\.]+)}g;
249              
250             # Update search clause with placeholder values
251 0           my %searches = ();
252 0           for my $holder (@holders) {
253 0           my $values = $self->value($flat, $holder);
254 0           for my $value (@$values) {
255 0           while ($copy =~ s{(\S+)\s*:\s*\$$holder}{$1:$value}) { }
256             }
257             }
258 0           $searches{$copy}++; # Make them unique
259              
260 0           return [ keys %searches ];
261             }
262              
263             1;
264              
265             __END__