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   322 use strict;
  59         105  
  59         2727  
13 59     59   291 use warnings;
  59         96  
  59         917  
14 59     59   225 use 5.10.1;
  59         108  
  59         1244  
15 59     59   633 use Mouse;
  59         223  
16 59     59   311  
  59         128  
  59         307  
17             use Config::Model::Exception;
18 59     59   18688 use Log::Log4perl qw(get_logger :levels);
  59         155  
  59         1673  
19 59     59   303 use JSON;
  59         113  
  59         473  
20 59     59   38794 use Path::Tiny;
  59         506620  
  59         305  
21 59     59   7240 use YAML::Tiny;
  59         118  
  59         2434  
22 59     59   26643  
  59         262757  
  59         3316  
23             use feature qw/postderef signatures/;
24 59     59   463 no warnings qw/experimental::postderef experimental::signatures/;
  59         112  
  59         4868  
25 59     59   363  
  59         123  
  59         412610  
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   297  
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   3865 return if $self->instance->initial_load;
61              
62 1362 100       3823 $cmd =~ s/\n/\\n/g;
63 56 100       443 foreach my $p (@params) {
64             $message =~ s/%(\w+)/$log_dispatch{$1}->($p)/e;
65 55         117 }
66 55         111 my $str = ref $cmd eq 'ARRAY' ? "@$cmd"
67 113         487 : ref $cmd ? $$cmd : $cmd;
  113         381  
68             $verbose_logger->info("command '$str': $message");
69 55 100       220 }
    50          
70              
71 55         212 my $self = shift;
72              
73             my %args = @_;
74              
75 401     401 1 805 my $node = $self->start_node;
76              
77 401         1098 my $steps = delete $args{steps} // delete $args{step};
78             croak "load error: missing 'steps' parameter" unless defined $steps;
79 401         1451  
80             my $caller_is_root = delete $args{caller_is_root};
81 401   66     1763  
82 401 50       1105 if (delete $args{experience}) {
83             carp "load: experience parameter is deprecated";
84 401         846 }
85              
86 401 50       1154 my $inst = $node->instance;
87 0         0  
88             # tune value checking
89             my $check = delete $args{check} || 'yes';
90 401         1240 croak __PACKAGE__, "load: unexpected check $check" unless $check =~ /yes|no|skip/;
91              
92             # accept commands
93 401   100     1472 my $huge_string = ref $steps ? join( ' ', @$steps ) : $steps;
94 401 50       3686  
95             # do a split on ' ' but take quoted string into account
96             my @command = (
97 401 50       1086 $huge_string =~ m/
98             ( # begin of *one* command
99             (?: # group parts of a command (e.g ...:...=... )
100 401         4382 [^\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         858 $logger->trace("_load returned $ret");
123 401         676  
124 401         800 # found '!' command
125 413         1672 if ( $ret eq 'root' ) {
126 398         1668 $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       3785 }
130 12 100       156 }
131 12 50       41 } 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       1254 }
138 3         7 else {
139 3 50       10 $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         8 error => $str,
144             object => $node
145 3 100       16 ) 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       1103  
153 0         0 return $ret;
154             }
155              
156             # returns elt action id subaction value
157 385         5340 my $cmd = shift;
158             $logger->trace("split on: ->$cmd<-");
159              
160             my $quoted_string = qr/"(?: \\" | [^"] )* "/x; # quoted string
161              
162 1228     1228   94855 # do a split on ' ' but take quoted string into account
163 1228         4591 my @command = (
164             $cmd =~ m!^
165 1228         10506 (\w[\w-]*)? # element name can be alone
166             (?:
167             (:~|:-[=~]?|:=~|:\.\w+|:[=<>@]?|~) # action
168 1228         30503 (?:
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         3237 error => "Syntax error: spurious char at command end: '$leftout'. Did you forget double quotes ?"
210             );
211 1228 100       3007 }
212 2         10 return wantarray ? @command : \@command;
213             }
214              
215             my %load_dispatch = (
216             node => \&_walk_node,
217 1226 100       7679 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   1793  
232 605   100     1942 my $cmd;
233 605         2077 while ( $cmd = shift @$cmdref ) {
234 605         2884 if ( $logger->is_debug ) {
235             my $msg = $cmd;
236 605         5552 $msg =~ s/\n/\\n/g;
237             $logger->debug("Loader: Executing cmd '$msg' on node $node_name");
238 605         910 }
239 605         1787  
240 1331 100       3260 next if $cmd =~ /^\s*$/;
241 50         184  
242 50         107 if ( $cmd eq '!' ) {
243 50         151 $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       11649 # Do not change current node as we don't want to mess up =~ commands
247             return 'root';
248 1331 100       3496 }
249 16         78  
250 16         192 if ( $cmd eq '-' ) {
251             my $parent = $node->parent;
252             if (defined $parent) {
253 16         134 $self->_log_cmd($cmd,'Going up from %name to %name', $node, $node->parent);
254             }
255             else {
256 1315 100       2880 $self->_log_cmd($cmd,'Going up from %name to exit Loader.', $node);
257 138         440 }
258 138 100       353 return 'up';
259 132         484 }
260              
261             if ( $cmd =~ m!^/([\w-]+)! ) {
262 6         24 my $search = $1;
263             if ($node->has_element($search)) {
264 138         1297 $self->_log_cmd($cmd, 'Element %qs found in current node (%name).', $search, $node);
265             $cmd =~ s!^/!! ;
266             } else {
267 1177 100       3138 $self->_log_cmd(
268 5         20 $cmd,
269 5 100       18 'Going up from %name to %name to search for element %qs.',
270 3         15 $node, $node->parent, $search
271 3         102 );
272             unshift @$cmdref, $cmd;
273 2         23 return 'up';
274             }
275             }
276              
277             my ( $element_name, $action, $function_param, $id, $subaction, $value_function_param2, $value_param, $note ) =
278 2         69 _split_cmd($cmd);
279 2         16  
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         2919  
284             if ( $logger->is_debug ) {
285             my @disp = map { defined $_ ? "'$_'" : '<undef>' } @instructions;
286             $logger->debug("_load instructions: @disp (from: $cmd)");
287 1173   100     6030 }
288 1173         3315  
289             if ( not defined $element_name and not defined $note ) {
290 1173 100       3268 Config::Model::Exception::Load->throw(
291 45 100       222 command => $cmd,
  315         603  
292 45         198 error => 'Syntax error: cannot find element in command'
293             );
294             }
295 1173 50 66     7501  
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       2494  
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     5565  
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     3468 Config::Model::Exception::UnknownElement->throw(
321 4         19 object => $node,
322 4         16 element => $element_name,
323             ) if $check eq 'yes';
324             unshift @$cmdref, $cmd;
325 1169 100       3673 return 'error';
326 6 100       70 }
327              
328             unless ( $node->is_element_available( name => $element_name ) ) {
329             Config::Model::Exception::UnavailableElement->throw(
330 2         7 object => $node,
331 2         7 element => $element_name
332             ) if $check eq 'yes';
333             unshift @$cmdref, $cmd;
334 1163 100       3404 return 'error';
335 1 50       31 }
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       3302 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         3526  
353             $logger->debug("_load: calling $element_type loader on element $element_name");
354 1162         2886  
355             my $ret = $self->$method( $node, $check, \@instructions, $cmdref, $cmd );
356 1162 50       2812 $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         5641  
360             if ( $ret eq 'error' or $ret eq 'done' ) {
361 1162         10608 $logger->debug("_load return: $node_name got $ret");
362 1152         5619 return $ret;
363 1152 50       9131 }
364             if ( $ret eq 'root' and not $at_top_level ) {
365             $logger->debug("_load return: $node_name got $ret");
366 1152 100 66     4828 return 'root';
367 58         261 }
368 58         500  
369             # ret eq up or ok -> go on with the loop
370 1094 50 66     6011 }
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         1251 }
379              
380              
381             my ( $self, $target_obj, $note, $instructions, $cmdref, $cmd ) = @_;
382 49     49   129  
383 49         164 unquote($note);
384 49         580  
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   2855 }
390             else {
391 1113         3014 Config::Model::Exception::Load->throw(
392             command => $$cmdref,
393             error => "Error: cannot set annotation with '"
394 1113 100       2939 . join( "','", grep { defined $_ } @$instructions ) . "'"
395 45 50       119 );
396 45         135 }
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   99 Config::Model::Exception::Load->throw(
410             command => $inst,
411 30         59 object => $node,
412 30         59 error => "Don't know what to do with '@left' "
413 30         97 . "for node element $element_name"
414 30         128 );
415             }
416 30         82  
  150         241  
417 30 50       126 $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         117 s/^"// && s/"$// && s!\\"!"!g;
427             }
428 30         488 }
429             }
430              
431             my ( $self, $node, $check, $inst, $cmdref, $cmd ) = @_;
432 2258     2258 0 4302 my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note ) = @$inst;
433 3613 100       6930  
434 1267         3007 my $element = $node->fetch_element( name => $element_name, check => $check );
435 1267         1917  
436 1267 100 100     4808 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   89 $logger->debug("_load_check_list: set whole list");
443 17         76  
444             $self->_log_cmd($cmd, 'Setting %name items %qs.', $element, $value);
445 17         172 # valid for check_list or list
446             $element->load( $value, check => $check );
447 17 0 33     94 $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     113 Config::Model::Exception::Load->throw(
453 17         66 object => $element,
454             command => join( '', grep { defined $_} @$inst ),
455 17         245 error => "Wrong assignment with '$subaction' on check_list"
456             );
457 17         252 }
458 17         339  
459 17         58 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   188 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     780 }
542             }
543              
544             my ( $self, $element, $check, $inst, $cmdref, $before_str, @values ) = @_;
545 296     296   1016 my $before = ($before_str =~ s!^/!! and $before_str =~ s!/$!!) ? qr/$before_str/ : $before_str;
546 296 100 100     1720 $element->insert_before( $before, @values );
547             return 'ok';
548 42         217 }
549 42 50       117  
550 42   66     107 my ( $self, $element, $check, $inst, $cmdref, @values ) = @_;
551 42         245 my %content = map { $_ => 1 } $element->fetch_all_values;
552             foreach my $one_value (@values) {
553 42         643 next if $content{$one_value};
554             $element->insort($one_value);
555             $content{$one_value} = 1;
556             }
557              
558 3     3   13 return 'ok';
559 3 100 66     65 }
560 3         17 my ( $self, $element, $check, $inst, $cmdref, $id ) = @_;
561 3         28 $logger->debug("_remove_by_id: removing id '$id'");
562             $element->remove($id);
563             return 'ok';
564             }
565 3     3   11  
566 3         15 # utf8 decode is done by JSON module, so slurp_raw must be used
  13         32  
567 3         10 return decode_json($file->slurp_raw);
568 6 100       19 }
569 3         15  
570 3         14 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         26  
574             my $data = __load_json_file($file);
575              
576 8     8   24 # test for diff before clobbering ? What about deep data ???
577 8         33 $element->load_data(
578 8         78 data => __data_from_vector($data, @vector),
579 7         42 check => $check
580             );
581             return 'ok';
582 3     3   7 }
  3         6  
  3         4  
583              
584 3         16 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         5 $element->delete($idx) if defined $v and $v eq $rm_val;
590 1         11 }
591              
592 1         5 return 'ok';
593             }
594              
595 1         228 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   3 $element->delete($idx) if defined $v and $v =~ /$rm_val/;
604             }
605 1         7  
606 1         17 return 'ok';
607 5         14 }
608 5 100 66     27  
609             my ( $self, $element, $check, $inst, $cmdref, $s_val ) = @_;
610              
611 1         5 $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   3 $self->_load_value( $l, $check, '=~', $s_val, $inst );
616             }
617 1         5  
618             return 'ok';
619 1         13 }
620              
621 1         5 my ( $self, $element, $check, $inst, $cmdref, $id ) = @_;
622 5         15 my $node = $element->insort($_[5]);
623 5 100 100     49 $logger->debug("_insort_hash_of_node: calling _load on node id $id");
624             return $self->_load( $node, $check, $cmdref );
625             }
626 1         7  
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         10 my @f_args = grep { defined } ( ( $f_arg // $id // '' ) =~ /([^,"]+)|"([^"]+)"/g );
633              
634 2         20 my $elt_type = $node->element_type($element_name);
635 8         31 my $cargo_type = $element->cargo_type;
636 8         22  
637             if ( defined $note and not defined $action and not defined $subaction ) {
638             $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
639 2         20 return 'ok';
640             }
641              
642             if ( defined $action and $action eq ':=' and $cargo_type eq 'leaf' ) {
643 1     1   4 # 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         23 $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   456 }
651 145         501  
652             # compat mode for list=a,b,c,d commands
653 145         453 if ( not defined $action
654             and defined $subaction
655 145   100     1608 and $subaction eq '='
  382   100     853  
656             and $cargo_type eq 'leaf' ) {
657 145         567 $logger->debug("_load_list: set whole list with '=' subaction'");
658 145         678  
659             # valid for check_list or list
660 145 100 100     498 $self->_log_cmd($cmd, 'Setting %name values to %qs.', $element, $value);
      66        
661 3         12 $element->load( $value, check => $check );
662 3         10 $self->_load_note( $element, $note, $inst, $cmdref, $cmd );
663             return 'ok';
664             }
665 142 100 100     853  
      66        
666             unquote( $id, $value, $note );
667 35         113  
668             if ( my $dispatch = $self->_get_dispatch($element, list => $cargo_type, $action, $cmd, @f_args)) {
669 35         365 return $dispatch->( $self, $element, $check, $inst, $cmdref, @f_args );
670 35         425 }
671 33         218  
672 33         192 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     722 error => "Wrong assignment with '$subaction' on "
      66        
      66        
677             . "element type: $elt_type, cargo_type: $cargo_type"
678             );
679             }
680 26         110  
681             if ( defined $action and $action eq ':' ) {
682             unquote($id);
683 26         255 my $obj = $element->fetch_with_id( index => $id, check => $check );
684 26         335 $self->_load_note( $obj, $note, $inst, $cmdref, $cmd );
685 26         178  
686 26         105 if ( $cargo_type =~ /node/ ) {
687              
688             # remove possible leading or trailing quote
689 81         310 $self->_log_cmd($cmd,'Going down from %name to %name', $node, $obj );
690             return $self->_load( $obj, $check, $cmdref );
691 81 100       348 }
692 28         97  
693             return 'ok' unless defined $subaction;
694              
695 53 50 66     201 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     231  
705 52         134 Config::Model::Exception::Load->throw(
706 52         221 object => $element,
707 52         246 command => join( '', map { $_ || '' } @$inst ),
708             error => "Wrong assignment with '$a_str' on "
709 52 100       269 . "element type: $elt_type, cargo_type: $cargo_type"
710             );
711              
712 38         146 }
713 38         420  
714             my ( $self, $node, $check, $inst, $cmdref, $cmd ) = @_;
715             my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note ) = @$inst;
716 14 100       48  
717             unquote( $id, $value, $note );
718 11 50       47  
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       4 object => $element,
  7         34  
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   888 my @keys = $element->fetch_all_indexes;
739 223         1032 my $ret = 'ok';
740             my $pattern = $id // $f_arg;
741 223         740 $pattern =~ s!^/|/$!!g if $pattern;
742             my @loop_on = $pattern ? grep { /$pattern/ } @keys : @keys;
743 223         596 if ($logger->is_debug) {
744 223         948 my $str = $pattern ? " with regex /$pattern/" : '';
745             $logger->debug("_load_hash: looping$str on keys @loop_on");
746 223 100 100     796 }
747              
748 4         21 my @saved_cmd = @$cmdref;
749 4         14 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       2729 $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         26  
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     964 Config::Model::Exception::Load->throw(
762 3         29 object => $element,
763 3         6 command => join( '', @$inst ),
764 3   66     13 error => "Hash assignment with '$action' on unexpected "
765 3 100       20 . "cargo_type: $cargo_type"
766 3 100       13 );
  10         131  
767 3 100       13 }
768 1 50       7  
769 1         7 $logger->debug("_load_hash: loop on id $loop_id returned $ret (left cmd: @$cmdref)");
770             if ( $ret eq 'error') { return $ret; }
771             }
772 3         30 return $ret;
773 3         9 }
774 11         32  
775 11         35 my @f_args = grep { defined } ( ( $f_arg // $id // '' ) =~ /([^,"]+)|"([^"]+)"/g );
776 11         42  
777 11 100       159 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         27 }
780              
781             if (not defined $id) {
782 2         8 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         55  
794 11 50       88 # remove possible leading or trailing quote
  0         0  
795             $self->_log_cmd($cmd,'Going down from %name to %name', $node, $obj );
796 3         30 if ( defined $subaction ) {
797             Config::Model::Exception::Load->throw(
798             object => $element,
799 215   100     1855 command => join( '', @$inst ),
  430   100     1135  
800             error => qq!Hash assignment with '$action"$id"$subaction"$value"' on unexpected !
801 215 100       872 . "cargo_type: $cargo_type"
802 14         53 );
803             }
804             return $self->_load( $obj, $check, $cmdref );
805 201 50       536 }
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         805 $self->_log_cmd($cmd,'Creating empty %name.', $obj );
814 201         821 $logger->debug("_load_hash: created empty element of type $cargo_type");
815             return 'ok';
816 201 100 66     1746 }
    100 66        
    50 66        
    0          
817             elsif ($action) {
818             $logger->debug("_load_hash: giving up");
819 114         452 Config::Model::Exception::Load->throw(
820 114 50       944 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         524 my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note ) = @$inst;
829              
830             unquote( $id, $value );
831              
832 83         416 my $element = $node->fetch_element( name => $element_name, check => $check );
833 83 50       805 $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         17 $self->_log_cmd($cmd, "Deleting %name.", $element );
838 4         94 $element->store(value => undef, check => $check);
839 4         41 }
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   2378 error => "Load error on leaf with "
853 747         2174 . "'$element_name$action$id' command "
854             . "(element '" . $element->name . "')"
855 747         2202 );
856             }
857 747         2058 }
858 747         3120  
859             return 'ok' unless defined $subaction;
860 747 100 66     2308  
861 5 100       22 if ( $logger->is_debug ) {
    50          
862 4         18 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       1954 '=' => \&_store_value ,
885             '.=' => \&_append_value,
886 736 100       1866 '=~' => \&_apply_regexp_on_value,
887 20 50       78 '=.file' => \&_store_file_in_value,
888 20         33 '=.json' => \&_store_json_vector_in_value,
889 20         64 '=.yaml' => \&_store_yaml_vector_in_value,
890             '=.env' => sub { $_[1]->store( value => $ENV{$_[2]}, check => $_[3] ); return 'ok'; },
891             );
892 736         5915  
893             my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
894 734 50       2817 $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   2495 $value, $element, $orig
920 814         2922 );
921 814         7864 if ($res) {
922 814         2729 Config::Model::Exception::Load->throw(
923             object => $element,
924             command => $instructions,
925             error => "Failed regexp '$value' on " . "element '"
926 6     6   50 . $element->name . "' : $res"
927 6         33 );
928 6         15 }
929 6         37 $element->store( value => $orig, check => $check );
930             }
931             else {
932             $self->_log_cmd(
933 6         117 $cmd, "Not applying regexp %qs on undefined value of %leaf.",
934             $value, $element, $orig
935             );
936             }
937 13     13   37 }
938              
939 13         53 my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
940 13 100       36  
941             if ($value eq '-') {
942             $element->store( value => join('',<STDIN>), check => $check );
943 11         1092 return 'ok';
944 11         61 }
945 11         44  
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       193 }
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         9 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       5 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         11 }
976 1 50       110 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   13 );
990 4         11 }
991 7 100       30 return ($file, @vector);
992             }
993 4         31  
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   18 $element->store(
998 6         45 value => __data_from_vector($data, @vector),
999 6         36 check => $check
1000 6         333 );
1001 6         21 }
1002 22         193  
1003 22 100       646 my ( $self, $element, $value, $check, $instructions, $cmd ) = @_;
    100          
1004 4         62 my ($file, @vector) = $self->__get_file_from_vector($element,$instructions,$value);
1005 4         16 my $data = YAML::Tiny->read($file->stringify);
1006             $element->store(
1007             value => __data_from_vector($data, @vector),
1008 12         517 check => $check
1009             );
1010             }
1011 6 100       75  
1012 2         10 my ( $self, $element, $check, $subaction, $value, $instructions, $cmd ) = @_;
1013              
1014 2 50       29 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         20  
1023             $logger->debug("_load_value: action '$subaction' value '$value' check $check");
1024             my $dispatch = $load_value_dispatch{$subaction};
1025             if ($dispatch) {
1026 4     4   11 return $dispatch->( $self, $element, $value, $check, $instructions, $cmd );
1027 4         15 }
1028 2         6 else {
1029 2         880 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   3 $logger->debug("_load_value: done returns ok");
1037 1         6 return 'ok';
1038 1         7 }
1039 1         1218  
1040             1;
1041              
1042             # ABSTRACT: Load serialized data into config tree
1043              
1044              
1045             =pod
1046 840     840   2430  
1047             =encoding UTF-8
1048 840 50       3580  
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.152
1056              
1057 840         4029 =head1 SYNOPSIS
1058 840         6367  
1059 840 50       1933 use Config::Model;
1060 840         2200  
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