File Coverage

blib/lib/Config/Model/Loader.pm
Criterion Covered Total %
statement 397 442 89.8
branch 155 210 73.8
condition 85 116 73.2
subroutine 46 46 100.0
pod 1 2 50.0
total 684 816 83.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::Loader 2.153; # TRIAL
11              
12 59     59   424 use Carp;
  59         126  
  59         3453  
13 59     59   403 use strict;
  59         144  
  59         1206  
14 59     59   289 use warnings;
  59         144  
  59         1463  
15 59     59   729 use 5.10.1;
  59         227  
16 59     59   441 use Mouse;
  59         142  
  59         376  
17              
18 59     59   23082 use Config::Model::Exception;
  59         162  
  59         2119  
19 59     59   416 use Log::Log4perl qw(get_logger :levels);
  59         159  
  59         513  
20 59     59   50472 use JSON;
  59         641999  
  59         350  
21 59     59   8983 use Path::Tiny;
  59         138  
  59         2790  
22 59     59   34085 use YAML::Tiny;
  59         327653  
  59         4134  
23              
24 59     59   490 use feature qw/postderef signatures/;
  59         141  
  59         5601  
25 59     59   454 no warnings qw/experimental::postderef experimental::signatures/;
  59         164  
  59         518601  
26              
27             my $logger = get_logger("Loader");
28             my $verbose_logger = get_logger("Verbose.Loader");
29              
30             ## load stuff, similar to grab, but used to set items in the tree
31             ## starting from this node
32              
33             has start_node => (
34             is => 'ro',
35             isa => 'Config::Model::Node',
36             weak_ref => 1,
37             required => 1,
38             );
39              
40             has instance => (
41             is => 'ro',
42             isa => 'Config::Model::Instance',
43             weak_ref => 1,
44             lazy_build => 1,
45             );
46              
47             sub _build_instance {
48 30     30   249 return $_[0]->start_node->instance;
49             }
50              
51             my %log_dispatch = (
52             name => sub { my $loc = $_[0]->location; return $loc ? $_[0]->get_type." '$loc'" : "root node"},
53             qs => sub { my $s = shift; unquote($s); return "'$s'"},
54             qa => sub { return '"'.join('", "', @{$_[0]}).'"'},
55             s => sub { return $_[0] }, # nop
56             leaf => sub { return $_[0]->get_type." '". $_[0]->location."' ".$_[0]->value_type;}
57             );
58              
59             sub _log_cmd {
60 1446     1446   4360 my ($self, $cmd, $message, @params) = @_;
61              
62 1446 100       3764 return unless $verbose_logger->is_info;
63 56 100       443 return if $self->instance->initial_load;
64              
65 55         132 $cmd =~ s/\n/\\n/g;
66 55         119 foreach my $p (@params) {
67 113         567 $message =~ s/%(\w+)/$log_dispatch{$1}->($p)/e;
  113         386  
68             }
69 55 100       178 my $str = ref $cmd eq 'ARRAY' ? "@$cmd"
    50          
70             : ref $cmd ? $$cmd : $cmd;
71 55         200 $verbose_logger->info("command '$str': $message");
72             }
73              
74 427     427   42895 sub _split_string ($str) {
  427         711  
  427         606  
75             # do a split on ' ' but take quoted string into account
76             return (
77 427         5273 $str =~ m/
78             ( # begin of *one* command
79             (?: # group parts of a command (e.g ...:...=... )
80             [^\s"']+ # match anything but a space and a quote
81             (?: # begin quoted group
82             " # begin of a string
83             (?: # begin group
84             \\" # match an escaped quote
85             | # or
86             [^"] # anything but a quote
87             )* # lots of time
88             " # end of the string
89             ) # end of quoted group
90             ? # match if I got more than one group
91             (?: # begin quoted group
92             ' # begin of a string
93             (?: # begin group
94             \\' # match an escaped quote
95             | # or
96             [^'] # anything but a quote
97             )* # lots of time
98             ' # end of the string
99             ) # end of quoted group
100             ? # match if I got more than one group
101             )+ # can have several parts in one command
102             ) # end of *one* command
103             /gx # 'g' means that all commands are fed into @command array
104             ); #"asdf ;
105             }
106              
107             sub load {
108 413     413 1 805 my $self = shift;
109              
110 413         1178 my %args = @_;
111              
112 413         1219 my $node = $self->start_node;
113              
114 413   66     1616 my $steps = delete $args{steps} // delete $args{step};
115 413 50       1092 croak "load error: missing 'steps' parameter" unless defined $steps;
116              
117 413         844 my $caller_is_root = delete $args{caller_is_root};
118              
119 413 50       1013 if (delete $args{experience}) {
120 0         0 carp "load: experience parameter is deprecated";
121             }
122              
123 413         1076 my $inst = $node->instance;
124              
125             # tune value checking
126 413   100     1437 my $check = delete $args{check} || 'yes';
127 413 50       3053 croak __PACKAGE__, "load: unexpected check $check" unless $check =~ /yes|no|skip/;
128              
129             # accept commands
130 413 50       1166 my $huge_string = ref $steps ? join( ' ', @$steps ) : $steps;
131              
132 413         1129 my @command = _split_string($huge_string);
133              
134             #print "command is ",join('+',@command),"\n" ;
135              
136 413         981 my $current_node = $node;
137 413         654 my $ret;
138 413         663 do {
139 425         1587 $ret = $self->_load( $current_node, $check, \@command, 1 );
140 410         1666 $logger->trace("_load returned $ret");
141              
142             # found '!' command
143 410 100       4128 if ( $ret eq 'root' ) {
144 12 100       181 $current_node = $caller_is_root ? $node : $current_node->root;
145 12 50       48 if ($logger->debug) {
146 0         0 $logger->debug("Setting current_node to root node: ".$current_node->name);
147             }
148             }
149             } while ( $ret eq 'root' );
150              
151 398 100       1056 if (@command) {
152 3         7 my $str = "Error: could not execute the required command, ";
153 3 50       14 if ($command[0] =~ m!^/([\w-]+)!) {
154 0         0 $str .= "the searched item '$1' was not found" ;
155             }
156             else {
157 3         10 $str .= "you may have specified too many '-' in your command";
158             }
159 3 100       18 Config::Model::Exception::Load->throw(
160             command => $command[0],
161             error => $str,
162             object => $node
163             ) if $check eq 'yes';
164             }
165              
166 397 50       947 if (%args) {
167 0         0 Config::Model::Exception::Internal->throw(
168             error => __PACKAGE__ . " load: unexpected parameters: " . join( ', ', keys %args ) );
169             }
170              
171 397         4918 return $ret;
172             }
173              
174             # returns elt action id subaction value
175             sub _split_cmd {
176 1311     1311   119601 my $cmd = shift;
177 1311         4955 $logger->trace("split on: ->$cmd<-");
178              
179 1311         12327 my $quoted_string = qr/(?:"(?: \\" | [^"] )* ")|(?:'(?: \\' | [^'] )* ')/x; # quoted string
180              
181             # do a split on ' ' but take quoted string into account
182 1311         36473 my @command = (
183             $cmd =~ m!^
184             (\w[\w-]*)? # element name can be alone
185             (?:
186             (:~|:-[=~]?|:=~|:\.\w+|:[=<>@]?|~) # action
187             (?:
188             (?: \( ( $quoted_string | [^)]+ ) \) ) # capture parameters between ( )
189             | (
190             /[^/]+/ # regexp
191             | (?:
192             $quoted_string (?:,)?
193             | [^#=\.<>]+ # non action chars
194             )+
195             )
196             )?
197             )?
198             (?:
199             (=~|\.=|=\.\w+|[=<>]) # apply regexp or assign or append
200             (?:
201             (?: \( ( $quoted_string | [^)]+ ) \) ) # capture parameters between ( )
202             | (
203             (?:
204             $quoted_string
205             | [^#\s"'] # or non whitespace
206             )+ # many
207             )
208             )?
209             )?
210             (?:
211             \# # optional annotation
212             (
213             (?:
214             $quoted_string
215             | [^\s] # or non whitespace
216             )+ # many
217             )
218             )?
219             (.*) # leftover
220             !gx
221             );
222              
223 1311         3852 my $leftout = pop @command;
224              
225 1311 100       2931 if ($leftout) {
226 2         16 Config::Model::Exception::Load->throw(
227             command => $cmd,
228             error => "Syntax error: spurious char at command end: '$leftout'. Did you forget double quotes ?"
229             );
230             }
231 1309         7686 return @command;
232             }
233              
234             my %load_dispatch = (
235             node => \&_walk_node,
236             warped_node => \&_walk_node,
237             hash => \&_load_hash,
238             check_list => \&_load_check_list,
239             list => \&_load_list,
240             leaf => \&_load_leaf,
241             );
242              
243             # return 'done', 'root', 'up', 'error'
244             sub _load {
245 633     633   1660 my ( $self, $node, $check, $cmdref, $at_top_level ) = @_;
246 633   100     1920 $at_top_level ||= 0;
247 633         1920 my $node_name = "'" . $node->name . "'";
248 633         2890 $logger->trace("_load: called on node $node_name");
249              
250 633         6292 my $inst = $node->instance;
251              
252 633         962 my $cmd;
253 633         3464 while ( $cmd = shift @$cmdref ) {
254 1415 100       3444 if ( $logger->is_debug ) {
255 50         185 my $msg = $cmd;
256 50         122 $msg =~ s/\n/\\n/g;
257 50         174 $logger->debug("Loader: Executing cmd '$msg' on node $node_name");
258             }
259              
260 1415 50       12594 next if $cmd =~ /^\s*$/;
261              
262 1415 100       3321 if ( $cmd eq '!' ) {
263 23         119 $self->_log_cmd(\$cmd,"Going from %name to root node", $node );
264 23         306 $logger->debug("_load: going to root, at_top_level is $at_top_level");
265              
266             # Do not change current node as we don't want to mess up =~ commands
267 23         240 return 'root';
268             }
269              
270 1392 100       2896 if ( $cmd eq '-' ) {
271 145         518 my $parent = $node->parent;
272 145 100       454 if (defined $parent) {
273 139         519 $self->_log_cmd($cmd,'Going up from %name to %name', $node, $node->parent);
274             }
275             else {
276 6         56 $self->_log_cmd($cmd,'Going up from %name to exit Loader.', $node);
277             }
278 145         1410 return 'up';
279             }
280              
281 1247 100       2877 if ( $cmd =~ m!^/([\w-]+)! ) {
282 5         17 my $search = $1;
283 5 100       21 if ($node->has_element($search)) {
284 3         20 $self->_log_cmd($cmd, 'Element %qs found in current node (%name).', $search, $node);
285 3         110 $cmd =~ s!^/!! ;
286             } else {
287 2         115 $self->_log_cmd(
288             $cmd,
289             'Going up from %name to %name to search for element %qs.',
290             $node, $node->parent, $search
291             );
292 2         61 unshift @$cmdref, $cmd;
293 2         11 return 'up';
294             }
295             }
296              
297 1245         2778 my ( $element_name, $action, $function_param, $id, $subaction, $value_function_param2, $value_param, $note ) =
298             _split_cmd($cmd);
299              
300             # regexp ensure that only $value_function_param $value_param is set
301 1243   100     4958 my $value = $value_function_param2 // $value_param ;
302 1243         3503 my @instructions = ( $element_name, $action, $function_param, $id, $subaction, $value, $note );
303              
304 1243 100       3336 if ( $logger->is_debug ) {
305 45 100       191 my @disp = map { defined $_ ? "'$_'" : '<undef>' } @instructions;
  315         691  
306 45         209 $logger->debug("_load instructions: @disp (from: $cmd)");
307             }
308              
309 1243 50 66     8328 if ( not defined $element_name and not defined $note ) {
310 0         0 Config::Model::Exception::Load->throw(
311             command => $cmd,
312             error => 'Syntax error: cannot find element in command'
313             );
314             }
315              
316 1243 50       2497 unless ( defined $node ) {
317 0         0 Config::Model::Exception::Load->throw(
318             command => $cmd,
319             error => "Error: Got undefined node"
320             );
321             }
322              
323 1243 50 66     5234 unless ( $node->isa("Config::Model::Node")
324             or $node->isa("Config::Model::WarpedNode") ) {
325 0         0 Config::Model::Exception::Load->throw(
326             command => $cmd,
327             error => "Error: Expected a node (even a warped node), got '" . $node->name . "'"
328             );
329              
330             # below, has_element method from WarpedNode will raise
331             # exception if warped_node is not available
332             }
333              
334 1243 100 66     3225 if ( not defined $element_name and defined $note ) {
335 4         21 $self->_set_note($node, \$cmd, $note);
336 4         16 next;
337             }
338              
339 1239 100       3620 unless ( $node->has_element($element_name) ) {
340 6 100       81 Config::Model::Exception::UnknownElement->throw(
341             object => $node,
342             element => $element_name,
343             ) if $check eq 'yes';
344 2         8 unshift @$cmdref, $cmd;
345 2         8 return 'error';
346             }
347              
348 1233 100       3788 unless ( $node->is_element_available( name => $element_name ) ) {
349 1 50       24 Config::Model::Exception::UnavailableElement->throw(
350             object => $node,
351             element => $element_name
352             ) if $check eq 'yes';
353 0         0 unshift @$cmdref, $cmd;
354 0         0 return 'error';
355             }
356              
357 1232 50       3200 unless ( $node->is_element_available( name => $element_name ) ) {
358 0 0       0 Config::Model::Exception::RestrictedElement->throw(
359             object => $node,
360             element => $element_name,
361             ) if $check eq 'yes';
362 0         0 unshift @$cmdref, $cmd;
363 0         0 return 'error';
364             }
365              
366 1232         3546 my $element_type = $node->element_type($element_name);
367              
368 1232         3024 my $method = $load_dispatch{$element_type};
369              
370 1232 50       2462 croak "_load: unexpected element type '$element_type' for $element_name"
371             unless defined $method;
372              
373 1232         5812 $logger->debug("_load: calling $element_type loader on element $element_name");
374              
375 1232         12589 my $ret = $self->$method( $node, $check, \@instructions, $cmdref, $cmd );
376 1222         5779 $logger->debug("_load: $element_type loader on element $element_name returned $ret");
377 1222 50       10630 die "Internal error: method dispatched for $element_type returned an undefined value "
378             unless defined $ret;
379              
380 1222 100 66     4896 if ( $ret eq 'error' or $ret eq 'done' ) {
381 60         348 $logger->debug("_load return: $node_name got $ret");
382 60         585 return $ret;
383             }
384 1162 50 66     5951 if ( $ret eq 'root' and not $at_top_level ) {
385 0         0 $logger->debug("_load return: $node_name got $ret");
386 0         0 return 'root';
387             }
388              
389             # ret eq up or ok -> go on with the loop
390             }
391              
392 384         1323 return 'done';
393             }
394              
395             sub _set_note {
396 49     49   122 my ($self, $target, $cmd, $note) = @_;
397 49         158 $self->_log_cmd($cmd, "Setting %name annotation to %qs", $target, $note);
398 49         568 $target->annotation($note);
399             }
400              
401              
402             sub _load_note {
403 1180     1180   2787 my ( $self, $target_obj, $note, $instructions, $cmdref, $cmd ) = @_;
404              
405 1180         2806 unquote($note);
406              
407             # apply note on target object
408 1180 100       3068 if ( defined $note ) {
409 45 50       98 if ( defined $target_obj ) {
410 45         130 $self->_set_note($target_obj, $cmd,$note);
411             }
412             else {
413             Config::Model::Exception::Load->throw(
414             command => $$cmdref,
415             error => "Error: cannot set annotation with '"
416 0         0 . join( "','", grep { defined $_ } @$instructions ) . "'"
  0         0  
417             );
418             }
419             }
420             }
421              
422             sub _walk_node {
423 30     30   129 my ( $self, $node, $check, $inst, $cmdref, $cmd ) = @_;
424              
425 30         72 my $element_name = shift @$inst;
426 30         66 my $note = pop @$inst;
427 30         86 my $new_node = $node->fetch_element($element_name);
428 30         128 $self->_load_note( $new_node, $note, $inst, $cmdref, $cmd );
429              
430 30         92 my @left = grep { defined $_ } @$inst;
  150         301  
431 30 50       99 if (@left) {
432 0         0 Config::Model::Exception::Load->throw(
433             command => $inst,
434             object => $node,
435             error => "Don't know what to do with '@left' "
436             . "for node element $element_name"
437             );
438             }
439              
440 30         122 $self->_log_cmd($cmd, 'Going down from %name to %name', $node, $new_node);
441              
442 30         541 return $self->_load( $new_node, $check, $cmdref );
443             }
444              
445             sub unquote {
446 2409     2409 0 5189 for (@_) {
447 3867 100       7989 if (defined $_) {
448 1372         3048 s/(?<!\\)\\n/\n/g;
449 1372         2208 s/\\\\/\\/g;
450 1372 100 100     4926 s/^"// && s/"$// && s!\\"!"!g;
451             }
452             }
453             }
454              
455             sub _load_check_list {
456 17     17   114 my ( $self, $node, $check, $inst, $cmdref, $cmd ) = @_;
457 17         93 my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note ) = @$inst;
458              
459 17         72 my $element = $node->fetch_element( name => $element_name, check => $check );
460              
461 17 0 33     150 if ( defined $note and not defined $action and not defined $subaction ) {
      33        
462 0         0 $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
463 0         0 return 'ok';
464             }
465              
466 17 50 33     153 if ( defined $subaction and $subaction eq '=' ) {
467 17         153 $logger->debug("_load_check_list: set whole list");
468              
469 17         230 $self->_log_cmd($cmd, 'Setting %name items %qs.', $element, $value);
470             # valid for check_list or list
471 17         305 $element->load( $value, check => $check );
472 17         401 $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
473 17         57 return 'ok';
474             }
475              
476 0 0 0     0 if ( not defined $action and defined $subaction ) {
477             Config::Model::Exception::Load->throw(
478             object => $element,
479 0         0 command => join( '', grep { defined $_} @$inst ),
  0         0  
480             error => "Wrong assignment with '$subaction' on check_list"
481             );
482             }
483              
484 0 0       0 my $a_str = defined $action ? $action : '<undef>';
485              
486             Config::Model::Exception::Load->throw(
487             object => $element,
488 0 0       0 command => join( '', map { $_ || '' } @$inst ),
  0         0  
489             error => "Wrong assignment with '$a_str' on check_list"
490             );
491              
492             }
493              
494             {
495             # sub is called with ( $self, $element, $check, $instance, @function_args )
496             # function_args are the arguments passed to the load command
497             my %dispatch_action = (
498             list_leaf => {
499             ':.sort' => sub { $_[1]->sort; return 'ok';},
500             ':.push' => sub { $_[1]->push( @_[ 5 .. $#_ ] ); return 'ok'; },
501             ':.unshift' => sub { $_[1]->unshift( @_[ 5 .. $#_ ] ); return 'ok'; },
502             ':.insert_at' => sub { $_[1]->insert_at( @_[ 5 .. $#_ ] ); return 'ok'; },
503             ':.insort' => sub { $_[1]->insort( @_[ 5 .. $#_ ] ); return 'ok'; },
504             ':.insert_before' => \&_insert_before,
505             ':.ensure' => \&_ensure_list_value,
506             },
507             'list_*' => {
508             ':.copy' => sub { $_[1]->copy( $_[5], $_[6] ); return 'ok'; },
509             ':.clear' => sub { $_[1]->clear; return 'ok'; },
510             },
511             hash_leaf => {
512             ':.insort' => sub { $_[1]->insort($_[5])->store($_[6]); return 'ok'; },
513             },
514             hash_node => => {
515             ':.insort' => \&_insort_hash_of_node,
516             },
517             'hash_*' => {
518             ':.sort' => sub { $_[1]->sort; return 'ok'; },
519             ':.copy' => sub { $_[1]->copy( $_[5], $_[6] ); return 'ok'; },
520             ':.clear' => sub { $_[1]->clear; return 'ok';},
521             },
522             # part of list or hash. leaf element have their own dispatch table
523             # (%load_value_dispatch) because the signture of the sub ref are
524             # different between the 2 dispatch tables.
525             leaf => {
526             ':.rm_value' => \&_remove_by_value,
527             ':.rm_match' => \&_remove_matched_value,
528             ':.substitute' => \&_substitute_value,
529             },
530             fallback => {
531             ':.rm' => \&_remove_by_id,
532             ':.json' => \&_load_json_vector_data,
533             }
534             );
535              
536             my %equiv = (
537             'hash_*' => { qw/:@ :.sort/},
538             list_leaf => { qw/:@ :.sort :< :.push :> :.unshift/ },
539             # fix for cme gh#2
540             leaf => { qw/:-= :.rm_value :-~ :.rm_match :=~ :.substitute/ },
541             fallback => { qw/:- :.rm ~ :.rm/ },
542             );
543              
544             while ( my ($target, $sub_equiv) = each %equiv) {
545             while ( my ($new_action, $existing_action) = each %$sub_equiv) {
546             $dispatch_action{$target}{$new_action} = $dispatch_action{$target}{$existing_action};
547             }
548             }
549              
550             sub _get_dispatch_data {
551 90     90   212 my ($dispatch, $type, $cargo_type, $action) = @_;
552             return $dispatch->{ $type.'_'.$cargo_type }{$action}
553             || $dispatch->{$type.'_*'}{$action}
554             || $dispatch->{$cargo_type}{$action}
555 90   66     844 || $dispatch->{'fallback'}{$action};
556             }
557              
558             sub _get_dispatch {
559 336     336   1064 my ($self, $element, $type, $cargo_type, $action, $cmd, @f_args, ) = @_;
560 336 100 100     1936 return unless (defined $action and $action ne ':');
561              
562 45         150 my $dispatch = _get_dispatch_data(\%dispatch_action, $type => $cargo_type, $action);
563 45 50       116 if ($dispatch) {
564 45   66     115 my $real_action = _get_dispatch_data(\%equiv, $type => $cargo_type, $action) // $action;
565 45         228 $self->_log_cmd($cmd, 'Running %qs on %name with %qa.', substr($real_action,2), $element, \@f_args);
566             }
567 45         630 return $dispatch;
568             }
569             }
570              
571             sub _insert_before {
572 3     3   11 my ( $self, $element, $check, $inst, $cmdref, $before_str, @values ) = @_;
573 3 100 66     74 my $before = ($before_str =~ s!^/!! and $before_str =~ s!/$!!) ? qr/$before_str/ : $before_str;
574 3         18 $element->insert_before( $before, @values );
575 3         18 return 'ok';
576             }
577              
578             sub _ensure_list_value {
579 3     3   18 my ( $self, $element, $check, $inst, $cmdref, @values ) = @_;
580 3         13 my %content = map { $_ => 1 } $element->fetch_all_values;
  13         32  
581 3         13 foreach my $one_value (@values) {
582 6 100       27 next if $content{$one_value};
583 3         14 $element->insort($one_value);
584 3         10 $content{$one_value} = 1;
585             }
586              
587 3         19 return 'ok';
588             }
589             sub _remove_by_id {
590 11     11   52 my ( $self, $element, $check, $inst, $cmdref, $id ) = @_;
591 11         57 $logger->debug("_remove_by_id: removing id '$id'");
592 11         148 $element->remove($id);
593 10         97 return 'ok';
594             }
595              
596 3     3   7 sub __load_json_file ($file) {
  3         4  
  3         7  
597             # utf8 decode is done by JSON module, so slurp_raw must be used
598 3         12 return decode_json($file->slurp_raw);
599             }
600              
601             sub _load_json_vector_data {
602 1     1   6 my ( $self, $element, $check, $inst, $cmdref, $vector ) = @_;
603 1         6 $logger->debug("_load_json_vector_data: loading '$vector'");
604 1         11 my ($file, @vector) = $self->__get_file_from_vector($element,$inst,$vector);
605              
606 1         5 my $data = __load_json_file($file);
607              
608             # test for diff before clobbering ? What about deep data ???
609 1         188 $element->load_data(
610             data => __data_from_vector($data, @vector),
611             check => $check
612             );
613 1         8 return 'ok';
614             }
615              
616             sub _remove_by_value {
617 1     1   4 my ( $self, $element, $check, $inst, $cmdref, $rm_val ) = @_;
618              
619 1         6 $logger->debug("_remove_by_value value $rm_val");
620 1         18 foreach my $idx ( $element->fetch_all_indexes ) {
621 5         16 my $v = $element->fetch_with_id($idx)->fetch;
622 5 100 66     38 $element->delete($idx) if defined $v and $v eq $rm_val;
623             }
624              
625 1         5 return 'ok';
626             }
627              
628             sub _remove_matched_value {
629 1     1   7 my ( $self, $element, $check, $inst, $cmdref, $rm_val ) = @_;
630              
631 1         8 $logger->debug("_remove_matched_value $rm_val");
632              
633 1         13 $rm_val =~ s!^/|/$!!g;
634              
635 1         7 foreach my $idx ( $element->fetch_all_indexes ) {
636 5         23 my $v = $element->fetch_with_id($idx)->fetch;
637 5 100 100     53 $element->delete($idx) if defined $v and $v =~ /$rm_val/;
638             }
639              
640 1         6 return 'ok';
641             }
642              
643             sub _substitute_value {
644 2     2   6 my ( $self, $element, $check, $inst, $cmdref, $s_val ) = @_;
645              
646 2         9 $logger->debug("_substitute_value $s_val");
647              
648 2         20 foreach my $idx ( $element->fetch_all_indexes ) {
649 8         32 my $l = $element->fetch_with_id($idx);
650 8         25 $self->_load_value( $l, $check, '=~', $s_val, $inst );
651             }
652              
653 2         20 return 'ok';
654             }
655              
656             sub _insort_hash_of_node {
657 1     1   8 my ( $self, $element, $check, $inst, $cmdref, $id ) = @_;
658 1         5 my $node = $element->insort($_[5]);
659 1         7 $logger->debug("_insort_hash_of_node: calling _load on node id $id");
660 1         11 return $self->_load( $node, $check, $cmdref );
661             }
662              
663             sub _load_list {
664 176     176   543 my ( $self, $node, $check, $inst, $cmdref, $cmd ) = @_;
665 176         535 my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note ) = @$inst;
666              
667 176         444 my $element = $node->fetch_element( name => $element_name, check => $check );
668              
669 176   100     1722 my @f_args = grep { defined } ( ( $f_arg // $id // '' ) =~ /([^,"]+)|"([^"]+)"/g );
  430   100     1045  
670              
671 176         554 my $elt_type = $node->element_type($element_name);
672 176         665 my $cargo_type = $element->cargo_type;
673              
674 176 100 100     651 if ( defined $note and not defined $action and not defined $subaction ) {
      66        
675 3         21 $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
676 3         11 return 'ok';
677             }
678              
679 173 100 100     969 if ( defined $action and $action eq ':=' and $cargo_type eq 'leaf' ) {
      66        
680             # due to ':=' action, the value is contained in $id
681 35         139 $logger->debug("_load_list: set whole list with ':=' action");
682             # valid for check_list or list
683 35         344 $self->_log_cmd($cmd, 'Setting %name values to %qs.', $element, $id);
684 35         439 $element->load( $id, check => $check );
685 33         156 $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
686 33         123 return 'ok';
687             }
688              
689             # compat mode for list=a,b,c,d commands
690 138 50 100     808 if ( not defined $action
      66        
      66        
691             and defined $subaction
692             and $subaction eq '='
693             and $cargo_type eq 'leaf' ) {
694 33         147 $logger->debug("_load_list: set whole list with '=' subaction'");
695              
696             # valid for check_list or list
697 33         355 $self->_log_cmd($cmd, 'Setting %name values to %qs.', $element, $value);
698 33         375 $element->load( $value, check => $check );
699 33         194 $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
700 33         108 return 'ok';
701             }
702              
703 105         396 unquote( $id, $value, $note );
704              
705 105 100       435 if ( my $dispatch = $self->_get_dispatch($element, list => $cargo_type, $action, $cmd, @f_args)) {
706 31         129 return $dispatch->( $self, $element, $check, $inst, $cmdref, @f_args );
707             }
708              
709 74 50 66     288 if ( not defined $action and defined $subaction ) {
710             Config::Model::Exception::Load->throw(
711             object => $element,
712 0         0 command => join( '', grep { defined $_} @$inst ),
  0         0  
713             error => "Wrong assignment with '$subaction' on "
714             . "element type: $elt_type, cargo_type: $cargo_type"
715             );
716             }
717              
718 74 100 66     324 if ( defined $action and $action eq ':' ) {
719 73         187 unquote($id);
720 73         291 my $obj = $element->fetch_with_id( index => $id, check => $check );
721 73         304 $self->_load_note( $obj, $note, $inst, $cmdref, $cmd );
722              
723 73 100       460 if ( $cargo_type =~ /node/ ) {
724              
725             # remove possible leading or trailing quote
726 38         168 $self->_log_cmd($cmd,'Going down from %name to %name', $node, $obj );
727 38         441 return $self->_load( $obj, $check, $cmdref );
728             }
729              
730 35 100       109 return 'ok' unless defined $subaction;
731              
732 32 50       122 if ( $cargo_type =~ /leaf/ ) {
733 32         167 $logger->debug("_load_list: calling _load_value on $cargo_type id $id");
734             # _log_cmd done in _load_value
735 32 50       401 $self->_load_value( $obj, $check, $subaction, $value, $cmdref, $cmd )
736             and return 'ok';
737             }
738             }
739              
740 1 50       5 my $a_str = defined $action ? $action : '<undef>';
741              
742             Config::Model::Exception::Load->throw(
743             object => $element,
744 1 100       6 command => join( '', map { $_ || '' } @$inst ),
  7         32  
745             error => "Wrong assignment with '$a_str' on "
746             . "element type: $elt_type, cargo_type: $cargo_type"
747             );
748              
749             }
750              
751             sub _load_hash {
752 239     239   743 my ( $self, $node, $check, $inst, $cmdref, $cmd ) = @_;
753 239         729 my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note ) = @$inst;
754              
755 239         792 unquote( $id, $value, $note );
756              
757 239         753 my $element = $node->fetch_element( name => $element_name, check => $check );
758 239         1035 my $cargo_type = $element->cargo_type;
759              
760 239 100 100     852 if ( defined $note and not defined $action ) {
761             # _log_cmd done in _load_note
762 4         30 $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
763 4         16 return 'ok';
764             }
765              
766 235 100       699 if ( not defined $action ) {
767             Config::Model::Exception::Load->throw(
768             object => $element,
769 1 100       5 command => join( '', map { $_ || '' } @$inst ),
  7         29  
770             error => "Missing key (e.g. '$element_name:some_key') on hash element, cargo_type: $cargo_type"
771             );
772             }
773              
774             # loop requires $subaction so does not fit in the dispatch table
775 234 100 66     1009 if ( $action eq ':~' or $action eq ':.foreach_match' ) {
776 3         34 my @keys = $element->fetch_all_indexes;
777 3         10 my $ret = 'ok';
778 3   66     15 my $pattern = $id // $f_arg;
779 3 100       25 $pattern =~ s!^/|/$!!g if $pattern;
780 3 100       13 my @loop_on = $pattern ? grep { /$pattern/ } @keys : @keys;
  10         121  
781 3 100       16 if ($logger->is_debug) {
782 1 50       9 my $str = $pattern ? " with regex /$pattern/" : '';
783 1         7 $logger->debug("_load_hash: looping$str on keys @loop_on");
784             }
785              
786 3         38 my @saved_cmd = @$cmdref;
787 3         9 foreach my $loop_id ( @loop_on ) {
788 11         33 @$cmdref = @saved_cmd; # restore command before loop
789 11         34 my $sub_elt = $element->fetch_with_id($loop_id);
790 11         46 $self->_log_cmd($cmd,'Running foreach_map loop on %name.',$sub_elt);
791 11 100       191 if ( $cargo_type =~ /node/ ) {
    50          
792             # remove possible leading or trailing quote
793 9         23 $ret = $self->_load( $sub_elt, $check, $cmdref );
794             }
795             elsif ( $cargo_type =~ /leaf/ ) {
796 2         6 $ret = $self->_load_value( $sub_elt, $check, $subaction, $value, $cmdref, $cmd );
797             }
798             else {
799 0         0 Config::Model::Exception::Load->throw(
800             object => $element,
801             command => join( '', @$inst ),
802             error => "Hash assignment with '$action' on unexpected "
803             . "cargo_type: $cargo_type"
804             );
805             }
806              
807 11         74 $logger->debug("_load_hash: loop on id $loop_id returned $ret (left cmd: @$cmdref)");
808 11 50       121 if ( $ret eq 'error') { return $ret; }
  0         0  
809             }
810 3         15 return $ret;
811             }
812              
813 231   100     1918 my @f_args = grep { defined } ( ( $f_arg // $id // '' ) =~ /([^,"]+)|"([^"]+)"/g );
  462   100     1370  
814              
815 231 100       972 if ( my $dispatch = $self->_get_dispatch($element, hash => $cargo_type, $action, $cmd, @f_args)) {
816 14         98 return $dispatch->( $self, $element, $check, $inst, $cmdref, @f_args );
817             }
818              
819 217 50       651 if (not defined $id) {
820 0         0 Config::Model::Exception::Load->throw(
821             object => $element,
822             command => join( '', @$inst ),
823             error => qq!Unexpected hash instruction: '$action' or missing id!
824             );
825             }
826              
827 217         896 my $obj = $element->fetch_with_id( index => $id, check => $check );
828 217         876 $self->_load_note( $obj, $note, $inst, $cmdref, $cmd );
829              
830 217 100 66     1982 if ( $action eq ':' and $cargo_type =~ /node/ ) {
    100 66        
    50 66        
    0          
831              
832             # remove possible leading or trailing quote
833 130         528 $self->_log_cmd($cmd,'Going down from %name to %name', $node, $obj );
834 130 50       1200 if ( defined $subaction ) {
835 0         0 Config::Model::Exception::Load->throw(
836             object => $element,
837             command => join( '', @$inst ),
838             error => qq!Hash assignment with '$action"$id"$subaction"$value"' on unexpected !
839             . "cargo_type: $cargo_type"
840             );
841             }
842 130         653 return $self->_load( $obj, $check, $cmdref );
843             }
844             elsif ( $action eq ':' and defined $subaction and $cargo_type =~ /leaf/ ) {
845             # _log_cmd is done in _load_value
846 83         432 $logger->debug("_load_hash: calling _load_value on leaf $id");
847 83 50       969 $self->_load_value( $obj, $check, $subaction, $value, $cmdref, $cmd )
848             and return 'ok';
849             }
850             elsif ( $action eq ':' ) {
851 4         20 $self->_log_cmd($cmd,'Creating empty %name.', $obj );
852 4         100 $logger->debug("_load_hash: created empty element of type $cargo_type");
853 4         47 return 'ok';
854             }
855             elsif ($action) {
856 0         0 $logger->debug("_load_hash: giving up");
857             Config::Model::Exception::Load->throw(
858             object => $element,
859 0         0 command => join( '', grep { defined $_ } @$inst ),
  0         0  
860             error => "Hash load with '$action' on unexpected " . "cargo_type: $cargo_type"
861             );
862             }
863             }
864              
865             sub _load_leaf {
866 770     770   2152 my ( $self, $node, $check, $inst, $cmdref, $cmd ) = @_;
867 770         2153 my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note ) = @$inst;
868              
869 770         2135 unquote( $id, $value );
870              
871 770         2162 my $element = $node->fetch_element( name => $element_name, check => $check );
872 770         2715 $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
873              
874 770 100 66     3734 if ( defined $action and $element->isa('Config::Model::Value')) {
875 5 100       25 if ($action eq '~') {
    50          
876 4         24 $self->_log_cmd($cmd, "Deleting %name.", $element );
877 4         81 $element->store(value => undef, check => $check);
878             }
879             elsif ($action eq ':') {
880 1         6 Config::Model::Exception::Load->throw(
881             object => $element,
882             command => $inst,
883             error => "Error: list or hash command (':') detected on a leaf."
884             . "(element '" . $element->name . "')"
885             );
886             }
887             else {
888 0         0 Config::Model::Exception::Load->throw(
889             object => $element,
890             command => $inst,
891             error => "Load error on leaf with "
892             . "'$element_name$action$id' command "
893             . "(element '" . $element->name . "')"
894             );
895             }
896             }
897              
898 769 100       1710 return 'ok' unless defined $subaction;
899              
900 759 100       1944 if ( $logger->is_debug ) {
901 20 50       86 my $msg = defined $value ? $value : '<undef>';
902 20         41 $msg =~ s/\n/\\n/g;
903 20         79 $logger->debug("_load_leaf: action '$subaction' value '$msg'");
904             }
905              
906 759         5599 my $res = $self->_load_value( $element, $check, $subaction, $value, $inst, $cmd );
907              
908 757 50       2690 return $res if $res ;
909              
910 0         0 Config::Model::Exception::Load->throw(
911             object => $element,
912             command => $inst,
913             error => "Load error on leaf with "
914             . "'$element_name$subaction$value' command "
915             . "(element '"
916             . $element->name . "')"
917             );
918             }
919              
920             # sub is called with ( $self, $element, $value, $check, $instructions )
921             # function_args are the arguments passed to the load command
922             my %load_value_dispatch = (
923             '=' => \&_store_value ,
924             '.=' => \&_append_value,
925             '=~' => \&_apply_regexp_on_value,
926             '=.file' => \&_store_file_in_value,
927             '=.json' => \&_store_json_vector_in_value,
928             '=.yaml' => \&_store_yaml_vector_in_value,
929             '=.env' => sub { $_[1]->store( value => $ENV{$_[2]}, check => $_[3] ); return 'ok'; },
930             );
931              
932             sub _store_value {
933 856     856   2128 my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
934 856         2542 $self->_log_cmd($cmd, 'Setting %leaf to %qs.', $element, $value);
935 856         7890 $element->store( value => $value, check => $check );
936 856         3364 return 'ok';
937             }
938              
939             sub _append_value {
940 8     8   29 my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
941 8         29 my $orig = $element->fetch( check => $check );
942 8         39 my $next = $orig.$value;
943 8         41 $self->_log_cmd(
944             $cmd, 'Appending %qs to %leaf. Result is %qs.',
945             $value, $element, $next
946             );
947 8         111 $element->store( value => $next, check => $check );
948             }
949              
950             sub _apply_regexp_on_value {
951 13     13   42 my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
952              
953 13         45 my $orig = $element->fetch( check => $check );
954 13 100       35 if (defined $orig) {
955             # $value may change at each run and is like s/foo/bar/ do block
956             # eval is not possible
957 11         1030 eval("\$orig =~ $value;"); ## no critic (ProhibitStringyEval)
958 11         62 my $res = $@;
959 11         46 $self->_log_cmd(
960             $cmd, "Applying regexp %qs to %leaf. Result is %qs.",
961             $value, $element, $orig
962             );
963 11 50       217 if ($res) {
964 0         0 Config::Model::Exception::Load->throw(
965             object => $element,
966             command => $instructions,
967             error => "Failed regexp '$value' on " . "element '"
968             . $element->name . "' : $res"
969             );
970             }
971 11         42 $element->store( value => $orig, check => $check );
972             }
973             else {
974 2         13 $self->_log_cmd(
975             $cmd, "Not applying regexp %qs on undefined value of %leaf.",
976             $value, $element, $orig
977             );
978             }
979             }
980              
981             sub _store_file_in_value {
982 1     1   4 my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
983              
984 1 50       6 if ($value eq '-') {
985 0         0 $element->store( value => join('',<STDIN>), check => $check );
986 0         0 return 'ok';
987             }
988              
989 1         10 my $path = $element->root_path->child($value);
990 1 50       138 if ($path->is_file) {
991 1         55 $element->store( value => $path->slurp_utf8, check => $check );
992             }
993             else {
994 0         0 Config::Model::Exception::Load->throw(
995             object => $element,
996             command => $instructions,
997             error => "cannot read file $value"
998             );
999             }
1000             }
1001              
1002             sub __data_from_vector {
1003 4     4   16 my ($data, @vector) = @_;
1004 4         12 for my $step (@vector) {
1005 7 100       34 $data = (ref($data) eq 'HASH') ? $data->{$step} : $data->[$step];
1006             }
1007 4         32 return $data;
1008             }
1009              
1010             sub __get_file_from_vector {
1011 6     6   13 my ($self, $element,$instructions,$raw_vector) = @_;
1012 6         41 my @vector = split m![/]+!m, $raw_vector;
1013 6         24 my $cur = path('.');
1014 6         243 my $file;
1015 6         21 while (my $subpath = shift @vector) {
1016 22         226 my $new_path = $cur->child($subpath);
1017 22 100       841 if ($new_path->is_file) {
    100          
1018 4         75 $file = $new_path;
1019 4         12 last;
1020             }
1021             elsif ($new_path->is_dir) {
1022 12         426 $cur = $new_path;
1023             }
1024             }
1025 6 100       73 if (not defined $file) {
1026 2         7 my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note )
1027             = @$instructions;
1028 2 50       30 Config::Model::Exception::Load->throw(
    50          
1029             object => $element,
1030             command => "$element_name"
1031             . ( $action ? "$action($f_arg)" : '' )
1032             . ( $subaction ? "$subaction($value)" : '' ),
1033             error => qq!Load error: Cannot find file in $value!
1034             );
1035             }
1036 4         23 return ($file, @vector);
1037             }
1038              
1039             sub _store_json_vector_in_value {
1040 4     4   10 my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
1041 4         11 my ($file, @vector) = $self->__get_file_from_vector($element,$instructions,$value);
1042 2         8 my $data = __load_json_file($file);
1043 2         1078 $element->store(
1044             value => __data_from_vector($data, @vector),
1045             check => $check
1046             );
1047             }
1048              
1049             sub _store_yaml_vector_in_value {
1050 1     1   4 my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
1051 1         4 my ($file, @vector) = $self->__get_file_from_vector($element,$instructions,$value);
1052 1         23 my $data = YAML::Tiny->read($file->stringify);
1053 1         1873 $element->store(
1054             value => __data_from_vector($data, @vector),
1055             check => $check
1056             );
1057             }
1058              
1059             sub _load_value {
1060 884     884   2448 my ( $self, $element, $check, $subaction, $value, $instructions, $cmd ) = @_;
1061              
1062 884 50       3634 if (not $element->isa('Config::Model::Value')) {
1063 0         0 my $class = ref($element);
1064 0         0 Config::Model::Exception::Load->throw(
1065             object => $element,
1066             command => $instructions,
1067             error => "Load error: _load_value called on non Value object. ($class)"
1068             );
1069             }
1070              
1071 884         3843 $logger->debug("_load_value: action '$subaction' value '$value' check $check");
1072 884         7180 my $dispatch = $load_value_dispatch{$subaction};
1073 884 50       1950 if ($dispatch) {
1074 884         2311 return $dispatch->( $self, $element, $value, $check, $instructions, $cmd );
1075             }
1076             else {
1077 0           Config::Model::Exception::Load->throw(
1078             object => $element,
1079             command => $instructions,
1080             error => "Unexpected operator or function on value: $subaction"
1081             );
1082             }
1083              
1084 0           $logger->debug("_load_value: done returns ok");
1085 0           return 'ok';
1086             }
1087              
1088             1;
1089              
1090             # ABSTRACT: Load serialized data into config tree
1091              
1092             __END__
1093              
1094             =pod
1095              
1096             =encoding UTF-8
1097              
1098             =head1 NAME
1099              
1100             Config::Model::Loader - Load serialized data into config tree
1101              
1102             =head1 VERSION
1103              
1104             version 2.153
1105              
1106             =head1 SYNOPSIS
1107              
1108             use Config::Model;
1109              
1110             # define configuration tree object
1111             my $model = Config::Model->new;
1112             $model->create_config_class(
1113             name => "Foo",
1114             element => [
1115             [qw/foo bar/] => {
1116             type => 'leaf',
1117             value_type => 'string'
1118             },
1119             ]
1120             );
1121              
1122             $model ->create_config_class (
1123             name => "MyClass",
1124              
1125             element => [
1126              
1127             [qw/foo bar/] => {
1128             type => 'leaf',
1129             value_type => 'string'
1130             },
1131             hash_of_nodes => {
1132             type => 'hash', # hash id
1133             index_type => 'string',
1134             cargo => {
1135             type => 'node',
1136             config_class_name => 'Foo'
1137             },
1138             },
1139             [qw/lista listb/] => {
1140             type => 'list',
1141             cargo => {type => 'leaf',
1142             value_type => 'string'
1143             }
1144             },
1145             ],
1146             ) ;
1147              
1148             my $inst = $model->instance(root_class_name => 'MyClass' );
1149              
1150             my $root = $inst->config_root ;
1151              
1152             # put data
1153             my $steps = 'foo=FOO hash_of_nodes:fr foo=bonjour -
1154             hash_of_nodes:en foo=hello
1155             ! lista=foo,bar lista:2=baz
1156             listb:0=foo listb:1=baz';
1157             $root->load( steps => $steps );
1158              
1159             my $s = $root->fetch_element_value('foo'); # => is 'FOO'
1160             $s = $root->grab_value('hash_of_nodes:en foo'); # => is 'hello'
1161             $s = $root->grab_value('lista:1'); # => is 'bar'
1162             $s = $root->grab_value('lista:2'); # => is 'baz'
1163              
1164             # delete some data
1165             $root->load( steps => 'lista~2' );
1166              
1167             $s = $root->grab_value('lista:2'); # => is undef
1168              
1169             # append some data
1170             $root->load( steps => q!hash_of_nodes:en foo.=" world"! );
1171              
1172             $s = $root->grab_value('hash_of_nodes:en foo'); # => is 'hello world'
1173              
1174             =head1 DESCRIPTION
1175              
1176             This module is used directly by L<Config::Model::Node> to load
1177             serialized configuration data into the configuration tree.
1178              
1179             Serialized data can be written by the user or produced by
1180             L<Config::Model::Dumper> while dumping data from a configuration tree.
1181              
1182             =head1 CONSTRUCTOR
1183              
1184             =head2 new
1185              
1186             The constructor should be used only by L<Config::Model::Node>.
1187              
1188             Parameters:
1189              
1190             =over
1191              
1192             =item start_node
1193              
1194             node ref of the root of the tree (of sub-root) to start the load from.
1195             Stored as a weak reference.
1196              
1197             =back
1198              
1199             =head1 load string syntax
1200              
1201             The string is made of the following items (also called C<actions>)
1202             separated by spaces. These actions can be divided in 4 groups:
1203              
1204             =over
1205              
1206             =item *
1207              
1208             navigation: moving up and down the configuration tree.
1209              
1210             =item *
1211              
1212             list and hash operation: select, add or delete hash or list item (also
1213             known as C<id> items)
1214              
1215             =item *
1216              
1217             leaf operation: select, modify or delecte leaf value
1218              
1219             =item *
1220              
1221             annotation: modify or delete configuration annotation (aka comment)
1222              
1223             =back
1224              
1225             =head2 navigation
1226              
1227             =over 8
1228              
1229             =item -
1230              
1231             Go up one node
1232              
1233             =item !
1234              
1235             Go to the root node of the configuration tree.
1236              
1237             =item xxx
1238              
1239             Go down using C<xxx> element. (For C<node> type element)
1240              
1241             =item /xxx
1242              
1243             Go up until the element C<xxx> is found. This search can be combined with one of the
1244             command specified below, e.g C</a_string="foo bar">
1245              
1246             =back
1247              
1248             =head2 list and hash operation
1249              
1250             =over
1251              
1252             =item xxx:yy
1253              
1254             Go down using C<xxx> element and id C<yy> (For C<hash> or C<list>
1255             element with C<node> cargo_type). Literal C<\n> are replaced by
1256             real C<\n> (LF in Unix).
1257              
1258             =item xxx:.foreach_match(yy) or xxx:~yy
1259              
1260             Go down using C<xxx> element and loop over the ids that match the regex
1261             specified by C<yy>. (For C<hash>).
1262              
1263             For instance, with C<OpenSsh> model, you could do
1264              
1265             Host:~"/.*.debian.org/" user='foo-guest'
1266              
1267             to set "foo-user" users for all your debian accounts.
1268              
1269             The leading and trailing '/' may be omitted. Be sure to surround the
1270             regexp with double quote if space are embedded in the regex.
1271              
1272             Note that the loop ends when the load command goes above the element
1273             where the loop is executed. For instance, the instruction below
1274             tries to execute C<DX=BV> and C<int_v=9> for all elements of C<std_id> hash:
1275              
1276             std_id:~/^\w+$/ DX=Bv int_v=9
1277              
1278             In the examples below only C<DX=BV> is executed by the loop:
1279              
1280             std_id:~/^\w+$/ DX=Bv - int_v=9
1281             std_id:~/^\w+$/ DX=Bv ! int_v=9
1282              
1283             The loop is done on all elements of the hash when no value is passed
1284             after "C<:~>" (mnemonic: an empty regexp matches any value).
1285              
1286             =item xxx:.rm(yy) or xxx:-yy
1287              
1288             Delete item referenced by C<xxx> element and id C<yy>. For a list,
1289             this is equivalent to C<splice xxx,yy,1>. This command does not go
1290             down in the tree (since it has just deleted the element). I.e. a
1291             'C<->' is generally not needed afterwards.
1292              
1293             =item xxx:.rm_value(yy) or xxx:-=yy
1294              
1295             Remove the element whose value is C<yy>. For list or hash of leaves.
1296             Does not not complain if the value to delete is not found.
1297              
1298             =item xxx:..rm_match(yy) or xxx:-~/yy/
1299              
1300             Remove the element whose value matches C<yy>. For list or hash of leaves.
1301             Does not not complain if no value were deleted.
1302              
1303             =item xxx:.substitute(/yy/zz/) or xxx:=~s/yy/zz/
1304              
1305             Substitute a value with another. Perl switches can be used(e.g. C<xxx:=~s/yy/zz/gi>)
1306              
1307             =item xxx:<yy or xxx:.push(yy)
1308              
1309             Push C<yy> value on C<xxx> list
1310              
1311             =item xxx:>yy or xxx:.unshift(yy)
1312              
1313             Unshift C<yy> value on C<xxx> list
1314              
1315             =item xxx:@ or xxx:.sort
1316              
1317             Sort the list
1318              
1319             =item xxx:.insert_at(yy,zz)
1320              
1321             Insert C<zz> value on C<xxx> list before B<index> C<yy>.
1322              
1323             =item xxx:.insert_before(yy,zz)
1324              
1325             Insert C<zz> value on C<xxx> list before B<value> C<yy>.
1326              
1327             =item xxx:.insert_before(/yy/,zz)
1328              
1329             Insert C<zz> value on C<xxx> list before B<value> matching C<yy>.
1330              
1331             =item xxx:.insort(zz)
1332              
1333             Insert C<zz> value on C<xxx> list so that existing alphanumeric order is preserved.
1334              
1335             =item xxx:.insort(zz)
1336              
1337             For hash element containing nodes: creates a new hash element with
1338             C<zz> key on C<xxx> hash so that existing alphanumeric order of keys
1339             is preserved. Note that all keys are sorted once this instruction is
1340             called. Following instructions are applied on the created
1341             element. I.e. putting key order aside, C<xxx:.insort(zz)> has the
1342             same effect as C<xxx:zz> instruction.
1343              
1344             =item xxx:.insort(zz,vv)
1345              
1346             For hash element containing leaves: creates a new hash element with
1347             C<zz> key and assing value C<vv> so that existing alphanumeric order of keys
1348             is preserved. Note that all keys are sorted once this instruction is
1349             called. Putting key order aside, C<xxx:.insort(zz,vv)> has the
1350             same effect as C<xxx:zz=vv> instruction.
1351              
1352             =item xxx:.ensure(zz)
1353              
1354             Ensure that list C<xxx> contains value C<zz>. If value C<zz> is
1355             already stored in C<xxx> list, this function does nothing. In the
1356             other case, value C<zz> is inserted in alphabetical order.
1357              
1358             =item xxx:=z1,z2,z3
1359              
1360             Set list element C<xxx> to list C<z1,z2,z3>. Use C<,,> for undef
1361             values, and C<""> for empty values.
1362              
1363             I.e, for a list C<('a',undef,'','c')>, use C<a,,"",c>.
1364              
1365             =item xxx:yy=zz
1366              
1367             For C<hash> element containing C<leaf> cargo_type. Set the leaf
1368             identified by key C<yy> to value C<zz>.
1369              
1370             Using C<xxx:~/yy/=zz> is also possible.
1371              
1372             =item xxx:.copy(yy,zz)
1373              
1374             copy item C<yy> in C<zz> (hash or list).
1375              
1376             =item xxx:.json("path/to/file.json/foo/bar")
1377              
1378             Store C<bar> content in array or hash. This should be used to store
1379             hash or list of values.
1380              
1381             You may store deep data structure. In this case, make sure that the
1382             structure of the loaded data matches the structure of the model. This
1383             won't happen by chance.
1384              
1385             =item xxx:.clear
1386              
1387             Clear the hash or list.
1388              
1389             =back
1390              
1391             =head2 leaf operation
1392              
1393             =over
1394              
1395             =item xxx=zz
1396              
1397             Set element C<xxx> to value C<yy>. load also accepts to set elements
1398             with a quoted string. (For C<leaf> element) Literal C<\n> are replaced by
1399             real C<\n> (LF in Unix). Literal C<\\> are replaced by C<\>.
1400              
1401             For instance C<foo="a quoted string"> or C<foo="\"bar\" and \"baz\"">.
1402              
1403             =item xxx=~s/foo/bar/
1404              
1405             Apply the substitution to the value of xxx. C<s/foo/bar/> is the standard Perl C<s>
1406             substitution pattern.
1407              
1408             Patterns with white spaces must be surrounded by quotes:
1409              
1410             xxx=~"s/foo bar/bar baz/"
1411              
1412             Perl pattern modifiers are accepted
1413              
1414             xxx=~s/FOO/bar/i
1415              
1416             =item xxx~
1417              
1418             Undef element C<xxx>
1419              
1420             =item xxx.=zzz
1421              
1422             Appends C<zzz> value to current value (valid for C<leaf> elements).
1423              
1424             =item xxx=.file(yyy)
1425              
1426             Store the content of file C<yyy> in element C<xxx>.
1427              
1428             Store STDIn in value xxx when C<yyy> is '-'.
1429              
1430             =item xxx=.json(path/to/data.json/foo/bar)
1431              
1432             Open file C<data.json> and store value from JSON data extracted with
1433             C<foo/bar> subpath.
1434              
1435             For instance, if C<data.json> contains:
1436              
1437             {
1438             "foo": {
1439             "bar": 42
1440             }
1441             }
1442              
1443             The instruction C<baz=.json(data.json/foo/bar)> stores C<42> in C<baz>
1444             element.
1445              
1446             =item xxx=.yaml(path/to/data.yaml/0/foo/bar)
1447              
1448             Open file C<data.yaml> and store value from YAML data extracted with
1449             C<0/foo/bar> subpath.
1450              
1451             Since a YAML file can contain several documents (separated by C<--->
1452             lines, the subpath must begin with a number to select the document
1453             containing the required value.
1454              
1455             For instance, if C<data.yaml> contains:
1456              
1457             ---
1458             foo:
1459             bar: 42
1460              
1461             The instruction C<baz=.yaml(data.yaml/0/foo/bar)> stores C<42> in
1462             C<baz> element.
1463              
1464             =item xxx=.env(yyy)
1465              
1466             Store the content of environment variable C<yyy> in element C<xxx>.
1467              
1468             =back
1469              
1470             =head2 annotation
1471              
1472             =over
1473              
1474             =item xxx#zzz or xxx:yyy#zzz
1475              
1476             Element annotation. Can be quoted or not quoted. Note that annotations are
1477             always placed at the end of an action item.
1478              
1479             I.e. C<foo#comment>, C<foo:bar#comment> or C<foo:bar=baz#comment> are valid.
1480             C<foo#comment:bar> is B<not> valid.
1481              
1482             =back
1483              
1484             =head2 Quotes
1485              
1486             You can surround indexes and values with single or double quotes. E.g.:
1487              
1488             a_string='"foo" and "bar"'
1489              
1490             Single quotes were added in version 2.153.
1491              
1492             =head1 Examples
1493              
1494             You can use L<cme> to modify configuration with C<cme modify> command.
1495              
1496             For instance, if L<Config::Model::Ssh> is installed, you can run:
1497              
1498             cme modify ssh 'ControlMaster=auto ControlPath="~/.ssh/master-%r@%n:%p"'
1499              
1500             To delete C<Host *> entry:
1501              
1502             cme modify ssh 'Host:-"*"'
1503              
1504             To specify 2 C<Host> with a single command:
1505              
1506             cme modify ssh 'Host:"foo* bar*" ForwardX11=yes HostName="foo.com" - Host:baz HostName="baz.com"'
1507              
1508             Note the 'C<->' used to go up one node before "C<Host:baz>". In this
1509             case, "up one node" leads to the "root node", so "C<!>" could also be
1510             used instead of "C<->":
1511              
1512             cme modify ssh 'Host:"foo* bar*" ForwardX11=yes HostName="foo.com" ! Host:baz HostName="baz.com"'
1513              
1514             Let's modify now the host name of using a C<.org> domain instead of
1515             C<.com>. The C<:~> operator uses a regexp to loop over several Host
1516             entries:
1517              
1518             cme modify ssh 'Host:~/ba[rz]/ HostName=~s/.com$/.org/'
1519              
1520             Now that ssh config is mucked up with dummy entries, let's clean up:
1521              
1522             cme modify ssh 'Host:-"baz" Host:-"foo* bar*"'
1523              
1524             =head1 Methods
1525              
1526             =head2 load
1527              
1528             Load data into the node tree (from the node passed with C<node>)
1529             and fill values as we go following the instructions passed with
1530             C<steps>. (C<steps> can also be an array ref).
1531              
1532             Parameters are:
1533              
1534             =over
1535              
1536             =item steps (or step)
1537              
1538             A string or an array ref containing the steps to load. See
1539             L<above/"load string syntax"> for a description of the string.
1540              
1541             =item check
1542              
1543             Whether to check values while loading. Either C<yes> (default), C<no> or C<skip>.
1544             Bad values are discarded when C<check> is set to C<skip>.
1545              
1546             =item caller_is_root
1547              
1548             Change the target of the C<!> command: when set, the C<!> command go
1549             to caller node instead of going to root node. (default is false)
1550              
1551             =back
1552              
1553             =head1 AUTHOR
1554              
1555             Dominique Dumont, (ddumont at cpan dot org)
1556              
1557             =head1 SEE ALSO
1558              
1559             L<Config::Model>,L<Config::Model::Node>,L<Config::Model::Dumper>
1560              
1561             =head1 AUTHOR
1562              
1563             Dominique Dumont
1564              
1565             =head1 COPYRIGHT AND LICENSE
1566              
1567             This software is Copyright (c) 2005-2022 by Dominique Dumont.
1568              
1569             This is free software, licensed under:
1570              
1571             The GNU Lesser General Public License, Version 2.1, February 1999
1572              
1573             =cut