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