File Coverage

blib/lib/Config/apiLayers.pm
Criterion Covered Total %
statement 157 235 66.8
branch 44 102 43.1
condition 24 54 44.4
subroutine 19 25 76.0
pod 7 7 100.0
total 251 423 59.3


line stmt bran cond sub pod time code
1             package Config::apiLayers;
2              
3 1     1   55206 use strict;
  1         2  
  1         31  
4 1     1   5 use warnings;
  1         2  
  1         35  
5 1     1   947 use Symbol qw{ qualify_to_ref };
  1         1182  
  1         104  
6              
7             BEGIN {
8 1     1   8 use vars qw($VERSION $LASTMOD $DEBUG $CXLCFG $CXLDATA);
  1         2  
  1         131  
9 1     1   3 $VERSION = '0.10';
10 1         2 $LASTMOD = 20151230;
11 1         2 $DEBUG = 0;
12 1         2 $CXLCFG = '_cxl_cfg';
13 1         3273 $CXLDATA = '_cxl_data';
14             }
15              
16             sub _api_factory ($;$) {
17 3     3   4 my $self = shift;
18 3         4 my $apiname = shift;
19 3   50     8 my $validator = shift || undef;
20              
21             my $f_validator = sub ($;$) {
22 1     1   2 my $self = shift;
23 1         1 my $name = shift;
24 1         2 my $validator = shift;
25 1         2 my $value = shift;
26 1 50       4 if (!defined $validator) {
    50          
    0          
27 0         0 return $value;
28             }
29             elsif (ref $validator eq "CODE") {
30 1         4 return $validator->($self,$name,$value);
31             }
32             elsif (!ref $validator) {
33 0 0       0 return $value if $value =~ /$validator/;
34 0         0 return undef;
35             }
36 3         18 };
37              
38             # The method for setting/getting the attribute's value
39 3 50       8 warn "Creating Getter/Setter for $apiname called.\n" if $self->{'DEBUG'};
40            
41             return sub {
42 6     6   397 my $name = $apiname;
43 6 50       15 warn "Getter/Setter for $name called.\n" if $self->{'DEBUG'};
44 6         9 my $self = shift;
45 6   100     20 my $value = shift || undef;
46              
47 6 100       12 if (defined $value) {
48              
49 1 50       4 if (my $valid = $f_validator->($self,$name,$validator,$value)) {
50             #return $self->config({ 'data' => { $name => $valid } });
51 1         10 $self->config({ 'data' => { $name => $valid } });
52 1         10 return 1;
53             } else {
54 0         0 return undef;
55             }
56              
57             } else {
58              
59             # In the future, we'll do more here
60             my $paramedic = sub {
61 5     5   7 my $value = shift;
62 5 100       13 if (ref $value eq "CODE") {
63 1         4 return $value->($self);
64             } else {
65 4         30 return $value;
66             }
67 5         22 };
68              
69 5         6 foreach my $layer (reverse @{$self->{$CXLDATA}}) {
  5         14  
70 7 100       23 if (exists $layer->{$name}) {
71 5         13 return $paramedic->($layer->{$name});
72             }
73             }
74              
75 0         0 return undef;
76              
77             }
78 3         17 };
79             }
80              
81             sub _api_define ($;$) {
82 3     3   4 my $self = shift;
83 3         5 my $apiname = shift;
84 3   50     7 my $function = shift || return undef;
85 3         4 my $ref = *{ Symbol::qualify_to_ref( $apiname ) };
  3         9  
86 3         63 *{ $ref } = $function;
  3         7  
87              
88 3         14 return 1;
89             }
90              
91             sub _api_undefine ($) {
92 0     0   0 my $self = shift;
93 0         0 my $apiname = shift;
94 0         0 my $ref = *{ Symbol::qualify_to_ref( $apiname ) };
  0         0  
95 0         0 *{ $ref } = undef;
  0         0  
96             }
97              
98              
99             # @attributes = [ a, b, c ];
100             # @attributes = [ { a => v1, b => v2, c => v3 } ]
101             # @attributes = [ { name => 'length', validator => \&func, getoptlong => 'length|l:i', description => 'long description' } ]
102             # @autoproto = 1|0 ; default is 1
103             sub new (;$) {
104 1     1 1 31 my $pkg = shift;
105 1         2 my $args = shift;
106 1   33     7 my $class = ref($pkg) || $pkg;
107 1         4 my $self = bless {},$class;
108              
109 1   50     4 my $autoproto = $args->{'autoproto'} || 1;
110              
111             my $attr_add = sub {
112 3     3   5 my $attr_name = shift;
113 3   50     9 my $validator = shift || undef;
114 3   100     11 my $getoptlong = shift || undef;
115 3   100     9 my $description = shift || undef;
116 3         4 push (@{$self->{$CXLCFG}->{'attributes'}}, $attr_name);
  3         17  
117 3 50       13 $self->{$CXLCFG}->{'validators'}->{$attr_name} = $validator if defined $validator;
118 3 100       8 $self->{$CXLCFG}->{'getoptlong'}->{$attr_name} = $getoptlong if defined $getoptlong;
119 3 100       11 $self->{$CXLCFG}->{'description'}->{$attr_name} = $description if defined $description;
120 3         9 my $attr_func = $self->_api_factory($attr_name,$validator);
121 3 50       10 warn "ERROR in creating function for $attr_name\n" if !defined $attr_func;
122 3         7 $self->{$CXLCFG}->{'api'}->{$attr_name} = $attr_func;
123 3 50       8 if ($autoproto == 1) {
124 3         7 $self->_api_define($attr_name,$attr_func);
125             }
126 1         5 };
127             my $attr_add_hash = sub {
128 3     3   4 my $attr_hash = shift;
129 3 50       8 if (exists $attr_hash->{'name'}) {
130 3         4 my $name = $attr_hash->{'name'};
131 3   50     8 my $validator = $attr_hash->{'validator'} || undef;
132 3   100     11 my $getoptlong = $attr_hash->{'getoptlong'} || undef;
133 3   100     10 my $description = $attr_hash->{'description'} || undef;
134 3         7 $attr_add->($name, $validator, $getoptlong, $description);
135             } else {
136 0         0 foreach my $attr (keys %{$attr_hash}) {
  0         0  
137 0         0 $attr_add->($attr, $attr_hash->{$attr});
138             }
139             }
140 1         4 };
141 1 50 33     9 if ((exists $args->{'attributes'}) && (ref $args->{'attributes'} eq "ARRAY")) {
    0 0        
142 1         2 foreach my $attr (@{$args->{'attributes'}}) {
  1         3  
143 3 50       8 if (ref $attr eq "HASH") {
144 3         8 $attr_add_hash->($attr);
145             } else {
146 0         0 $attr_add->($attr);
147             }
148             }
149             } elsif ((exists $args->{'attributes'}) && (ref $args->{'attributes'} eq "HASH")) {
150 0         0 foreach my $attr_name (keys %{$args->{'attributes'}}) {
  0         0  
151 0         0 $attr_add_hash->($attr_name);
152             }
153             }
154              
155 1         12 return $self;
156             }
157              
158              
159             # Set or retrieve a configuration layer, without validation.
160             # Set with @index and @data, or jusr @data for the last existing index
161             # Retrieve with only @index, or without index retrieve the last existing index
162             # @data
163             # @index
164             sub config ($) {
165 2     2 1 682 my $self = shift;
166 2   50     6 my $args = shift || {};
167 2 100       17 my $lastLayer = ref $self->{$CXLDATA} eq "ARRAY" ? (scalar @{$self->{$CXLDATA}} - 1) : 0;
  1         3  
168 2 50       5 my $layer_idx = exists $args->{'index'} ? $args->{'index'} : $lastLayer;
169 2 50       32 if (! exists $args->{'data'}) {
170 0 0       0 if (defined $self->{$CXLDATA}->[$layer_idx]) {
171 0         0 return $self->{$CXLDATA}->[$layer_idx];
172             } else {
173 0         0 return undef;
174             }
175             }
176 2         4 my $config = $args->{'data'};
177 2         4 my $attrs = $self->{$CXLCFG}->{'attributes'};
178 2         3 foreach my $key (keys %{$config}) {
  2         8  
179 4 50       5 next unless grep {/^$key$/} @{$attrs};
  12         77  
  4         6  
180 4         14 $self->{$CXLDATA}->[$layer_idx]->{$key} = $config->{$key};
181             }
182             }
183              
184             # Import the data, performing validation as available
185             # @data - the data to import
186             sub importdata ($) {
187 1     1 1 11 my $self = shift;
188 1         1 my $args = shift;
189 1         3 my $attrs = $self->{$CXLCFG}->{'attributes'};
190 1         2 my $errors = 0;
191 1 50       4 if (exists $args->{'data'}) {
192 1         2 foreach my $key (keys %{$args->{'data'}}) {
  1         4  
193 1 50       2 next unless grep {/^$key$/} @{$attrs};
  3         21  
  1         2  
194 1 50       5 unless ($self->apicall($key,$args->{'data'}->{$key})) {
195 0         0 $errors++;
196             }
197             }
198             }
199 1 50       4 return 0 if $errors >= 1;
200 1         3 return 1;
201             }
202              
203             # Export the data
204             # @cfg - getoptlong|descriptions
205             # @data - undef|layerNumber|[startingLayer,endingLayer]
206             sub exportdata ($) {
207 2     2 1 797 my $self = shift;
208 2         4 my $args = shift;
209 2         4 my $attrs = $self->{$CXLCFG}->{'attributes'};
210 2 100 66     25 if ((exists $args->{'cfg'}) && ($args->{'cfg'} eq "getoptlong")) {
    50 33        
    0          
211 1         2 my $getoptlong = [];
212 1         11 foreach my $attr_name (@{$attrs}) {
  1         3  
213 3 100       12 next unless defined $self->{$CXLCFG}->{'getoptlong'}->{$attr_name};
214 2         2 push (@{$getoptlong}, $self->{$CXLCFG}->{'getoptlong'}->{$attr_name});
  2         6  
215             }
216 1         3 return $getoptlong;
217             } elsif ((exists $args->{'cfg'}) && ($args->{'cfg'} eq "descriptions")) {
218 1         2 my $description = [];
219 1         2 foreach my $attr_name (@{$attrs}) {
  1         3  
220 3 100       11 next unless defined $self->{$CXLCFG}->{'description'}->{$attr_name};
221 2         3 push (@{$description}, $attr_name);
  2         4  
222 2         3 push (@{$description}, $self->{$CXLCFG}->{'description'}->{$attr_name});
  2         5  
223             }
224 1         4 return $description;
225             } elsif (exists $args->{'data'}) {
226 0         0 my $firstLayer = 0;
227 0         0 my $lastLayer = (scalar @{$self->{$CXLDATA}} - 1);
  0         0  
228 0 0 0     0 if ((!ref $args->{'data'}) && ($args->{'data'} >= $firstLayer) && ($args->{'data'} <= $lastLayer)) {
    0 0        
229 0         0 return $self->{$CXLDATA}->[$args->{'data'}];
230             } elsif (ref $args->{'data'} eq "ARRAY") {
231 0   0     0 $firstLayer = shift @{$args->{'data'}} || 0;
232 0   0     0 $lastLayer = pop @{$args->{'data'}} || $lastLayer;
233             }
234 0         0 my $export = {};
235 0         0 foreach my $key (@{$attrs}) {
  0         0  
236 0         0 for ($firstLayer..$lastLayer) {
237 0         0 my $layer_idx = $_;
238 0 0       0 next unless exists $self->{$CXLDATA}->[$layer_idx]->{$key};
239 0         0 $export->{$key} = $self->{$CXLDATA}->[$layer_idx]->{$key};
240             }
241             }
242 0 0       0 return $export if keys %{$export};
  0         0  
243 0         0 return undef;
244             }
245             }
246              
247              
248             # Add layers up to the given index, with or without data.
249             # Add a layer with @index and @data, or just @index, or add one more layer without @index
250             # Add more than one layer by providing the appropriate @index layer number.
251             # The @data is only set into the last layer.
252             # @index
253             # @data
254             sub add_layer($) {
255 1     1 1 376 my $self = shift;
256 1   50     7 my $args = shift || {};
257 1 50       5 if (ref $self->{$CXLDATA} ne "ARRAY") {
258 0         0 $self->{$CXLDATA} = [];
259             }
260 1         1 my $nextLayer = scalar @{$self->{$CXLDATA}};
  1         3  
261 1 50       4 my $layerNumber = exists $args->{'index'} ? $args->{'index'} : $nextLayer;
262              
263 1         4 for ($nextLayer..$layerNumber) {
264 1         2 push( @{$self->{$CXLDATA}}, {} );
  1         3  
265             }
266              
267 1 50       10 if (exists $args->{'data'}) {
268 0         0 $self->config({ data => $args->{'data'} , index => $layerNumber });
269             }
270              
271 1         2 return (scalar @{$self->{$CXLDATA}} - 1);
  1         4  
272             }
273              
274             sub apican(;$) {
275 3     3 1 4 my $self = shift;
276 3   50     8 my $attr_name = shift || undef;
277 3 50       8 if (defined $attr_name) {
278 3 50       13 return $self->{$CXLCFG}->{'api'}->{$attr_name} if exists $self->{$CXLCFG}->{'api'}->{$attr_name};
279 0         0 return undef;
280             } else {
281 0 0       0 return wantarray ? @{$self->{$CXLCFG}->{'attributes'}} : $self->{$CXLCFG}->{'attributes'};
  0         0  
282             }
283             }
284              
285             sub apicall(;$){
286 3     3 1 12 my $self = shift;
287 3   50     8 my $attr_name = shift || return undef;
288 3 50       8 if (defined $attr_name) {
289 3         7 my $subref = $self->apican($attr_name);
290 3         9 unshift(@_,$self);
291             #goto &$subref if defined $subref;
292 3 50       11 $subref->(@_) if defined $subref;
293             }
294             }
295              
296              
297             #
298             # Non-Object helper functions
299             # To be used inside the api functions
300             #
301              
302             sub _mendPath(@) {
303 0     0     my @path = @_;
304 0           my $path;
305 0           foreach my $p (@path) {
306 0 0         next unless defined $p;
307 0 0         if ($path =~ /.+\/$/) {
308 0           chop($path);
309             }
310 0           while ($p =~ /.+\/\/$/) {
311 0           chop($p);
312             }
313 0 0         if (!defined $path) {
314 0           $path = $p;
315             } else {
316 0           $path = ($path.'/'.$p);
317             }
318             }
319 0           return $path;
320             }
321              
322             # _mendLastRootPath
323             # Given an array of items that may define one or more paths from root '/'
324             # return the last grouping of items that define one path from root
325             # ex: _mendLastRootPath(qw( /path to file /next path to dir))
326             # returns: '/next/path/to/dir'
327             # This is handy when the users input for a parameter can either be a full path
328             # from root, or a subpath of another parameter.
329             # In this case, this would be the resulting example:
330             # # when $homedir = /home
331             # # and $userhomedir = ( jsmith | /home/jsmith )
332             # my $path = mendlastrootpath( $homedir, $userhomedir);
333             # # $path eq '/home/jsmith'
334             sub _mendLastRootPath (@) {
335 0     0     my $self = shift;
336 0           my @items = @_;
337 0           my @rootitems;
338 0           foreach my $item (@items) {
339 0 0         if ($item =~ /^\//) {
340 0           @rootitems = ();
341 0           push (@rootitems,$item);
342             } else {
343 0           push (@rootitems,$item);
344             }
345             }
346 0           return $self->mendPath(@rootitems);
347             }
348              
349             sub _dirFileSplit($) {
350 0     0     my $path = shift;
351 0 0         if (-d $path) {
352 0           return ($path,undef);
353             }
354 0           my ($baseDir,$fileName) = $path =~ /^(.*\/)([^\/]*)$/;
355 0           return @{[$baseDir,$fileName]};
  0            
356             }
357              
358             sub _dirBase($) {
359 0     0     my $path = shift;
360 0           my ($baseDir,$fileName) = _DirFileSplit($path);
361 0 0         $baseDir = './' unless defined $baseDir;
362 0           return $baseDir;
363             }
364              
365             sub _fileName($) {
366 0     0     my $path = shift;
367 0           my ($baseDir,$fileName) = _DirFileSplit($path);
368 0           return $fileName;
369             }
370              
371             1;
372             __END__