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   366 use strict;
  59         118  
  59         3448  
13 59     59   314 use warnings;
  59         109  
  59         1050  
14 59     59   240  
  59         101  
  59         1409  
15             use Config::Model::Exception;
16 59     59   268 use Config::Model::ObjTreeScanner;
  59         1718  
  59         1230  
17 59     59   23514 use Config::Model::Value;
  59         156  
  59         1921  
18 59     59   37412  
  59         245  
  59         87778  
19             bless {}, shift;
20             }
21 154     154 1 457  
22             _quote( qr/[\s~"#*]/, @_ );
23             }
24              
25 3772     3772 0 12868 _quote( qr/[\s~"@*<>.=#]/, @_ );
26             }
27              
28             my ( $re, @res ) = @_;
29 4375     4375 0 11495 foreach (@res) {
30             if ( defined $_ and ( /$re/ or $_ eq '' ) ) {
31             s/"/\\"/g; # escape present quotes
32             $_ = '"' . $_ . '"'; # add my quotes
33 8147     8147   13338 }
34 8147         12314 }
35 8165 100 100     23597 return wantarray ? @res : $res[0];
      100        
36 232         571 }
37 232         599  
38             my @res = @_;
39             foreach (@res) {
40 8147 100       16703 if ( defined $_ and $_ and (/(\s|"|\*)/) ) {
41             s/"/\\"/g; # escape present quotes
42             $_ = '"' . $_ . '"'; # add my quotes
43             }
44 5103     5103 0 8462 }
45 5103         7462 return wantarray ? @res : $res[0];
46 5103 100 66     15864 }
      100        
47 165         446  
48 165         502 my $self = shift;
49              
50             my %args = @_;
51 5103 50       10993 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 261  
56             if ($full) {
57 154         440 carp "dump_tree: full_dump parameter is deprecated, please use mode => 'user'";
58 154   50     586 }
59 154   100     525  
60 154   100     489 my $check = delete $args{check} || 'yes';
61 154   100     515 if ( $check !~ /yes|no|skip/ ) {
62             croak "dump_tree: unexpected 'check' value: $check";
63 154 50       366 }
64 0         0  
65             # mode parameter is slightly different from fetch's mode
66             my $fetch_mode =
67 154   100     470 $full ? 'user'
68 154 50       1096 : $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       740 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       2312 my $compute_pad = sub {
80 0         0 my $depth = 0;
81             my $obj = shift;
82             while ( defined $obj->parent ) {
83             $depth++;
84 154   33     457 $obj = $obj->parent;
85             }
86             return ' ' x $depth;
87 4885     4885   6275 };
88 4885         6078  
89 4885         12733 my $leaf_cb = sub {
90 5979         6571 my ( $scanner, $data_r, $node, $element, $index, $value_obj ) = @_;
91 5979         13333  
92             # get value or only customized value
93 4885         10422 my $value = quote( $value_obj->fetch( mode => $fetch_mode, check => $check ) );
94 154         727 $index = id_quote($index);
95              
96             my $pad = $compute_pad->($node);
97 3737     3737   6473  
98             my $name =
99             defined $index
100 3737         9514 ? "$element:$index"
101 3734         8749 : $element;
102              
103 3734         8531 # add annotation for obj contained in hash or list
104             my $note = note_quote( $value_obj->annotation );
105 3734 100       6968 $$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         8829 };
112 3734 100 100     10795  
113 3734 100       6648 my $check_list_cb = sub {
114 1242         2522 my ( $scanner, $data_r, $node, $element, $index, $value_obj ) = @_;
115 1242         2091  
116             # get value or only customized value
117 3734 100       15929 my $value = $value_obj->fetch( mode => $fetch_mode, check => $check );
118 154         672 my $qvalue = quote($value);
119             $index = id_quote($index);
120             my $pad = $compute_pad->($node);
121 38     38   93  
122             my $name =
123             defined $index
124 38         154 ? "$element:$index"
125 38         120 : $element;
126 38         120  
127 38         102 # add annotation for obj contained in hash or list
128             my $note = note_quote( $value_obj->annotation );
129 38 50       96 $$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         141 my ( $scanner, $data_r, $node, $element, @keys ) = @_;
136 38 100 66     179  
137 38 100       108 my $pad = $compute_pad->($node);
138 38 100       522 my $list_obj = $node->fetch_element($element);
139 154         586  
140             # add annotation for list element
141             my $list_note = note_quote( $list_obj->annotation );
142 312     312   715 $$data_r .= "\n$pad$element#$list_note" if $list_note;
143              
144 312         682 if ( $list_obj->cargo_type eq 'node' ) {
145 312         808 foreach my $k (@keys) {
146             $scanner->scan_list( $data_r, $node, $element, $k );
147             }
148 312         920 }
149 312 100       633 else {
150             # write value comments
151 312 100       879 foreach my $idx ( $list_obj->fetch_all_indexes ) {
152 17         40 my $note = $list_obj->fetch_with_id($idx)->annotation;
153 34         113 $$data_r .= "\n$pad$element:$idx#" . note_quote($note) if $note;
154             }
155              
156             # skip undef values
157             my @val = id_quote(
158 295         793 grep { defined $_ }
159 383         918 $list_obj->fetch_all_values(mode => $fetch_mode, check => $check)
160 383 100       1085 );
161             $$data_r .= "\n$pad$element:=" . join( ',', @val ) if @val;
162             }
163             };
164              
165 295         878 my $hash_element_cb = sub {
  313         590  
166             my ( $scanner, $data_r, $node, $element, @keys ) = @_;
167              
168 295 100       2179 my $pad = $compute_pad->($node);
169             my $hash_obj = $node->fetch_element($element);
170 154         737  
171             # add annotation for list or hash element
172             my $note = note_quote( $hash_obj->annotation );
173 168     168   549 $$data_r .= "\n$pad$element#$note" if $note;
174              
175 168         372 # resume exploration
176 168         470 map { $scanner->scan_hash( $data_r, $node, $element, $_ ); } @keys;
177             };
178              
179 168         487 # called for nodes contained in nodes (not root).
180 168 100       408 # 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         395  
  336         1116  
184 154         561 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   1399 my $elt = $node->fetch_element($element);
190              
191 633         1631 # 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     1585 # but foo#comment:bar if not valid -> foo#commaent foo:bar
194              
195 633         1417 my $head = "\n$pad$element";
196 633         1538 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         1821 $scanner->scan_node( \$sub_data, $contained_node );
203 633         1785 $$data_r .= $head . $sub_data . ' -';
204             }
205 633 100 100     2293 else {
206 308         888 $head .= '#' . $node_note if $node_note;
207 308 100       905 my $sub_data = '';
208 308         577 $scanner->scan_node( \$sub_data, $contained_node );
209 308         1275  
210 307         2994 # skip simple nodes that do not bring data
211             $$data_r .= $head . $sub_data . ' -' if $sub_data;
212             }
213 325 100       722 };
214 325         520  
215 325         1302 my @scan_args = (
216             fallback => 'all',
217             auto_vivify => $auto_v,
218 325 100       2477 list_element_cb => $list_element_cb,
219             hash_element_cb => $hash_element_cb,
220 154         648 leaf_cb => $leaf_cb,
221             node_element_cb => $node_element_cb,
222 154         659 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         303 my $root_note = note_quote( $node->annotation );
234 154 50       397 $ret .= "\n#$root_note" if $root_note;
235             $view_scanner->scan_node( \$ret, $node );
236              
237 154         1057 substr( $ret, 0, 1, '' ); # remove leading \n
238             $ret .= ' -' if $ret;
239 154         322 return $ret . "\n";
240 154         741 }
241 154 100       362  
242 154         660 1;
243              
244 151         736 # ABSTRACT: Serialize data of config tree
245 151 100       452  
246 151         7381  
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.151
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