File Coverage

blib/lib/Config/Model/Dumper.pm
Criterion Covered Total %
statement 127 130 97.6
branch 54 62 87.1
condition 31 37 83.7
subroutine 18 18 100.0
pod 2 5 40.0
total 232 252 92.0


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   377 use strict;
  59         106  
  59         3352  
13 59     59   313 use warnings;
  59         94  
  59         1087  
14 59     59   243  
  59         101  
  59         1375  
15             use Config::Model::Exception;
16 59     59   307 use Config::Model::ObjTreeScanner;
  59         1102  
  59         1252  
17 59     59   23458 use Config::Model::Value;
  59         171  
  59         1834  
18 59     59   37323  
  59         235  
  59         88512  
19             bless {}, shift;
20             }
21 154     154 1 476  
22             _quote( qr/[\s~"#*]/, @_ );
23             }
24              
25 3772     3772 0 12094 _quote( qr/[\s~"@*<>.=#]/, @_ );
26             }
27              
28             my ( $re, @res ) = @_;
29 4375     4375 0 10391 foreach (@res) {
30             if ( defined $_ and ( /$re/ or $_ eq '' ) ) {
31             s/"/\\"/g; # escape present quotes
32             $_ = '"' . $_ . '"'; # add my quotes
33 8147     8147   13143 }
34 8147         11632 }
35 8165 100 100     23674 return wantarray ? @res : $res[0];
      100        
36 232         616 }
37 232         669  
38             my @res = @_;
39             foreach (@res) {
40 8147 100       16670 if ( defined $_ and $_ and (/(\s|"|\*)/) ) {
41             s/"/\\"/g; # escape present quotes
42             $_ = '"' . $_ . '"'; # add my quotes
43             }
44 5103     5103 0 8345 }
45 5103         7970 return wantarray ? @res : $res[0];
46 5103 100 66     16302 }
      100        
47 165         829  
48 165         538 my $self = shift;
49              
50             my %args = @_;
51 5103 50       10388 my $full = delete $args{full_dump} || 0;
52             my $skip_aw = delete $args{skip_auto_write} || '';
53             my $auto_v = delete $args{auto_vivify} || 0;
54             my $mode = delete $args{mode} || '';
55 154     154 1 303  
56             if ($full) {
57 154         471 carp "dump_tree: full_dump parameter is deprecated, please use mode => 'user'";
58 154   50     646 }
59 154   100     684  
60 154   100     535 my $check = delete $args{check} || 'yes';
61 154   100     506 if ( $check !~ /yes|no|skip/ ) {
62             croak "dump_tree: unexpected 'check' value: $check";
63 154 50       378 }
64 0         0  
65             # mode parameter is slightly different from fetch's mode
66             my $fetch_mode =
67 154   100     492 $full ? 'user'
68 154 50       1209 : $mode eq 'full' ? 'user'
69 0         0 : $mode ? $mode
70             : 'custom';
71              
72             if ( my $err = Config::Model::Value->is_bad_mode($fetch_mode) ) {
73 154 100       2107 croak "dump_tree: $err";
    100          
    50          
74             }
75              
76             my $node = delete $args{node}
77             || croak "dump_tree: missing 'node' parameter";
78              
79 154 50       666 my $compute_pad = sub {
80 0         0 my $depth = 0;
81             my $obj = shift;
82             while ( defined $obj->parent ) {
83             $depth++;
84 154   33     499 $obj = $obj->parent;
85             }
86             return ' ' x $depth;
87 4885     4885   5922 };
88 4885         5989  
89 4885         13800 my $leaf_cb = sub {
90 5979         6854 my ( $scanner, $data_r, $node, $element, $index, $value_obj ) = @_;
91 5979         13037  
92             # get value or only customized value
93 4885         10922 my $value = quote( $value_obj->fetch( mode => $fetch_mode, check => $check ) );
94 154         805 $index = id_quote($index);
95              
96             my $pad = $compute_pad->($node);
97 3737     3737   6360  
98             my $name =
99             defined $index
100 3737         9133 ? "$element:$index"
101 3734         8652 : $element;
102              
103 3734         7889 # add annotation for obj contained in hash or list
104             my $note = note_quote( $value_obj->annotation );
105 3734 100       6832 $$data_r .= "\n" . $pad . $name if defined $value or $note;
106             if (defined $value) {
107             $value =~ s/\\n/\\\\n/g;
108             $$data_r .= '=' . $value;
109             }
110             $$data_r .= '#' . $note if $note;
111 3734         9022 };
112 3734 100 100     11923  
113 3734 100       6580 my $check_list_cb = sub {
114 1242         2462 my ( $scanner, $data_r, $node, $element, $index, $value_obj ) = @_;
115 1242         2212  
116             # get value or only customized value
117 3734 100       15326 my $value = $value_obj->fetch( mode => $fetch_mode, check => $check );
118 154         709 my $qvalue = quote($value);
119             $index = id_quote($index);
120             my $pad = $compute_pad->($node);
121 38     38   105  
122             my $name =
123             defined $index
124 38         151 ? "$element:$index"
125 38         119 : $element;
126 38         125  
127 38         113 # add annotation for obj contained in hash or list
128             my $note = note_quote( $value_obj->annotation );
129 38 50       107 $$data_r .= "\n" . $pad . $name if $value or $note;
130             $$data_r .= '=' . $qvalue if $value;
131             $$data_r .= '#' . $note if $note;
132             };
133              
134             my $list_element_cb = sub {
135 38         152 my ( $scanner, $data_r, $node, $element, @keys ) = @_;
136 38 100 66     165  
137 38 100       120 my $pad = $compute_pad->($node);
138 38 100       197 my $list_obj = $node->fetch_element($element);
139 154         691  
140             # add annotation for list element
141             my $list_note = note_quote( $list_obj->annotation );
142 312     312   724 $$data_r .= "\n$pad$element#$list_note" if $list_note;
143              
144 312         684 if ( $list_obj->cargo_type eq 'node' ) {
145 312         804 foreach my $k (@keys) {
146             $scanner->scan_list( $data_r, $node, $element, $k );
147             }
148 312         870 }
149 312 100       685 else {
150             # write value comments
151 312 100       853 foreach my $idx ( $list_obj->fetch_all_indexes ) {
152 17         54 my $note = $list_obj->fetch_with_id($idx)->annotation;
153 34         122 $$data_r .= "\n$pad$element:$idx#" . note_quote($note) if $note;
154             }
155              
156             # skip undef values
157             my @val = id_quote(
158 295         730 grep { defined $_ }
159 383         840 $list_obj->fetch_all_values(mode => $fetch_mode, check => $check)
160 383 100       999 );
161             $$data_r .= "\n$pad$element:=" . join( ',', @val ) if @val;
162             }
163             };
164              
165 295         937 my $hash_element_cb = sub {
  313         642  
166             my ( $scanner, $data_r, $node, $element, @keys ) = @_;
167              
168 295 100       2262 my $pad = $compute_pad->($node);
169             my $hash_obj = $node->fetch_element($element);
170 154         831  
171             # add annotation for list or hash element
172             my $note = note_quote( $hash_obj->annotation );
173 168     168   472 $$data_r .= "\n$pad$element#$note" if $note;
174              
175 168         358 # resume exploration
176 168         499 map { $scanner->scan_hash( $data_r, $node, $element, $_ ); } @keys;
177             };
178              
179 168         567 # called for nodes contained in nodes (not root).
180 168 100       438 # This node can be held by a plain element or a hash element or a list element
181             my $node_element_cb = sub {
182             my ( $scanner, $data_r, $node, $element, $key, $contained_node ) = @_;
183 168         453  
  336         1087  
184 154         597 my $type = $node->element_type($element);
185              
186             return if $skip_aw and $contained_node->is_auto_write_for_type($skip_aw);
187              
188             my $pad = $compute_pad->($node);
189 633     633   1535 my $elt = $node->fetch_element($element);
190              
191 633         1541 # load string can feature only one comment per element_type
192             # ie foo#comment foo:bar#comment foo:bar=val#comment are fine
193 633 50 66     1705 # but foo#comment:bar if not valid -> foo#commaent foo:bar
194              
195 633         1168 my $head = "\n$pad$element";
196 633         1484 my $node_note = note_quote( $contained_node->annotation );
197              
198             if ( $type eq 'list' or $type eq 'hash' ) {
199             $head .= ':' . id_quote($key);
200             $head .= '#' . $node_note if $node_note;
201             my $sub_data = '';
202 633         1704 $scanner->scan_node( \$sub_data, $contained_node );
203 633         1711 $$data_r .= $head . $sub_data . ' -';
204             }
205 633 100 100     2306 else {
206 308         688 $head .= '#' . $node_note if $node_note;
207 308 100       919 my $sub_data = '';
208 308         515 $scanner->scan_node( \$sub_data, $contained_node );
209 308         1170  
210 307         2817 # skip simple nodes that do not bring data
211             $$data_r .= $head . $sub_data . ' -' if $sub_data;
212             }
213 325 100       767 };
214 325         530  
215 325         1190 my @scan_args = (
216             fallback => 'all',
217             auto_vivify => $auto_v,
218 325 100       2447 list_element_cb => $list_element_cb,
219             hash_element_cb => $hash_element_cb,
220 154         712 leaf_cb => $leaf_cb,
221             node_element_cb => $node_element_cb,
222 154         717 check_list_element_cb => $check_list_cb,
223             check => $check,
224             );
225              
226             my @left = keys %args;
227             croak "Dumper: unknown parameter:@left" if @left;
228              
229             # perform the scan
230             my $view_scanner = Config::Model::ObjTreeScanner->new(@scan_args);
231              
232             my $ret = '';
233 154         310 my $root_note = note_quote( $node->annotation );
234 154 50       370 $ret .= "\n#$root_note" if $root_note;
235             $view_scanner->scan_node( \$ret, $node );
236              
237 154         1100 substr( $ret, 0, 1, '' ); # remove leading \n
238             $ret .= ' -' if $ret;
239 154         345 return $ret . "\n";
240 154         677 }
241 154 100       381  
242 154         607 1;
243              
244 151         795 # ABSTRACT: Serialize data of config tree
245 151 100       530  
246 151         7596  
247             =pod
248              
249             =encoding UTF-8
250              
251             =head1 NAME
252              
253             Config::Model::Dumper - Serialize data of config tree
254              
255             =head1 VERSION
256              
257             version 2.152
258              
259             =head1 SYNOPSIS
260              
261             use Config::Model ;
262              
263             # define configuration tree object
264             my $model = Config::Model->new ;
265             $model ->create_config_class (
266             name => "MyClass",
267             element => [
268             [qw/foo bar/] => {
269             type => 'leaf',
270             value_type => 'string'
271             },
272             baz => {
273             type => 'hash',
274             index_type => 'string' ,
275             cargo => {
276             type => 'leaf',
277             value_type => 'string',
278             },
279             },
280              
281             ],
282             ) ;
283              
284             my $inst = $model->instance(root_class_name => 'MyClass' );
285              
286             my $root = $inst->config_root ;
287              
288             # put some data in config tree the hard way
289             $root->fetch_element('foo')->store('yada') ;
290             $root->fetch_element('bar')->store('bla bla') ;
291             $root->fetch_element('baz')->fetch_with_id('en')->store('hello') ;
292              
293             # put more data the easy way
294             my $steps = 'baz:fr=bonjour baz:hr="dobar dan"';
295             $root->load( steps => $steps ) ;
296              
297             # dump only customized data
298             print $root->dump_tree;
299              
300             =head1 DESCRIPTION
301              
302             This module is used directly by L<Config::Model::Node> to serialize
303             configuration data in a compact (but readable) string.
304              
305             The serialization can be done in standard mode where only customized
306             values are dumped in the string. I.e. only data modified by the user
307             are dumped.
308              
309             All other mode supported by L<Config::Model::Value/fetch> can be used,
310             for instance, to get default values.
311              
312             The serialized string can be used by L<Config::Model::Loader> to store
313             the data back into a configuration tree.
314              
315             Note that undefined values are skipped for list element. I.e. if a list
316             element contains C<('a',undef,'b')>, the dump then contains C<'a','b'>.
317              
318             =head1 CONSTRUCTOR
319              
320             =head2 new
321              
322             No parameter. The constructor should be used only by
323             L<Config::Model::Node>.
324              
325             =head1 Methods
326              
327             =head2 dump_tree
328              
329             Return a string that contains a dump of the object tree with all the
330             values. This string follows the convention defined by
331             L<Config::Model::Loader>.
332              
333             The serialized string can be used by L<Config::Model::Loader> to store
334             the data back into a configuration tree.
335              
336             Parameters are:
337              
338             =over
339              
340             =item mode
341              
342             C<full> dumps all configuration data including default
343             values.
344              
345             All mode values from L<Config::Model::Value/fetch> can be used.
346              
347             By default, the dump contains only data modified by the user
348             (i.e. C<custom> data that differ from default or preset values).
349              
350             =item node
351              
352             Reference to the L<Config::Model::Node> object that is dumped. All
353             nodes and leaves attached to this node are also dumped.
354              
355             =item skip_auto_write ( <backend_name> )
356              
357             Skip node that have a write capability matching C<backend_name> in
358             their model. See L<Config::Model::BackendMgr>.
359              
360             This option must be used when using Dumper to write back configuration
361             data. When a configuration model contains several backends (one at the
362             tree root and others in tree nodes), setting this option ensure that
363             the "root" configuration file does not contain data duplicated in
364             configuration file of others tree nodes.
365              
366             =item auto_vivify
367              
368             Scan and create data for nodes elements even if no actual data was
369             stored in them. This may be useful to trap missing mandatory values.
370             (default: 0)
371              
372             =item check
373              
374             Check value before dumping. Valid check are 'yes', 'no' and 'skip'.
375              
376             =back
377              
378             =head1 AUTHOR
379              
380             Dominique Dumont, (ddumont at cpan dot org)
381              
382             =head1 SEE ALSO
383              
384             L<Config::Model>,L<Config::Model::Node>,L<Config::Model::Loader>
385              
386             =head1 AUTHOR
387              
388             Dominique Dumont
389              
390             =head1 COPYRIGHT AND LICENSE
391              
392             This software is Copyright (c) 2005-2022 by Dominique Dumont.
393              
394             This is free software, licensed under:
395              
396             The GNU Lesser General Public License, Version 2.1, February 1999
397              
398             =cut