File Coverage

blib/lib/Config/Model/DumpAsData.pm
Criterion Covered Total %
statement 129 131 98.4
branch 44 54 81.4
condition 33 44 75.0
subroutine 20 20 100.0
pod 3 3 100.0
total 229 252 90.8


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              
11             use Carp;
12 59     59   393 use strict;
  59         129  
  59         3426  
13 59     59   341 use warnings;
  59         121  
  59         1131  
14 59     59   248 use 5.10.1;
  59         121  
  59         1369  
15 59     59   594  
  59         207  
16             use Config::Model::Exception;
17 59     59   365 use Config::Model::ObjTreeScanner;
  59         146  
  59         1449  
18 59     59   315  
  59         137  
  59         74696  
19             bless {}, shift;
20             }
21 43     43 1 128  
22             my $self = shift;
23              
24             my %args = @_;
25 41     41 1 73 my $dump_node = delete $args{node}
26             || croak "dump_as_data: missing 'node' parameter";
27 41         114 my $mode = delete $args{mode} // '';
28             my $skip_aw = delete $args{skip_auto_write} || '';
29 41   33     149 my $auto_v = delete $args{auto_vivify} || 0;
30 41   100     154 my $ordered_hash_as_list = delete $args{ordered_hash_as_list};
31 41   100     161 my $to_boolean = delete $args{to_boolean} // sub {return $_[0] };
32 41   50     148 $ordered_hash_as_list = 1 unless defined $ordered_hash_as_list;
33 41         80  
34 41   100 6   227 # mode and full_dump params are both accepted
  6         19  
35 41 100       136 my $full = delete $args{full_dump} || 0;
36             carp "dump_as_data: full_dump parameter is deprecated. Please use 'mode => user' instead"
37             if $full;
38 41   50     143  
39 41 50       100 my $fetch_mode =
40             $full ? 'user'
41             : $mode eq 'full' ? 'user'
42 41 100       189 : $mode ? $mode
    50          
    50          
43             : 'custom';
44              
45             my $std_cb = sub {
46             my ( $scanner, $data_r, $obj, $element, $index, $value_obj ) = @_;
47             my $v = $value_obj->fetch(mode => $fetch_mode);
48             # transform boolean type in boolean object
49 405     405   728 $$data_r = $value_obj->value_type eq 'boolean' ? $to_boolean->($v) : $v;
50 405         1016 };
51              
52 405 100       1558 my $check_list_element_cb = sub {
53 41         151 my ( $scanner, $data_r, $node, $element_name, @check_items ) = @_;
54             my $a_ref = $node->fetch_element($element_name)->get_checked_list;
55              
56 11     11   32 # don't store empty checklist
57 11         28 $$data_r = $a_ref if @$a_ref;
58             };
59              
60 11 100       75 my $hash_element_cb = sub {
61 41         129 my ( $scanner, $data_ref, $node, $element_name, @keys ) = @_;
62              
63             my $force_write = $node->fetch_element($element_name)->write_empty_value;
64 35     35   97  
65             # resume exploration but pass a ref on $data_ref hash element
66 35         86 # instead of data_ref
67             my %h;
68             my @res;
69             foreach my $k (@keys) {
70 35         66 my $v;
71             $scanner->scan_hash( \$v, $node, $element_name, $k );
72 35         66  
73 50         69 # don't create the key if $v is undef
74 50         162 if (defined $v or $force_write) {
75             $h{$k} = $v;
76             push @res , $k, $v;
77 50 100 100     159 }
78 45         114 } ;
79 45         114  
80             my $ordered_hash = $node->fetch_element($element_name)->ordered;
81              
82             if ( $ordered_hash and $ordered_hash_as_list ) {
83 35         109 $$data_ref = \@res if @res;
84             }
85 35 100 100     164 else {
86 10 100       51 $h{'__'.$element_name.'_order'} = \@keys if $ordered_hash and @keys;
87             $$data_ref = \%h if scalar %h;
88             }
89 25 100 66     87 };
90 25 100       135  
91             my $list_element_cb = sub {
92 41         190 my ( $scanner, $data_ref, $node, $element_name, @idx ) = @_;
93              
94             # resume exploration but pass a ref on $data_ref hash element
95 45     45   100 # instead of data_ref
96             my @a;
97             foreach my $i (@idx) {
98             my $v;
99 45         58 $scanner->scan_hash( \$v, $node, $element_name, $i );
100 45         95 push @a, $v if defined $v;
101 60         80 }
102 60         190 $$data_ref = \@a if scalar @a;
103 60 100       227 };
104              
105 45 100       203 my $node_content_cb = sub {
106 41         139 my ( $scanner, $data_ref, $node, @element ) = @_;
107             my %h;
108             foreach my $e (@element) {
109 86     86   249 my $v;
110 86         124 $scanner->scan_element( \$v, $node, $e );
111 86         145 $h{$e} = $v if defined $v;
112 431         538 }
113 431         1299 $$data_ref = \%h if scalar %h;
114 431 100       1160 };
115              
116 86 100       437 my $node_element_cb = sub {
117 41         137 my ( $scanner, $data_ref, $node, $element_name, $key, $next ) = @_;
118              
119             return if $skip_aw and $next->is_auto_write_for_type($skip_aw);
120 68     68   200  
121             $scanner->scan_node( $data_ref, $next );
122 68 50 66     236 };
123              
124 68         232 my @scan_args = (
125 41         142 check => delete $args{check} || 'yes',
126             fallback => 'all',
127             auto_vivify => $auto_v,
128 41   100     286 list_element_cb => $list_element_cb,
129             check_list_element_cb => $check_list_element_cb,
130             hash_element_cb => $hash_element_cb,
131             leaf_cb => $std_cb,
132             node_element_cb => $node_element_cb,
133             node_content_cb => $node_content_cb,
134             );
135              
136             my @left = keys %args;
137             croak "DumpAsData: unknown parameter:@left" if @left;
138              
139 41         91 # perform the scan
140 41 50       141 my $view_scanner = Config::Model::ObjTreeScanner->new(@scan_args);
141              
142             my $obj_type = $dump_node->get_type;
143 41         239 my $result;
144             my $p = $dump_node->parent;
145 41         193 my $e = $dump_node->element_name;
146 41         78 my $i = $dump_node->index_value; # defined only for hash and list
147 41         142  
148 41         129 if ( $obj_type =~ /node/ ) {
149 41         179 $view_scanner->scan_node( \$result, $dump_node );
150             }
151 41 100 100     239 elsif ( defined $i ) {
    100 100        
    50 66        
152 18         66 $view_scanner->scan_hash( \$result, $p, $e, $i );
153             }
154             elsif ($obj_type eq 'list'
155 10         33 or $obj_type eq 'hash'
156             or $obj_type eq 'leaf'
157             or $obj_type eq 'check_list' ) {
158             $view_scanner->scan_element( \$result, $p, $e );
159             }
160             else {
161 13         47 croak "dump_as_data: unexpected type: $obj_type";
162             }
163              
164 0         0 return $result;
165             }
166              
167 41         1368 my $self = shift;
168              
169             my %args = @_;
170             my $dump_node = delete $args{node}
171 2     2 1 4 || croak "dump_annotations_as_pod: missing 'node' parameter";
172              
173 2         7 my $annotation_to_pod = sub {
174             my $obj = shift;
175 2   33     9 my $path = shift || $obj->location;
176             my $a = $obj->annotation;
177             if ($a) {
178 366     366   482 chomp $a;
179 366   66     1023 return "=item $path\n\n$a\n\n";
180 366         795 }
181 366 100       582 else {
182 16         33 return '';
183 16         60 }
184             };
185              
186 350         794 my $std_cb = sub {
187             my ( $scanner, $data_r, $obj, $element, $index, $value_obj ) = @_;
188 2         16 $$data_r .= $annotation_to_pod->($value_obj);
189             };
190              
191 157     157   258 my $hash_element_cb = sub {
192 157         271 my ( $scanner, $data_ref, $node, $element_name, @keys ) = @_;
193 2         8 my $h = $node->fetch_element($element_name);
194             my $h_path = $h->location . ':';
195             foreach (@keys) {
196 26     26   65 $$data_ref .= $annotation_to_pod->( $h->fetch_with_id($_), $h_path . $_ );
197 26         58 $scanner->scan_hash( $data_ref, $node, $element_name, $_ );
198 26         134 }
199 26         75 };
200 37         93  
201 37         106 my $node_content_cb = sub {
202             my ( $scanner, $data_ref, $node, @element ) = @_;
203 2         9 my $node_path = $node->location;
204             $node_path .= ' ' if $node_path;
205             foreach (@element) {
206 28     28   81 $$data_ref .= $annotation_to_pod->(
207 28         106 $node->fetch_element( name => $_, check => 'no' ),
208 28 100       63 $node_path . $_
209 28         50 );
210 172         400 $scanner->scan_element( $data_ref, $node, $_ );
211             }
212             };
213              
214 172         512 my @scan_args = (
215             check => delete $args{check} || 'yes',
216 2         7 fallback => 'all',
217             leaf_cb => $std_cb,
218             node_content_cb => $node_content_cb,
219 2   50     16 hash_element_cb => $hash_element_cb,
220             list_element_cb => $hash_element_cb,
221             );
222              
223             my @left = keys %args;
224             croak "dump_annotations_as_pod: unknown parameter:@left" if @left;
225              
226             # perform the scan
227 2         5 my $view_scanner = Config::Model::ObjTreeScanner->new(@scan_args);
228 2 50       7  
229             my $obj_type = $dump_node->get_type;
230             my $result = '';
231 2         16  
232             my $a = $dump_node->annotation;
233 2         9 my $l = $dump_node->location;
234 2         5 $result .= "=item $l\n\n$a\n\n" if $a;
235              
236 2         7 if ( $obj_type =~ /node/ ) {
237 2         8 $view_scanner->scan_node( \$result, $dump_node );
238 2 50       12 }
239             else {
240 2 50       11 croak "dump_annotations_as_pod: unexpected type: $obj_type";
241 2         10 }
242              
243             return '' unless $result;
244 0         0 return "=head1 Annotations\n\n=over\n\n" . $result . "=back\n\n";
245             }
246              
247 2 50       9 1;
248 2         85  
249             # ABSTRACT: Dump configuration content as a perl data structure
250              
251              
252             =pod
253              
254             =encoding UTF-8
255              
256             =head1 NAME
257              
258             Config::Model::DumpAsData - Dump configuration content as a perl data structure
259              
260             =head1 VERSION
261              
262             version 2.151
263              
264             =head1 SYNOPSIS
265              
266             use Config::Model ;
267             use Data::Dumper ;
268              
269             # define configuration tree object
270             my $model = Config::Model->new ;
271             $model ->create_config_class (
272             name => "MyClass",
273             element => [
274             [qw/foo bar/] => {
275             type => 'leaf',
276             value_type => 'string'
277             },
278             baz => {
279             type => 'hash',
280             index_type => 'string' ,
281             cargo => {
282             type => 'leaf',
283             value_type => 'string',
284             },
285             },
286              
287             ],
288             ) ;
289              
290             my $inst = $model->instance(root_class_name => 'MyClass' );
291              
292             my $root = $inst->config_root ;
293              
294             # put some data in config tree the hard way
295             $root->fetch_element('foo')->store('yada') ;
296             $root->fetch_element('bar')->store('bla bla') ;
297             $root->fetch_element('baz')->fetch_with_id('en')->store('hello') ;
298              
299             # put more data the easy way
300             my $steps = 'baz:fr=bonjour baz:hr="dobar dan"';
301             $root->load( steps => $steps ) ;
302              
303             print Dumper($root->dump_as_data);
304             # $VAR1 = {
305             # 'bar' => 'bla bla',
306             # 'baz' => {
307             # 'en' => 'hello',
308             # 'fr' => 'bonjour',
309             # 'hr' => 'dobar dan'
310             # },
311             # 'foo' => 'yada'
312             # };
313              
314             =head1 DESCRIPTION
315              
316             This module is used directly by L<Config::Model::Node> to dump the content
317             of a configuration tree in perl data structure.
318              
319             The perl data structure is a hash of hash. Only
320             L<CheckList|Config::Model::CheckList> content is stored in an array ref.
321              
322             User can pass a sub reference to apply to values of boolean type. This
323             sub can be used to convert the value to an object representing a
324             boolean like L<boolean>. (since 2.129)
325              
326             Note that undefined values are skipped for list element. I.e. if a
327             list element contains C<('a',undef,'b')>, the data structure then
328             contains C<'a','b'>.
329              
330             =head1 CONSTRUCTOR
331              
332             =head2 new
333              
334             No parameter. The constructor should be used only by
335             L<Config::Model::Node>.
336              
337             =head1 Methods
338              
339             =head2 dump_as_data
340              
341             Return a perl data structure
342              
343             Parameters are:
344              
345             =over
346              
347             =item node
348              
349             Reference to a L<Config::Model::Node> object. Mandatory
350              
351             =item full_dump
352              
353             Also dump default values in the data structure. Useful if the dumped
354             configuration data is used by the application. This parameter is
355             deprecated in favor of mode parameter.
356              
357             =item mode
358              
359             Note that C<mode> parameter is also accepted and overrides
360             C<full_dump> parameter. See L<Config::Model::Value/fetch> for
361             details on C<mode>.
362              
363             =item skip_auto_write
364              
365             Skip node that have a C<perl write> capability in their model. See
366             L<Config::Model::BackendMgr>.
367              
368             This option must be used when using DumpAsData: to write back
369             configuration data. When a configuration model contains several
370             backends (one at the tree root and others in tree nodes), setting this
371             option ensure that the "root" configuration file does not contain data
372             duplicated in configuration file of others tree nodes.
373              
374             =item auto_vivify
375              
376             Scan and create data for nodes elements even if no actual data was
377             stored in them. This may be useful to trap missing mandatory values.
378              
379             =item ordered_hash_as_list
380              
381             By default, ordered hash (i.e. the order of the keys are important)
382             are dumped as Perl list. This is the faster way to dump such hashed
383             while keeping the key order. But it's the less readable way.
384              
385             When this parameter is 1 (default), the ordered hash is dumped as a
386             list:
387              
388             my_hash => [ A => 'foo', B => 'bar', C => 'baz' ]
389              
390             When this parameter is set as 0, the ordered hash is dumped with a
391             special key that specifies the order of keys. E.g.:
392              
393             my_hash => {
394             __my_hash_order => [ 'A', 'B', 'C' ] ,
395             B => 'bar', A => 'foo', C => 'baz'
396             }
397              
398             =item to_boolean
399              
400             Sub reference to map a value of type boolean to a boolean class (since
401             2.129). For instance:
402              
403             to_boolean => sub { boolean($_[0]); }
404              
405             Default is C<sub { return $_[0] }>
406              
407             =back
408              
409             =head1 Methods
410              
411             =head2 dump_annotations_as_pod
412              
413             Return a string formatted in pod (See L<perlpod>) with the annotations.
414              
415             Parameters are:
416              
417             =over
418              
419             =item node
420              
421             Reference to a L<Config::Model::Node> object. Mandatory
422              
423             =item check_list
424              
425             Yes, no or skip
426              
427             =back
428              
429             =head1 AUTHOR
430              
431             Dominique Dumont, (ddumont at cpan dot org)
432              
433             =head1 SEE ALSO
434              
435             L<Config::Model>,L<Config::Model::Node>,L<Config::Model::ObjTreeScanner>
436              
437             =head1 AUTHOR
438              
439             Dominique Dumont
440              
441             =head1 COPYRIGHT AND LICENSE
442              
443             This software is Copyright (c) 2005-2022 by Dominique Dumont.
444              
445             This is free software, licensed under:
446              
447             The GNU Lesser General Public License, Version 2.1, February 1999
448              
449             =cut