File Coverage

blib/lib/Config/Model/Loader.pm
Criterion Covered Total %
statement 393 438 89.7
branch 157 212 74.0
condition 85 116 73.2
subroutine 45 45 100.0
pod 1 2 50.0
total 681 813 83.7


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