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   384 use strict;
  59         132  
  59         3559  
13 59     59   360 use warnings;
  59         132  
  59         1104  
14 59     59   271 use 5.10.1;
  59         111  
  59         1415  
15 59     59   622  
  59         200  
16             use Config::Model::Exception;
17 59     59   351 use Config::Model::ObjTreeScanner;
  59         146  
  59         1452  
18 59     59   302  
  59         121  
  59         74807  
19             bless {}, shift;
20             }
21 43     43 1 123  
22             my $self = shift;
23              
24             my %args = @_;
25 41     41 1 61 my $dump_node = delete $args{node}
26             || croak "dump_as_data: missing 'node' parameter";
27 41         101 my $mode = delete $args{mode} // '';
28             my $skip_aw = delete $args{skip_auto_write} || '';
29 41   33     119 my $auto_v = delete $args{auto_vivify} || 0;
30 41   100     150 my $ordered_hash_as_list = delete $args{ordered_hash_as_list};
31 41   100     140 my $to_boolean = delete $args{to_boolean} // sub {return $_[0] };
32 41   50     127 $ordered_hash_as_list = 1 unless defined $ordered_hash_as_list;
33 41         66  
34 41   100 6   204 # mode and full_dump params are both accepted
  6         19  
35 41 100       112 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     139  
39 41 50       91 my $fetch_mode =
40             $full ? 'user'
41             : $mode eq 'full' ? 'user'
42 41 100       137 : $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   709 $$data_r = $value_obj->value_type eq 'boolean' ? $to_boolean->($v) : $v;
50 405         885 };
51              
52 405 100       1842 my $check_list_element_cb = sub {
53 41         144 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   37 # don't store empty checklist
57 11         24 $$data_r = $a_ref if @$a_ref;
58             };
59              
60 11 100       46 my $hash_element_cb = sub {
61 41         121 my ( $scanner, $data_ref, $node, $element_name, @keys ) = @_;
62              
63             my $force_write = $node->fetch_element($element_name)->write_empty_value;
64 35     35   72  
65             # resume exploration but pass a ref on $data_ref hash element
66 35         73 # instead of data_ref
67             my %h;
68             my @res;
69             foreach my $k (@keys) {
70 35         60 my $v;
71             $scanner->scan_hash( \$v, $node, $element_name, $k );
72 35         59  
73 50         59 # don't create the key if $v is undef
74 50         144 if (defined $v or $force_write) {
75             $h{$k} = $v;
76             push @res , $k, $v;
77 50 100 100     139 }
78 45         94 } ;
79 45         120  
80             my $ordered_hash = $node->fetch_element($element_name)->ordered;
81              
82             if ( $ordered_hash and $ordered_hash_as_list ) {
83 35         115 $$data_ref = \@res if @res;
84             }
85 35 100 100     118 else {
86 10 100       46 $h{'__'.$element_name.'_order'} = \@keys if $ordered_hash and @keys;
87             $$data_ref = \%h if scalar %h;
88             }
89 25 100 66     59 };
90 25 100       91  
91             my $list_element_cb = sub {
92 41         154 my ( $scanner, $data_ref, $node, $element_name, @idx ) = @_;
93              
94             # resume exploration but pass a ref on $data_ref hash element
95 45     45   101 # instead of data_ref
96             my @a;
97             foreach my $i (@idx) {
98             my $v;
99 45         51 $scanner->scan_hash( \$v, $node, $element_name, $i );
100 45         83 push @a, $v if defined $v;
101 60         70 }
102 60         160 $$data_ref = \@a if scalar @a;
103 60 100       207 };
104              
105 45 100       147 my $node_content_cb = sub {
106 41         165 my ( $scanner, $data_ref, $node, @element ) = @_;
107             my %h;
108             foreach my $e (@element) {
109 86     86   277 my $v;
110 86         122 $scanner->scan_element( \$v, $node, $e );
111 86         127 $h{$e} = $v if defined $v;
112 431         523 }
113 431         1137 $$data_ref = \%h if scalar %h;
114 431 100       1071 };
115              
116 86 100       305 my $node_element_cb = sub {
117 41         131 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   159  
121             $scanner->scan_node( $data_ref, $next );
122 68 50 66     176 };
123              
124 68         161 my @scan_args = (
125 41         126 check => delete $args{check} || 'yes',
126             fallback => 'all',
127             auto_vivify => $auto_v,
128 41   100     222 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         81 # perform the scan
140 41 50       84 my $view_scanner = Config::Model::ObjTreeScanner->new(@scan_args);
141              
142             my $obj_type = $dump_node->get_type;
143 41         231 my $result;
144             my $p = $dump_node->parent;
145 41         162 my $e = $dump_node->element_name;
146 41         63 my $i = $dump_node->index_value; # defined only for hash and list
147 41         114  
148 41         109 if ( $obj_type =~ /node/ ) {
149 41         110 $view_scanner->scan_node( \$result, $dump_node );
150             }
151 41 100 100     188 elsif ( defined $i ) {
    100 100        
    50 66        
152 18         61 $view_scanner->scan_hash( \$result, $p, $e, $i );
153             }
154             elsif ($obj_type eq 'list'
155 10         28 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         37 croak "dump_as_data: unexpected type: $obj_type";
162             }
163              
164 0         0 return $result;
165             }
166              
167 41         1076 my $self = shift;
168              
169             my %args = @_;
170             my $dump_node = delete $args{node}
171 2     2 1 7 || croak "dump_annotations_as_pod: missing 'node' parameter";
172              
173 2         12 my $annotation_to_pod = sub {
174             my $obj = shift;
175 2   33     7 my $path = shift || $obj->location;
176             my $a = $obj->annotation;
177             if ($a) {
178 366     366   475 chomp $a;
179 366   66     1106 return "=item $path\n\n$a\n\n";
180 366         768 }
181 366 100       623 else {
182 16         23 return '';
183 16         49 }
184             };
185              
186 350         806 my $std_cb = sub {
187             my ( $scanner, $data_r, $obj, $element, $index, $value_obj ) = @_;
188 2         11 $$data_r .= $annotation_to_pod->($value_obj);
189             };
190              
191 157     157   283 my $hash_element_cb = sub {
192 157         266 my ( $scanner, $data_ref, $node, $element_name, @keys ) = @_;
193 2         9 my $h = $node->fetch_element($element_name);
194             my $h_path = $h->location . ':';
195             foreach (@keys) {
196 26     26   58 $$data_ref .= $annotation_to_pod->( $h->fetch_with_id($_), $h_path . $_ );
197 26         63 $scanner->scan_hash( $data_ref, $node, $element_name, $_ );
198 26         111 }
199 26         69 };
200 37         84  
201 37         101 my $node_content_cb = sub {
202             my ( $scanner, $data_ref, $node, @element ) = @_;
203 2         7 my $node_path = $node->location;
204             $node_path .= ' ' if $node_path;
205             foreach (@element) {
206 28     28   72 $$data_ref .= $annotation_to_pod->(
207 28         126 $node->fetch_element( name => $_, check => 'no' ),
208 28 100       57 $node_path . $_
209 28         53 );
210 172         404 $scanner->scan_element( $data_ref, $node, $_ );
211             }
212             };
213              
214 172         437 my @scan_args = (
215             check => delete $args{check} || 'yes',
216 2         8 fallback => 'all',
217             leaf_cb => $std_cb,
218             node_content_cb => $node_content_cb,
219 2   50     14 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         6 my $view_scanner = Config::Model::ObjTreeScanner->new(@scan_args);
228 2 50       8  
229             my $obj_type = $dump_node->get_type;
230             my $result = '';
231 2         8  
232             my $a = $dump_node->annotation;
233 2         9 my $l = $dump_node->location;
234 2         6 $result .= "=item $l\n\n$a\n\n" if $a;
235              
236 2         12 if ( $obj_type =~ /node/ ) {
237 2         7 $view_scanner->scan_node( \$result, $dump_node );
238 2 50       10 }
239             else {
240 2 50       9 croak "dump_annotations_as_pod: unexpected type: $obj_type";
241 2         9 }
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       11 1;
248 2         61  
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.152
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