File Coverage

blib/lib/Config/Model/AnyThing.pm
Criterion Covered Total %
statement 113 136 83.0
branch 46 66 69.7
condition 37 52 71.1
subroutine 24 29 82.7
pod 9 16 56.2
total 229 299 76.5


line stmt bran cond sub pod time code
1             #
2             # This file is part of Config-Model
3             #
4             # This software is Copyright (c) 2005-2022 by Dominique Dumont.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10             package Config::Model::AnyThing 2.153; # TRIAL
11              
12 59     59   35698 use Mouse;
  59         146  
  59         442  
13              
14             # FIXME: must cleanup warp mechanism to implement this
15             # use MouseX::StrictConstructor;
16              
17 59     59   57915 use Pod::POM;
  59         1305049  
  59         3290  
18 59     59   511 use Carp;
  59         208  
  59         3496  
19 59     59   498 use Log::Log4perl qw(get_logger :levels);
  59         181  
  59         600  
20 59     59   8811 use 5.10.1;
  59         256  
21              
22             my $logger = get_logger("Anything");
23             my $change_logger = get_logger("ChangeTracker");
24              
25             has element_name => ( is => 'ro', isa => 'Str' );
26             has parent => ( is => 'ro', isa => 'Config::Model::Node', weak_ref => 1 );
27              
28             has instance => (
29             is => 'ro',
30             isa => 'Config::Model::Instance',
31             weak_ref => 1,
32             handles => [qw/show_message root_path/]
33             );
34              
35             # needs_check defaults to 1 to trap undef mandatory values
36             has needs_check => ( is => 'rw', isa => 'Bool', default => 1 );
37              
38             # index_value can be written to when move method is called. But let's
39             # not advertise this feature.
40             has index_value => (
41             is => 'rw',
42             isa => 'Str',
43             trigger => sub { my $self = shift; $self->{location} = $self->_location; },
44             );
45              
46             has container => ( is => 'ro', isa => 'Ref', required => 1, weak_ref => 1 );
47              
48             has container_type => ( is => 'ro', isa => 'Str', builder => '_container_type', lazy => 1 );
49              
50             sub _container_type {
51 0     0   0 my $self = shift;
52 0         0 my $p = $self->parent;
53 0 0       0 return defined $p
54             ? $p->element_type( $self->element_name )
55             : 'node'; # root node
56              
57             }
58              
59             has root => (
60             is => 'ro',
61             isa => 'Config::Model::Node',
62             weak_ref => 1,
63             builder => '_root',
64             lazy => 1
65             );
66              
67             sub _root {
68 15     15   48 my $self = shift;
69              
70 15   66     175 return $self->parent || $self;
71             }
72              
73             has location => ( is => 'ro', isa => 'Str', builder => '_location', lazy => 1 );
74             has location_short => ( is => 'ro', isa => 'Str', builder => '_location_short', lazy => 1 );
75              
76             has backend_support_annotation => (
77             is => 'ro',
78             isa => 'Bool',
79             builder => '_backend_support_annotation',
80             lazy => 1
81             );
82              
83             sub _backend_support_annotation {
84 1     1   3 my $self = shift;
85             # this method is overridden in Config::Model::Node
86 1         13 return $self->parent->backend_support_annotation;
87             };
88              
89             sub notify_change {
90 4267     4267 1 6821 my $self = shift;
91 4267         16682 my %args = @_;
92              
93 4267 50 66     14621 return if $self->instance->initial_load and not $args{really};
94              
95 4267 100       9512 if ($change_logger->is_trace) {
96 224   100     1381 my @with = map { "'$_' -> '". ($args{$_} // '<undef>') ."'" } sort keys %args;
  1314         4407  
97 224         813 $change_logger->trace("called for ", $self->name, " from ", join( ' ', caller ), " with ", join( ' ', @with ));
98             }
99              
100             # needs_save may be overridden by caller
101 4267   100     32353 $args{needs_save} //= 1;
102 4267   100     13134 $args{path} //= $self->location;
103 4267 100 66     16801 $args{name} //= $self->element_name if $self->element_name;
104 4267 100 66     13058 $args{index} //= $self->index_value if $self->index_value;
105              
106             # better use %args instead of @_ to forward arguments. %args eliminates duplicated keys
107 4267         20270 $self->container->notify_change(%args);
108             }
109              
110             sub _location {
111 3407     3407   7030 my $self = shift;
112              
113 3407         5460 my $str = '';
114 3407 100       16460 $str .= $self->parent->location if defined $self->parent;
115              
116 3407 100       7740 $str .= ' ' if $str;
117              
118 3407         8167 $str .= $self->composite_name;
119              
120 3407         20407 return $str;
121             }
122              
123             sub _location_short {
124 51     51   109 my $self = shift;
125              
126 51         93 my $str = '';
127 51 100       386 $str .= $self->parent->location_short if defined $self->parent;
128              
129 51 100       134 $str .= ' ' if $str;
130              
131 51         164 $str .= $self->composite_name_short;
132              
133 51         333 return $str;
134             }
135              
136             #has composite_name => (is => 'ro', isa => 'Str' , builder => '_composite_name', lazy => 1);
137              
138             sub composite_name {
139 3625     3625 1 6511 my $self = shift;
140              
141 3625         8098 my $element = $self->element_name;
142 3625 100       7815 $element = '' unless defined $element;
143              
144 3625         8695 my $idx = $self->index_value;
145 3625 100       9939 return $element unless defined $idx;
146 1251 100       4984 $idx = '"' . $idx . '"' if $idx =~ /\W/;
147              
148 1251         4074 return "$element:$idx";
149             }
150              
151             sub composite_name_short {
152 51     51 1 94 my $self = shift;
153              
154 51         132 my $element = $self->element_name;
155 51 100       124 $element = '' unless defined $element;
156              
157              
158 51         196 my $idx = $self->shorten_idx($self->index_value);
159 51 100       177 return $element unless length $idx;
160 1 50       7 $idx = '"' . $idx . '"' if $idx =~ /\W/;
161 1         5 return "$element:$idx";
162             }
163              
164             sub shorten_idx {
165 58     58 0 107 my $self = shift;
166 58         99 my $long_index = shift ;
167              
168 58   100     300 my @idx = split /\n/, $long_index // '' ;
169 58         118 my $idx = shift @idx;
170 58 100       133 $idx .= '[...]' if @idx;
171              
172 58   100     243 return $idx // ''; # may be undef on freebsd with perl 5.10.1 ...
173             }
174              
175              
176             ## Fixme: not yet tested
177             sub xpath {
178 0     0 0 0 my $self = shift;
179              
180 0         0 $logger->trace("xpath called on $self");
181              
182 0         0 my $element = $self->element_name;
183 0 0       0 $element = '' unless defined $element;
184              
185 0         0 my $idx = $self->index_value;
186              
187 0         0 my $str = '';
188 0 0 0     0 $str .= $self->cim_parent->parent->xpath
189             if $self->can('cim_parent')
190             and defined $self->cim_parent;
191              
192 0 0       0 $str .= '/' . $element . ( defined $idx ? "[\@id=$idx]" : '' ) if $element;
    0          
193              
194 0         0 return $str;
195             }
196              
197             sub annotation {
198 6783     6783 1 11433 my $self = shift;
199 6783   100     21513 my $old_note = $self->{annotation} || '';
200 6783 100 66     18087 if (@_ and not $self->instance->preset and not $self->instance->layered) {
      100        
201 201         511 my $new = $self->{annotation} = join( "\n", grep { defined $_} @_ );
  214         1051  
202 201 100       982 $self->notify_change(note => 'updated annotation') unless $new eq $old_note;
203             }
204              
205 6783   100     25294 return $self->{annotation} || '';
206             }
207              
208             sub clear_annotation {
209 1     1 1 4 my $self = shift;
210 1 50       7 $self->notify_change(note => 'deleted annotation') if $self->{annotation};
211 1         6 $self->{annotation} = '';
212             }
213              
214             # may be used (but not yet) to load annotation from perl data file
215             sub load_pod_annotation {
216 1     1 1 4477 my $self = shift;
217 1         3 my $pod = shift;
218              
219 1         12 my $parser = Pod::POM->new();
220 1   33     23 my $pom = $parser->parse_text($pod)
221             || croak $parser->error();
222 1         3952 my $sections = $pom->head1();
223              
224 1         24 foreach my $s (@$sections) {
225 1 50       9 next unless $s->title eq 'Annotations';
226              
227 1         115 foreach my $item ( $s->over->[0]->item ) {
228 9         101 my $path = $item->title . ''; # force string representation. Not understood why...
229 9         531 $path =~ s/^[\s\*]+//;
230 9         41 my $note = $item->text . '';
231 9         697 $note =~ s/\s+$//;
232 9         44 $logger->trace("load_pod_annotation: '$path' -> '$note'");
233 9         93 $self->grab( steps => $path )->annotation($note);
234             }
235             }
236             }
237              
238             # fallback method for object that don't implement has_data
239             sub has_data {
240 0     0 0 0 my $self= shift;
241 0 0       0 $logger->trace("called fall-back has_data for element", $self->name) if $logger->is_trace;
242 0         0 return 1;
243             }
244              
245             sub model_searcher {
246 8     8 1 823 my $self = shift;
247 8         23 my %args = @_;
248              
249 8         47 my $model = $self->instance->config_model;
250 8         48 return Config::Model::SearchElement->new( model => $model, node => $self, %args );
251             }
252              
253             sub searcher {
254 0     0 0 0 carp "Config::Model::AnyThing searcher is deprecated";
255 0         0 goto &model_searcher;
256             }
257              
258             sub dump_as_data {
259 41     41 1 17459 my $self = shift;
260 41         140 my %args = @_;
261 41   50     200 my $full = delete $args{full_dump} || 0;
262 41 50       104 if ($full) {
263 0         0 carp "dump_as_data: full_dump parameter is deprecated. Please use 'mode => user' instead";
264 0   0     0 $args{mode} //= 'user';
265             }
266 41         203 my $dumper = Config::Model::DumpAsData->new;
267 41         180 $dumper->dump_as_data( node => $self, %args );
268             }
269              
270             # hum, check if the check information is valid
271             sub _check_check {
272 40207     40207   62711 my $self = shift;
273 40207         66096 my $p = shift;
274              
275 40207 100 66     182181 return 'yes' if not defined $p or $p eq '1' or $p eq 'yes';
      100        
276 7229 100 66     25411 return 'no' if $p eq '0' or $p eq 'no';
277 5556 50       17182 return $p if $p eq 'skip';
278              
279 0         0 croak "Internal error: Unvalid check value: $p";
280             }
281              
282             sub has_fixes {
283 0     0 0 0 my $self = shift;
284 0         0 $logger->trace( "dummy has_fixes called on " . $self->name );
285 0         0 return 0;
286             }
287              
288             sub has_warning {
289 4     4 0 22 my $self = shift;
290 4         22 $logger->trace( "dummy has_warning called on " . $self->name );
291 4         51 return 0;
292             }
293              
294             sub warp_error {
295 16     16 1 39 my $self = shift;
296 16 100       64 return '' unless defined $self->{warper};
297 7         33 return $self->{warper}->warp_error;
298             }
299              
300             # used by Value and AnyId
301             sub set_convert {
302 54     54 0 121 my ( $self, $arg_ref ) = @_;
303              
304 54         134 my $convert = delete $arg_ref->{convert};
305              
306             # convert_sub keeps a subroutine reference
307             $self->{convert_sub} =
308 2     2   9 $convert eq 'uc' ? sub { uc(shift) }
309 101     101   371 : $convert eq 'lc' ? sub { lc(shift) }
310 54 50       362 : undef;
    100          
311              
312             Config::Model::Exception::Model->throw(
313             object => $self,
314             error => "Unexpected convert value: $convert, " . "expected lc or uc"
315 54 50       203 ) unless defined $self->{convert_sub};
316             }
317              
318             __PACKAGE__->meta->make_immutable;
319              
320             1;
321              
322             # ABSTRACT: Base class for configuration tree item
323              
324             __END__
325              
326             =pod
327              
328             =encoding UTF-8
329              
330             =head1 NAME
331              
332             Config::Model::AnyThing - Base class for configuration tree item
333              
334             =head1 VERSION
335              
336             version 2.153
337              
338             =head1 SYNOPSIS
339              
340             # internal class
341              
342             =head1 DESCRIPTION
343              
344             This class must be inherited by all nodes or leaves of the
345             configuration tree.
346              
347             AnyThing provides some methods and no constructor.
348              
349             =head1 Introspection methods
350              
351             =head2 element_name
352              
353             Returns the element name that contain this object.
354              
355             =head2 index_value
356              
357             For object stored in an array or hash element, returns the index (or key)
358             containing this object.
359              
360             =head2 parent
361              
362             Returns the node containing this object. May return undef if C<parent>
363             is called on the root of the tree.
364              
365             =head2 container
366              
367             A bit like parent, this method returns the element containing this
368             object. See L</container_type>
369              
370             =head2 container_type
371              
372             Returns the type (e.g. C<list> or C<hash> or C<leaf> or C<node> or
373             C<warped_node>) of the element containing this object.
374              
375             =head2 root
376              
377             Returns the root node of the configuration tree.
378              
379             =head2 location
380              
381             Returns the node location in the configuration tree. This location
382             conforms with the syntax defined by L<grab|Config::Model::Role::Grab/grab> method.
383              
384             =head2 location_short
385              
386             Returns the node location in the configuration tree. This location truncates long
387             indexes to be readable. It cannot be used by L<grab|Config::Model::Role::Grab/grab> method.
388              
389             =head2 composite_name
390              
391             Return the element name with its index (if any). I.e. returns C<foo:bar> or
392             C<foo>.
393              
394             =head2 composite_name_short
395              
396             Return the element name with its index (if any). Too long indexes are
397             truncated to be readable.
398              
399             =head1 Annotation
400              
401             Annotation is a way to store miscellaneous information associated to
402             each node. (Yeah... comments). Reading and writing annotation makes
403             sense only if they can be read from and written to the configuration
404             file, hence the need for the following method:
405              
406             =head2 backend_support_annotation
407              
408             Returns 1 if at least one of the backends attached to a parent node
409             support to read and write annotations (aka comments) in the
410             configuration file.
411              
412             =head2 support_annotation
413              
414             Returns 1 if at least one of the backends support to read and write annotations
415             (aka comments) in the configuration file.
416              
417             =head2 annotation
418              
419             Parameters: C<( [ note1, [ note2 , ... ] ] )>
420              
421             Without argument, return a string containing the object's annotation (or
422             an empty string).
423              
424             With several arguments, join the arguments with "\n", store the annotations
425             and return the resulting string.
426              
427             =head2 load_pod_annotation
428              
429             Parameters: C<( pod_string )>
430              
431             Load annotations in configuration tree from a pod document. The pod must
432             be in the form:
433              
434             =over
435            
436             =item path
437            
438             Annotation text
439            
440             =back
441              
442             =head2 clear_annotation
443              
444             Clear the annotation of an element
445              
446             =head1 Information management
447              
448             =head2 notify_change
449              
450             Notify the instance of semantic changes. Parameters are:
451              
452             =over 8
453              
454             =item old
455              
456             old value. (optional)
457              
458             =item new
459              
460             new value (optional)
461              
462             =item path
463              
464             Location of the changed parameter starting from root node. Default to C<$self->location>.
465              
466             =item name
467              
468             element name. Default to C<$self->element_name>
469              
470             =item index
471              
472             If the changed parameter is part of a hash or an array, C<index>
473             contains the key or the index to get the changed parameter.
474              
475             =item note
476              
477             information about the change. Mandatory when neither old or new value are defined.
478              
479             =item really
480              
481             When set to 1, force recording of change even if in initial load phase.
482              
483             =item needs_save
484              
485             internal parameter.
486              
487             =back
488              
489             =head2 show_message
490              
491             Parameters: C<( string )>
492              
493             Forwarded to L<Config::Model::Instance/show_message>.
494              
495             =head2 root_path
496              
497             Forwarded to L<Config::Model::Instance/"root_path">.
498              
499             =head2 model_searcher
500              
501             Returns an object dedicated to search an element in the configuration
502             model.
503              
504             This method returns a L<Config::Model::SearchElement> object. See
505             L<Config::Model::Searcher> for details on how to handle a search.
506              
507             =head2 dump_as_data
508              
509             Dumps the configuration data of the node and its siblings into a perl
510             data structure.
511              
512             Returns a hash ref containing the data. See
513             L<Config::Model::DumpAsData> for details.
514              
515             =head2 warp_error
516              
517             Returns a string describing any issue with L<Config::Model::Warper> object.
518             Returns '' if invoked on a tree object without warp specification.
519              
520             =head1 AUTHOR
521              
522             Dominique Dumont, (ddumont at cpan dot org)
523              
524             =head1 SEE ALSO
525              
526             L<Config::Model>,
527             L<Config::Model::Instance>,
528             L<Config::Model::Node>,
529             L<Config::Model::Loader>,
530             L<Config::Model::Dumper>
531              
532             =head1 AUTHOR
533              
534             Dominique Dumont
535              
536             =head1 COPYRIGHT AND LICENSE
537              
538             This software is Copyright (c) 2005-2022 by Dominique Dumont.
539              
540             This is free software, licensed under:
541              
542             The GNU Lesser General Public License, Version 2.1, February 1999
543              
544             =cut