File Coverage

blib/lib/Config/apiLayers.pm
Criterion Covered Total %
statement 173 234 73.9
branch 54 102 52.9
condition 35 58 60.3
subroutine 19 25 76.0
pod 7 7 100.0
total 288 426 67.6


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