File Coverage

blib/lib/App/Getconf/Node.pm
Criterion Covered Total %
statement 98 122 80.3
branch 66 82 80.4
condition 31 43 72.0
subroutine 13 17 76.4
pod 13 13 100.0
total 221 277 79.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4              
5             App::Getconf::Node - TODO: fill me
6              
7             =head1 SYNOPSIS
8              
9             TODO: fill me
10              
11             =cut
12              
13             package App::Getconf::Node;
14              
15             #-----------------------------------------------------------------------------
16              
17 10     10   63 use warnings;
  10         23  
  10         319  
18 10     10   52 use strict;
  10         20  
  10         357  
19              
20 10     10   53 use Carp;
  10         26  
  10         18894  
21              
22             our @CARP_NOT = qw{App::Getconf};
23              
24             #-----------------------------------------------------------------------------
25              
26             =head1 METHODS
27              
28             Following methods are available:
29              
30             =over
31              
32             =cut
33              
34             #-----------------------------------------------------------------------------
35              
36             =item C
37              
38             TODO: fill me
39              
40             Supported options:
41              
42             =over
43              
44             =item C
45              
46             =item C
47              
48             =item C
49              
50             C, C, C
51              
52             =item C
53              
54             =item C
55              
56             =item C
57              
58             =item C
59              
60             =back
61              
62             =cut
63              
64             sub new {
65 365     365 1 1253 my ($class, %opts) = @_;
66              
67 365   100     2999 my $self = bless {
      100        
68             type => $opts{type} || "string",
69             check => $opts{check},
70             storage => $opts{storage} || "scalar",
71             help => $opts{help},
72             #value => $opts{value}, # NOTE: existence of the key will be used
73             #default => $opts{default},
74             alias => $opts{alias},
75             }, $class;
76              
77             # not a supported type
78 365 50       722 if (not grep { $_ eq $self->{type} } qw{flag bool int float string}) {
  1825         5228  
79 0         0 croak "Not a supported type: $self->{type}";
80             }
81              
82 365 100       949 if ($self->{type} eq 'flag') {
83 67 50       219 if ($self->{storage} ne 'scalar') {
84 0         0 croak "Unsupported combination: flag with non-scalar storage";
85             }
86 67         156 $self->{value} = 0;
87             }
88              
89 365 50 66     1066 if ($self->{type} eq 'bool' && $self->{storage} ne 'scalar') {
90 0         0 croak "Unsupported combination: bool with non-scalar storage";
91             }
92              
93 365 100       1303 if ($self->{storage} eq 'array') {
    100          
94 15         33 $self->{value} = [];
95             } elsif ($self->{storage} eq 'hash') {
96 15         33 $self->{value} = {};
97             }
98              
99             # not a supported check type
100 365         646 my $check_type = ref $self->{check};
101 365 50 66     1306 if ($self->{check} &&
      66        
102             !($check_type eq 'CODE' ||
103             $check_type =~ /(^|::)Regexp$/ ||
104             $check_type eq 'ARRAY')) {
105 0         0 croak "Unknown check type: $check_type";
106             }
107              
108 365 100       864 $self->set($opts{value}) if exists $opts{value};
109 365 100       1016 $self->{default} = $self->verify($opts{default}) if exists $opts{default};
110              
111 365         2654 return $self;
112             }
113              
114             #-----------------------------------------------------------------------------
115              
116             =item C
117              
118             Method tells whether this option I an argument passed in command line
119             (but it may be still possible not to pass an argument to this option; see
120             C method).
121              
122             =cut
123              
124             sub uses_arg {
125 98     98 1 136 my ($self) = @_;
126              
127 98   66     1051 return $self->{type} eq "int" || $self->{type} eq "float" ||
128             $self->{type} eq "string" || $self->{type} eq "bool";
129             }
130              
131             =item C
132              
133             Method tells whether this option I an argument in command line.
134              
135             =cut
136              
137             sub requires_arg {
138 47     47 1 75 my ($self) = @_;
139              
140 47   100     499 return !($self->{type} eq 'flag' || $self->{type} eq 'bool' ||
141             exists $self->{default});
142             }
143              
144             =item C
145              
146             Retrieve help message for this option.
147              
148             =cut
149              
150             sub help {
151 321     321 1 488 my ($self) = @_;
152              
153 321         1507 return $self->{help};
154             }
155              
156             #-----------------------------------------------------------------------------
157              
158             =item C
159              
160             If the node is an alias, method returns what option it points to.
161              
162             If the node is autonomous, method returns C.
163              
164             =cut
165              
166             sub alias {
167 414     414 1 894 my ($self) = @_;
168              
169 414         1393 return $self->{alias};
170             }
171              
172             #-----------------------------------------------------------------------------
173              
174             =item C
175              
176             =item C
177              
178             Set value of this option. The second form is for options with I storage.
179              
180             =cut
181              
182             sub set {
183 112     112 1 219 my ($self, $key, $value) = @_;
184              
185 112 100       300 if (@_ == 2) {
186             # second argument is actually the value and there's no key
187 91         124 $value = $key;
188 91         228 $key = undef;
189             }
190              
191 112 100 100     414 if (@_ == 1 && $self->requires_arg) {
192 1         293 croak "Option requires an argument, but none was provided";
193             }
194              
195 111 50 66     477 if (@_ > 1 && !$self->uses_arg) {
196 0         0 croak "Option doesn't use an argument, but one was provided";
197             }
198              
199 111 100       341 if ($self->storage eq 'hash') {
200             # TODO: how about an array as the value?
201 14 100       59 if (defined $key) {
    100          
202 7         28 $self->{value}{$key} = $self->verify($value);
203             } elsif ($value =~ /^(.*?)=(.*)$/) {
204 6         18 $self->{value}{$1} = $self->verify($2);
205             } else {
206 1         388 croak "For hash option key=value pair must be provided";
207             }
208 13         63 return;
209             }
210              
211 97 50       247 if (defined $key) {
212 0         0 croak "Can't store key=value pair in @{[ $self->storage ]} storage";
  0         0  
213             }
214              
215 97 100       198 if ($self->storage eq 'array') {
216 9 100       24 if (ref $value eq 'ARRAY') {
217 1         1 push @{ $self->{value} }, @$value;
  1         3  
218             } else {
219 8         11 push @{ $self->{value} }, $value;
  8         22  
220             }
221 9         104 return;
222             }
223              
224 88 50       238 if (ref $value) {
225 0         0 croak "Can't store @{[ ref $value ]} in scalar storage";
  0         0  
226             }
227              
228 88 100 100     193 if ($self->type eq 'flag') {
    100 66        
    100          
229             # for flags, just increment the counter
230 10         36 $self->{value} += 1;
231             } elsif ($self->type eq 'bool' && @_ == 1) {
232             # if Boolean option with no argument is being set, it means the option
233             # value is TRUE
234 1         6 $self->{value} = 1;
235             } elsif (@_ == 1 && exists $self->{default}) {
236 2         8 $self->{value} = $self->{default};
237             } else {
238 75         179 $self->{value} = $self->verify($value);
239             }
240             }
241              
242             =item C
243              
244             Retrieve value of this option.
245              
246             =cut
247              
248             sub get {
249 16     16 1 23 my ($self) = @_;
250              
251 16         97 return $self->{value};
252             }
253              
254             =item C
255              
256             Tell whether the value was set somehow (with command line, config or with
257             initial value).
258              
259             =cut
260              
261             sub has_value {
262 0     0 1 0 my ($self) = @_;
263              
264 0         0 return exists $self->{value};
265             }
266              
267             =item C
268              
269             Tell whether the value was set somehow (with command line, config or with
270             initial value).
271              
272             =cut
273              
274             sub has_default {
275 0     0 1 0 my ($self) = @_;
276              
277 0         0 return exists $self->{default};
278             }
279              
280             =item C
281              
282             Determine what data type this option stores.
283              
284             See C for supported types.
285              
286             =cut
287              
288             sub type {
289 167     167 1 211 my ($self) = @_;
290              
291 167         888 return $self->{type};
292             }
293              
294             =item C
295              
296             Determine what kind of storage this option uses.
297              
298             Returned value: C, C or C.
299              
300             =cut
301              
302             sub storage {
303 213     213 1 296 my ($self) = @_;
304              
305 213         714 return $self->{storage};
306             }
307              
308             =item C
309              
310             If the option is enum (check was specified as an array of values), arrayref of
311             the values is returned. Otherwise, method returns C.
312              
313             =cut
314              
315             sub enum {
316 0     0 1 0 my ($self) = @_;
317              
318 0 0       0 return ref $self->{check} eq 'ARRAY' ? $self->{check} : undef;
319             }
320              
321             #-----------------------------------------------------------------------------
322              
323             =item C
324              
325             Check correctness of C<$value> for this option.
326              
327             Method will C if the value is incorrect.
328              
329             For convenience, C<$value> is returned. This way following is possible:
330              
331             my $foo = $node->verify($value);
332              
333             =cut
334              
335             sub verify {
336 110     110 1 274 my ($self, $value) = @_;
337              
338 110         327 my $type = $self->{type};
339 110         211 my $check = $self->{check};
340              
341 110         155 eval {
342             # convert warnings to errors
343 110     0   742 local $SIG{__WARN__} = sub { die $@ };
  0         0  
344              
345 110 100       327 if ($type eq 'string') {
    100          
    100          
    50          
346 94 100       592 $value = defined $value ? "$value" : undef;
347             } elsif ($type eq 'int') {
348             # TODO: better check
349 9         56 $value = int(0 + $value);
350             } elsif ($type eq 'float') {
351             # TODO: better check
352 4         28 $value = 0.0 + $value;
353             } elsif ($type eq 'bool') {
354 3 100 66     85 if (defined $value && $value =~ /^(1|true|yes)$/i) {
    50 33        
355 1         5 $value = 1;
356             } elsif (defined $value && $value =~ /^(0|false|no)$/i) {
357 2         17 $value = 0;
358             } else {
359 0         0 die "can't convert $value to bool";
360             }
361             }
362             # XXX: flags are not supposed to be processed by this function
363             };
364              
365             # on any warning, assume the data is not in correct format
366 110 50       273 if ($@) {
367 0         0 croak "Invalid value \"$value\" for type $type";
368             }
369 110 50       279 if ($type eq 'flag') {
370 0         0 croak "Flag can't have a value";
371             }
372              
373             # check for correctness
374              
375 110 100       318 if (not $self->{check}) {
    100          
    100          
    50          
376             # no check, so everything is OK
377              
378 98         476 return $value;
379             } elsif (ref $self->{check} eq 'CODE') {
380             # check based on function
381              
382 4 100       5 if (do { local $_ = $value; $self->{check}->($_) }) {
  4         8  
  4         14  
383 2         27 return $value;
384             } else {
385 2         454 croak "Value \"$value\" ($type) was not accepted by check";
386             }
387             } elsif (ref($self->{check}) =~ /(^|::)Regexp$/) {
388             # check based on regexp
389              
390 4         7 my $re = $self->{check};
391 4 100       30 if ($value =~ /$re/) {
392 2         14 return $value;
393             } else {
394 2         423 croak "Value \"$value\" ($type) was not accepted by regexp check";
395             }
396             } elsif (ref $self->{check} eq 'ARRAY') {
397 4 50 33     13 if (!defined $value && grep { !defined } @{ $self->{check} }) {
  0         0  
  0         0  
398 0         0 return $value;
399             }
400 4 100 66     14 if (defined $value && grep { $_ eq $value } @{ $self->{check} }) {
  12         35  
  4         12  
401 2         14 return $value;
402             }
403 2 50       9 $value = defined $value ? "\"$value\"" : "";
404 2         516 croak "Invalid value $value for enum";
405             }
406              
407             # XXX: never reached
408 0           die "Unknown check type: @{[ ref $self->{check} ]}";
  0            
409             }
410              
411             #-----------------------------------------------------------------------------
412              
413             =back
414              
415             =cut
416              
417             #-----------------------------------------------------------------------------
418              
419             =head1 AUTHOR
420              
421             Stanislaw Klekot, C<< >>
422              
423             =head1 LICENSE AND COPYRIGHT
424              
425             Copyright 2012 Stanislaw Klekot.
426              
427             This program is free software; you can redistribute it and/or modify it
428             under the terms of either: the GNU General Public License as published
429             by the Free Software Foundation; or the Artistic License.
430              
431             See http://dev.perl.org/licenses/ for more information.
432              
433             =head1 SEE ALSO
434              
435             L
436              
437             =cut
438              
439             #-----------------------------------------------------------------------------
440             1;
441             # vim:ft=perl