File Coverage

blib/lib/Config/Model/ValueComputer.pm
Criterion Covered Total %
statement 218 229 95.2
branch 87 116 75.0
condition 23 27 85.1
subroutine 29 29 100.0
pod 2 4 50.0
total 359 405 88.6


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 Mouse;
12 59     59   395 use MouseX::StrictConstructor;
  59         117  
  59         470  
13 59     59   25257  
  59         134  
  59         334  
14             # use Scalar::Util qw(weaken) ;
15             use Carp;
16 59     59   8037 use Parse::RecDescent 1.90.0;
  59         123  
  59         4943  
17 59     59   347 use Data::Dumper ();
  59         866  
  59         319  
18 59     59   1985 use Log::Log4perl qw(get_logger :levels);
  59         110  
  59         1066  
19 59     59   262  
  59         108  
  59         505  
20             use vars qw($compute_grammar $compute_parser);
21 59     59   7138  
  59         117  
  59         3739  
22             use feature qw/postderef signatures/;
23 59     59   347 no warnings qw/experimental::postderef experimental::signatures/;
  59         134  
  59         4509  
24 59     59   334  
  59         142  
  59         92567  
25             my $logger = get_logger("ValueComputer");
26              
27             # allow_override is intercepted and handled by Value object
28              
29             has formula => ( is => 'ro', isa => 'Str', required => 1 );
30             has value_type => ( is => 'ro', isa => 'Str', required => 1 );
31              
32             # value_object is mostly used for error messages
33             has value_object => (
34             is => 'ro',
35             isa => 'Config::Model::AnyThing',
36             required => 1,
37             weak_ref => 1,
38             handles => [qw/grab grab_value location index element/],
39             );
40              
41             has variables => ( is => 'ro', isa => 'HashRef', default => sub { {} } );
42             has replace => ( is => 'ro', isa => 'HashRef', default => sub { {} } );
43             has [qw/use_eval allow_override use_as_upstream_default/] =>
44             ( is => 'ro', isa => 'Bool', default => 0 );
45              
46             has allow_user_override => (
47             is => 'ro',
48             isa => 'Bool',
49             lazy => 1,
50             builder =>
51             sub { my $self = shift; return $self->allow_override || $self->use_as_upstream_default; } );
52              
53             has need_quote => ( is => 'ro', isa => 'Bool', builder => '_need_quote', lazy => 1 );
54              
55             my $self = shift;
56              
57 188     188   386 my $need_quote = 0;
58             $need_quote = 1 if $self->{use_eval} and $self->{value_type} !~ /(integer|number|boolean)/;
59 188         317 return $need_quote;
60 188 100 66     769 }
61 188         1788  
62             has undef_is => ( is => 'ro', isa => 'Maybe[Str]' );
63              
64             has undef_replacement => (
65             is => 'ro',
66             isa => 'Maybe[Str]',
67             builder => '_build_undef_replacement',
68             lazy => 1
69             );
70              
71             my $self = shift;
72              
73             my $sui = $self->undef_is;
74 185     185   370  
75             return defined $sui && $sui eq "''" ? ''
76 185         505 : defined $sui ? $sui
77             : undef;
78 185 100 100     1362  
    100          
79             }
80              
81             my $self = shift;
82              
83             # create parser if needed
84             $compute_parser ||= Parse::RecDescent->new($compute_grammar);
85 188     188 1 530  
86             $logger->trace("called with formula: $self->{formula}");
87             # must make a first pass at computation to subsitute index and
88 188   66     708 # element values. leaves $xxx outside of &index or &element untouched
89             my $result_r =
90 188         1402397 $compute_parser->pre_compute( $self->{formula}, 1, $self->{value_object},
91             $self->{variables}, $self->{replace}, 'yes', $self->need_quote, );
92              
93             $logger->trace("pre_formula: ". ($result_r ? $$result_r : ' pre_compute failed, using original formula'));
94             $self->{pre_formula} = $result_r ? $$result_r : $self->{formula};
95 188         2777 return;
96             }
97 188 50       418884  
98 188 50       2004 my $check = $args{check} || 'yes';
99 188         1003  
100             my $pre_formula = $self->{pre_formula};
101             $logger->trace("called with pre_formula: $pre_formula");
102 592     592 1 996 my $variables = $self->compute_variables( check => $check );
  592         933  
  592         940  
  592         816  
103 592   100     2156  
104             die "internal error" unless defined $variables;
105 592         1141  
106 592         2313 my $result;
107 592         5279 my @parser_args = (
108             $self->{value_object}, $variables, $self->{replace}, $check, $self->{need_quote},
109 592 50       1518 $self->undef_replacement
110             );
111 592         813  
112             if ( $self->{use_eval}
113             or $self->{value_type} =~ /(integer|number|boolean)/ ) {
114 592         3347 $logger->trace("will use eval");
115             my $all_defined = 1;
116             my %__vars;
117 592 100 100     4377 foreach my $key ( sort keys %$variables ) {
118              
119 68         234 # no need to get variable if not used in formula;
120 68         454 next unless index( $pre_formula, $key ) > 0;
121 68         116 my $vr = _value_from_object( $key, @parser_args );
122 68         245 my $v = $$vr;
123             $v = $self->undef_replacement unless defined $v;
124             $logger->debug( "compute: var $key -> ", ( defined $v ? $v : '<undef>' ) );
125 95 50       317 # security: $v are stored in %__vars hash, so they are
126 95         194 # used by eval'ed code, but not directly eval'ed
127 95         161 if ( defined $v ) { $__vars{$key} = $v }
128 95 100       212 else { $all_defined = 0; }
129 95 100       299 }
130              
131             if ($all_defined) {
132 95 100       645 my $formula = $pre_formula;
  87         250  
133 8         23 $formula =~ s/\$([_a-zA-Z]\w*)/defined $__vars{$1} ? "\$__vars{$1}" : "\$$1" /eg;
134             $logger->debug("compute: evaluating '$formula'");
135             $result = eval $formula; ## no critic (ProhibitStringyEval)
136 68 100       141 if ($@) {
137 61         107 Config::Model::Exception::Formula->throw(
138 61 100       334 object => $self->{value_object},
  104         543  
139 61         233 error => "Eval of formula '$formula' failed:\n$@"
140 61     1   5614 . "Make sure that your element is indeed "
  1     1   10  
  1     1   2  
  1     1   184  
  1     1   8  
  1     1   3  
  1     1   81  
  1         7  
  1         3  
  1         79  
  1         8  
  1         2  
  1         106  
  1         7  
  1         3  
  1         119  
  1         7  
  1         1  
  1         87  
  1         7  
  1         4  
  1         87  
141 61 50       353 . "'$self->{value_type}'"
142             );
143             }
144 0         0 }
145             }
146             else {
147             $logger->trace("calling parser with compute on pre_formula $pre_formula");
148             my $formula_r = $compute_parser->compute( $pre_formula, 1, @parser_args );
149              
150             $result = $$formula_r;
151              
152 524         2061 #$result = $self->{computed_formula} = $formula;
153 524         6513 }
154              
155 524         21770 $logger->debug( "compute result is '" . ( defined $result ? $result : '<undef>' ) . "'" );
156              
157             return $result;
158             }
159              
160 592 100       3189 my $check = $args{check} || 'yes';
161             $logger->trace("compute_info called with $self->{formula}");
162 592         6359  
163             my $orig_variables = $self->{variables};
164             my $variables = $self->compute_variables;
165 8     8 0 14 my $str = "value is computed from '$self->{formula}'";
  8         12  
  8         15  
  8         12  
166 8   50     55  
167 8         40 return $str unless defined $variables;
168              
169 8         58 if (%$variables) {
170 8         20 $str .= ", where ";
171 8         29 foreach my $k ( sort keys %$variables ) {
172             my $u_val = $variables->{$k};
173 8 50       22 if ( ref($u_val) ) {
174             foreach (sort keys %$u_val) {
175 8 100       22 $str .= "\n\t\t'\$$k" . "{$_} is converted to '$orig_variables->{$k}{$_}'";
176 7         17 }
177 7         23 }
178 14         24 else {
179 14 50       24 my $val;
180 0         0 if ( defined $u_val ) {
181 0         0 my $obj = eval { $self->{value_object}->grab($u_val) };
182             if ($@) {
183             my $e = $@;
184             my $msg = ref($e) ? $e->full_message : $e;
185 14         20 Config::Model::Exception::Model->throw(
186 14 100       40 object => $self,
187 11         17 error => "Compute variable:\n" . $msg
  11         49  
188 11 50       34 );
189 0         0 }
190 0 0       0 $val =
191 0         0 $obj->get_type eq 'node' ? '<node>'
192             : $obj->get_type eq 'hash' ? '<hash>'
193             : $obj->get_type eq 'list' ? '<list>'
194             : $obj->fetch( check => $check );
195             }
196             $str .= "\n\t\t'$k' from path '$orig_variables->{$k}' is ";
197 11 50       35 $str .= defined $val ? "'$val'" : 'undef';
    50          
    50          
198             }
199             }
200             }
201              
202 14         54 #$str .= " (evaluated as '$self->{computed_formula}')"
203 14 100       45 # if $self->{formula} ne $self->{computed_formula} ;
204              
205             return $str;
206             }
207              
208             # internal. resolves variables that contains $foo or &bar
209             # returns a hash of variable names -> variable path
210             my $check = $args{check} || 'yes';
211 8         37  
212             # a shallow copy should be enough as we don't allow
213             # replace in replacement rules
214             my %variables = %{ $self->{variables} };
215             $logger->trace( "called on variables '", join( "', '", sort keys %variables ), "'" )
216 658     658 0 1198 if $logger->is_trace;
  658         1192  
  658         1240  
  658         978  
217 658   100     1949  
218             # apply a compute on all variables until no $var is left
219             my $var_left = scalar( keys %variables ) + 1;
220              
221 658         1119 while ($var_left) {
  658         2015  
222 658 100       1835 my $old_var_left = $var_left;
223             foreach my $key ( keys %variables ) {
224             my $value = $variables{$key}; # value may be undef
225             next unless defined $value;
226 658         5356  
227             #next if ref($value); # skip replacement rules
228 658         1629 $logger->trace("key '$key', value '$value', left $var_left");
229 665         1102 next unless $value =~ /\$|&/;
230 665         1469  
231 264         662 my $pre_res_r =
232 264 100       571 $compute_parser->pre_compute( $value, 1, $self->{value_object}, \%variables,
233             $self->{replace}, $check );
234             $logger->trace("key '$key', pre res '$$pre_res_r', left $var_left\n");
235 261         921 $variables{$key} = $$pre_res_r;
236 261 100       2438 $logger->trace( "variable after pre_compute: ", join( " ", keys %variables ) )
237             if $logger->is_trace;
238              
239             if ( $$pre_res_r =~ /\$/ ) {
240 42         350 # variables needs to be evaluated
241 42         96060 my $res_ref =
242 42         392 $compute_parser->compute( $$pre_res_r, 1, $self->{value_object}, \%variables,
243 42 50       114 $self->{replace}, $check );
244              
245             #return undef unless defined $res ;
246 42 100       412 $variables{$key} = $$res_ref;
247             $logger->trace( "variable after compute: ", join( " ", keys %variables ) )
248             if $logger->is_trace;
249             }
250 32         264 {
251             no warnings "uninitialized"; ## no critic (TestingAndDebugging::ProhibitNoWarnings)
252             $logger->trace("result $key -> '$variables{$key}' left '$var_left'");
253 32         1366 }
254 32 50       102 }
255              
256             my @var_left = grep { defined $variables{$_} && $variables{$_} =~ /[\$&]/ }
257             sort keys %variables;
258 59     59   521  
  59         129  
  59         67948  
  42         319  
259 42         187 $var_left = @var_left;
260              
261             Config::Model::Exception::Formula->throw(
262             object => $self->{value_object},
263 665 100       1862 error => "Can't resolve user variable: '" . join( "','", @var_left ) . "'"
  264         1201  
264             ) unless ( $var_left < $old_var_left );
265             }
266 665         1099  
267             $logger->trace("done");
268             return \%variables;
269             }
270 665 50       2182  
271             my ( $replace_h, $pre_value ) = @_;
272              
273             $logger->trace("value: _pre_replace called with value '$pre_value'");
274 658         1801 my $result =
275 658         4637 exists $replace_h->{$pre_value}
276             ? $replace_h->{$pre_value}
277             : '$replace{' . $pre_value . '}';
278             return \$result;
279 25     25   43486 }
280              
281 25         116 my ( $replace_h, $value, $value_object, $variables, $replace, $check, $need_quote, $undef_is )
282             = @_;
283              
284 25 100       238 if ( $logger->is_trace ) {
285             my $str = defined $value ? $value : '<undef>';
286 25         386 $logger->trace("value: _replace called with value '$str'");
287             }
288              
289             my $result;
290 26     26   5337 if ( defined $value and $value =~ /\$/ ) {
291              
292             # must keep original variable
293 26 50       82 $result = '$replace{' . $value . '}';
294 0 0       0 }
295 0         0 elsif ( defined $value ) {
296             my $r = $replace_h->{$value};
297             $result = defined $r ? $r : $undef_is;
298 26         157 }
299 26 100 100     136 return \$result;
    100          
300             }
301              
302 7         19 my ( $up, $function, $return, $value_object, $variables_h, $replace_h, $check, $need_quote ) =
303             @_;
304              
305 10         22 $logger->trace("handling &$function($up) ");
306 10 100       30  
307             my $target = $value_object->eval_function($function, $up, $check);
308 26         415 $return = \$target ;
309              
310             # print "\&foo(...) result = ",$$return," \n";
311              
312 20     20   28005 # make sure that result of function is quoted (avoid bareword errors)
313             $$return = '"' . $$return . '"' if $need_quote;
314              
315 20         102 $logger->debug("&$function(...) returns $$return");
316             return $return;
317 20         200 }
318 20         40  
319             my ( $f_name, $return, $value_object, $variables_h, $replace_h, $check, $need_quote ) = @_;
320              
321             $logger->trace("_function_alone: handling $f_name");
322              
323 20 100       45 my $method_name =
324             $f_name eq 'element' ? 'element_name'
325 20         68 : $f_name eq 'index' ? 'index_value'
326 20         487 : $f_name eq 'location' ? 'location'
327             : undef;
328              
329             Config::Model::Exception::Formula->throw(
330 19     19   41646 object => $value_object,
331             error => "Unknown computation function &$f_name, " . "expected &element or &index"
332 19         78 ) unless defined $method_name;
333              
334 19 50       176 my $result = $value_object->$method_name();
    100          
    100          
335              
336             my $vt = $value_object->value_type;
337             if ( $vt =~ /^integer|number|boolean$/ ) {
338             $result = '"' . $result . '"';
339             }
340 19 50       43  
341             $return = \$result;
342              
343             Config::Model::Exception::Formula->throw(
344             object => $value_object,
345 19         77 error => "Missing $f_name attribute (method '$method_name' on "
346             . ref($value_object) . ")\n"
347 19         41 ) unless defined $result;
348 19 100       73 return $return;
349 4         9 }
350              
351             my ( $value_ref, $return,
352 19         28 $value_object, $variables_h, $replace_h, $check, $need_quote, $undef_is )
353             = @_;
354 19 50       36  
355             my @values = map { $$_ } @{$value_ref};
356              
357             if ( $logger->is_debug ) {
358             my @display = map { defined $_ ? $_ : '<undef>' } @values;
359 19         295 $logger->debug( "_compute called with values '", join( "','", @display ) );
360             }
361              
362             my $result = '';
363 558     558   758017  
364             # return undef if one value is undef
365             foreach my $v (@values) {
366             if ( defined $v or defined $undef_is ) {
367 558         1077 $result .= defined $v ? $v : $undef_is;
  1265         2895  
  558         1409  
368             }
369 558 100       2067 else {
370 28 50       135 $result = undef;
  56         143  
371 28         131 last;
372             }
373             }
374 558         4440  
375             return \$result;
376             }
377 558         1313  
378 1238 100 66     3036 my ( $name, $value_object, $variables_h, $replace_h, $check, $need_quote ) = @_;
379 1222 50       2963  
380             $logger->warn("Warning: No variable definition found for \$$name")
381             unless exists $variables_h->{$name};
382 16         25  
383 16         31 # $path can be a ref for test purpose, or can be undef if path is computed from another value
384             my $path = $variables_h->{$name};
385             my $my_res;
386              
387 558         9256 if ( $logger->is_debug ) {
388             my $str = defined $path ? $path : '<undef>';
389             $logger->debug("replace \$$name with path $str...");
390             }
391 198     198   135730  
392             if ( defined $path and $path =~ /[\$&]/ ) {
393             $logger->trace("skip name $name path '$path'");
394 198 50       661 $my_res = "\$$name"; # restore name that contain '$var'
395             }
396             elsif ( defined $path ) {
397 198         398  
398 198         276 $logger->trace("fetching var object '$name' with '$path'");
399              
400 198 100       570 $my_res = eval { $value_object->grab_value( step => $path, check => $check ); };
401 22 50       97  
402 22         66 if ($@) {
403             my $e = $@;
404             my $msg = $e ? $e->full_message : '';
405 198 100 100     2132 Config::Model::Exception::Model->throw(
    100          
406 7         32 object => $value_object,
407 7         52 error => "Compute argument '$name', error with '$path':\n" . $msg
408             );
409             }
410              
411 184         787 $logger->trace( "fetched var object '$name' with '$path', result '",
412             defined $my_res ? $my_res : '<undef>', "'" );
413 184         1411 }
  184         726  
414              
415 184 50       421 # my_res stays undef if $path if not defined
416 0         0  
417 0 0       0 # quote result if asked when calling compute
418 0         0 #my $quote = $need_quote || 0;
419             #if ($quote && $my_res) {
420             # $my_res =~ s/'/\\'/g;
421             # $my_res = "'$my_res'";
422             #}
423              
424 184 100       810 return \$my_res; # So I can return undef ... or a ref to undef
425             }
426              
427             $compute_grammar = << 'END_OF_GRAMMAR' ;
428             {
429              
430             # This grammar is compatible with Parse::RecDescent < 1.90 or >= 1.90
431             use strict;
432             use warnings ;
433             }
434              
435             # computed value may return undef even if parsing is done right. To
436             # avoid getting problems with Parse::RecDescent (where undef means
437 198         3520 # that the parsing did not match), we always return a scalar
438             # reference to the actual returned value
439              
440             # @arg is value_object, $variables_h, $replace_h, $check,$need_quote
441              
442             pre_compute: <skip:''> pre_value[@arg](s) {
443             # print "pre-compute on @{$item[-1]}\n";
444             my $str = join ( '', map { $$_ } @{ $item[-1] } ) ;
445             $return = \$str;
446             }
447              
448             pre_value:
449             <skip:''> '$replace' '{' /\s*/ pre_value[@arg] /\s*/ '}' {
450             $return = Config::Model::ValueComputer::_pre_replace($arg[2], ${ $item{pre_value} } ) ;
451             }
452             | <skip:''> function '(' /\s*/ up /\s*/ ')' {
453             $return = Config::Model::ValueComputer::_function_on_object($item{up},$item{function},$return,@arg ) ;
454             }
455             | <skip:''> '&' /\w+/ func_param(?) {
456             $return = Config::Model::ValueComputer::_function_alone($item[3],$return,@arg ) ;
457             }
458             | <skip:''> /\$( |\d+|_|!|&|@|{\^[A-Z]+})/ {
459             my $result = $item[-1] ;
460             $return = \$result ;
461             }
462             | object {
463             # print "pre_value handling \$foo\n";
464             my $object = $item{object};
465             my $result ="\$".$object ;
466             $return = \$result ;
467             }
468             | <skip:''> /[^\$&]*/ {
469             # print "pre_value copying '$item[-1]'\n";
470             my $result = $item[-1] ;
471             $return = \$result ;
472             }
473              
474             func_param: /\(\s*\)/
475              
476             up: /-\d+|-( ?-)*/
477              
478             compute: <skip:''> value[@arg](s) {
479             # if one value is undef, return undef;
480             Config::Model::ValueComputer::_compute($item[-1],$return,@arg ) ;
481             }
482              
483             value:
484             <skip:''> '$replace' '{' <commit> /\s*/ value_to_replace[@arg] /\s*/ '}' {
485             $return = Config::Model::ValueComputer::_replace($arg[2], ${ $item{value_to_replace} },@arg ) ;
486             }
487             | <skip:''> /\$(\d+|_)\b/ {
488             my $result = $item[-1] ;
489             $return = \$result ;
490             }
491             | <skip:''> object <commit> {
492             $return = Config::Model::ValueComputer::_value_from_object($item{object},@arg ) ;
493             1;
494             }
495             | <skip:''> /[^\$]*/ {
496             my $result = $item[-1] ;
497             $return = \$result ;
498             }
499              
500             value_to_replace:
501             <skip:''> object <commit> {
502             $return = Config::Model::ValueComputer::_value_from_object($item{object},@arg ) ;
503             1;
504             }
505             | <skip:''> /[\w\-\.+]*/ {
506             my $result = $item[-1] ;
507             $return = \$result ;
508             }
509            
510             object: <skip:''> /\$/ /[a-zA-Z]\w*/
511              
512             function: <skip:''> '&' /\w+/
513              
514             END_OF_GRAMMAR
515              
516             __PACKAGE__->meta->make_immutable;
517              
518             1;
519              
520             # ABSTRACT: Provides configuration value computation
521              
522              
523             =pod
524              
525             =encoding UTF-8
526              
527             =head1 NAME
528              
529             Config::Model::ValueComputer - Provides configuration value computation
530              
531             =head1 VERSION
532              
533             version 2.151
534              
535             =head1 SYNOPSIS
536              
537             use Config::Model;
538              
539             # define configuration tree object
540             my $model = Config::Model->new;
541             $model ->create_config_class (
542             name => "MyClass",
543              
544             element => [
545             [qw/av bv/] => {
546             type => 'leaf',
547             value_type => 'integer',
548             },
549             compute_int => {
550             type => 'leaf',
551             value_type => 'integer',
552             compute => {
553             formula => '$a + $b',
554             variables => { a => '- av', b => '- bv'}
555             },
556             },
557             ],
558             );
559              
560             my $inst = $model->instance(root_class_name => 'MyClass' );
561              
562             my $root = $inst->config_root ;
563              
564             # put data
565             $root->load( steps => 'av=33 bv=9' );
566              
567             print "Computed value is ",$root->grab_value('compute_int'),"\n";
568             # Computed value is 42
569              
570             =head1 DESCRIPTION
571              
572             This class provides a way to compute a configuration value. This
573             computation uses a formula and some other configuration values from
574             the configuration tree.
575              
576             The computed value can be overridden, in other words, the computed
577             value can be used as a default value.
578              
579             =head1 Computed value declaration
580              
581             A computed value must be declared in a 'leaf' element. The leaf element
582             must have a C<compute> argument pointing to a hash ref.
583              
584             This array ref contains:
585              
586             =over
587              
588             =item *
589              
590             A string formula that use variables and replace function.
591              
592             =item *
593              
594             A set of variable and their relative location in the tree (using the
595             notation explained in
596             L<grab method|Config::Model::Role::Grab/grab">
597              
598             =item *
599              
600             An optional set of replace rules.
601              
602             =item *
603              
604             An optional parameter to force a Perl eval of a string.
605              
606             =back
607              
608             B<Note>: A variable must point to a valid location in the configuration
609             tree. Even when C<&index()> or C<$replace{}> is used. After substitution
610             of these functions, the string is used as a path (See
611             L<grab|Config::Model::Role::Grab/grab">) starting from the
612             computed value. Hence the path must begin with C<!> to go back to root
613             node, or C<-> to go up a level.
614              
615             =head2 Compute formula
616              
617             The first element of the C<compute> array ref must be a string that
618             contains the computation algorithm (i.e. a formula for arithmetic
619             computation for integer values or a string template for string
620             values).
621              
622             This string or formula should contain variables (like C<$foo> or
623             C<$bar>). Note that these variables are not interpolated by Perl.
624              
625             For instance:
626              
627             'My cat has $nb legs'
628             '$m * $c**2'
629              
630             This string or formula may also contain:
631              
632             =over
633              
634             =item *
635              
636             The index value of the current object : C<&index> or C<&index()>.
637              
638             =item *
639              
640             The index value of a parent object: C<&index(-)>. Ancestor index value can be retrieved
641             with C<&index(-2)> or C<&index(-3)> or C<&index(- -)> or C<&index(- - -)>
642              
643             =item *
644              
645             The element name of the current object: C<&element> or C<&element()>.
646              
647             =item *
648              
649             The element name of a parent object: C<&element(-)>. Likewise, ancestor element name
650             can be retrieved with C<&element(-2)> or C<&element(-3)>.
651              
652             =item *
653              
654             The full location (path) of the current object: C<&location> or C<&location()>.
655              
656             =back
657              
658             For instance, you could have this template string:
659              
660             'my element is &element, my index is &index' .
661             'upper element is &element(-), upper index is &index(-)',
662              
663             If you need to perform more complex operations than substitution, like
664             extraction with regular expressions, you can force an eval done by
665             Perl with C<< use_eval => 1 >>. In this case, the result of the eval
666             is used as the computed value.
667              
668             For instance:
669              
670             # extract host from url
671             compute => {
672             formula => '$old =~ m!http://[\w\.]+(?::\d+)?(/.*)!; $1 ;',
673             variables => { old => '- url' } ,
674             use_eval => 1 ,
675             },
676              
677             # capitalize
678             compute => {
679             formula => 'uc($old)',
680             variables => { old => '- small_caps' } ,
681             use_eval => 1
682             }
683              
684             =head2 Compute variables
685              
686             Compute variables are a set of C<< key => value >> pairs that define
687             the variables used in the specified formula. The key is a variable name used in
688             the string that represents the formula. The value is a string that is used to get
689             the correct L<Value|Config::Model::Value> object.
690              
691             In this numeric example, C<result> default value is C<av + bv>:
692              
693             element => [
694             av => {
695             type => 'leaf',
696             value_type => 'integer'
697             },
698             bv => {
699             type => 'leaf',
700             value_type => 'integer'
701             },
702             result => {
703             type => 'leaf',
704             value_type => 'integer',
705             compute => {
706             formula => '$a + $b' ,
707             variables => { a => '- av', b => '- bv' },
708             }
709             }
710             ]
711              
712             In this string example, the default value of the C<Comp> element is
713             actually a string made of "C<macro is >" and the value of the
714             "C<macro>" element of the object located 2 nodes above:
715              
716             comp => {
717             type => 'leaf',
718             value_type => 'string',
719             compute => {
720             formula => '"macro is $m"' ,
721             variables => { m => '- - macro' }
722             }
723             }
724              
725             =head2 Compute replace
726              
727             Sometime, using the value of a tree leaf is not enough and you need to
728             substitute a replacement for any value you can get. This replacement
729             can be done using a hash like notation within the formula using the
730             C<%replace> hash.
731              
732             For instance, if you want to display a summary of a config, you can do :
733              
734             compute_with_replace => {
735             formula => '$replace{$who} is the $replace{$what} of $replace{$country}',
736             variables => {
737             who => '! who' ,
738             what => '! what' ,
739             country => '- country',
740             },
741             replace => {
742             chief => 'president',
743             America => 'USA'
744             }
745             }
746              
747             =head2 Complex formula
748              
749             C<&index>, C<&element>, and replace can be combined. But the
750             argument of C<&element> or C<&index> can only be a value object
751             specification (I.e. something like 'C<- - foo>'), it cannot be a value
752             replacement of another C<&element> or C<&index>.
753              
754             I.e. C<&element($foo)> is ok, but C<&element(&index($foo))> is not allowed.
755              
756             =head2 computed variable
757              
758             Compute variables can themselves be computed :
759              
760             compute => {
761             formula => 'get_element is $replace{$s}, indirect value is \'$v\'',
762             variables => {
763             s => '! $where',
764             where => '! where_is_element',
765             v => '! $replace{$s}',
766             }
767             replace => {
768             m_value_element => 'm_value',
769             compute_element => 'compute'
770             }
771             }
772              
773             Be sure not to specify a loop when doing recursive computation.
774              
775             The function C<&index> C<&element> and C<&location> are also allowed.
776              
777             =head2 compute override
778              
779             In some case, a computed value must be interpreted as a default value
780             and the user must be able to override this computed default value. In
781             this case, you must use C<< allow_override => 1 >> with the
782             compute parameter:
783              
784             computed_value_with_override => {
785             type => 'leaf',
786             value_type => 'string',
787             compute => {
788             formula => '"macro is $m"' ,
789             variables => { m => '- - macro' } ,
790             allow_override => 1,
791             }
792             }
793              
794             This computed default value is written to the configuration file.
795              
796             This default value may be already known by the application so the computed value
797             should B<not> be written to the configuration file. The computed value is interesting
798             because it cab be shown to the user. In this case, use the C<use_as_upstream_default>
799             parameter:
800              
801             compute_known_upstream => {
802             type => 'leaf',
803             value_type => 'string',
804             compute => {
805             formula => '"macro is $m"' ,
806             variables => { m => '- - macro' } ,
807             use_as_upstream_default => 1,
808             }
809             }
810              
811             C<use_as_upstream_default> implies C<allow_override>.
812              
813             =head2 Undefined variables
814              
815             You may need to compute value where one of the variables (i.e. other configuration
816             parameter) is undefined. By default, any formula yields an undefined value if one
817             variable is undefined.
818              
819             You may change this behavior with C<undef_is> parameter. With this
820             parameter, you can specify a "fallback" value that is used in your
821             formula instead of an undefined variable.
822              
823             The most useful fallback values are:
824              
825             undef_is => "''", # for string values
826             undef_is => 0 , # for integers, boolean values
827              
828             Example:
829              
830             Source => {
831             type => 'leaf',
832             value_type => 'string',
833             mandatory => 1,
834             migrate_from => {
835             use_eval => 1,
836             formula => '$old || $older ;',
837             undef_is => "''",
838             variables => {
839             older => '- Original-Source-Location',
840             old => '- Upstream-Source'
841             }
842             },
843             },
844             [qw/Upstream-Source Original-Source-Location/] => {
845             value_type => 'string',
846             status => 'deprecated',
847             type => 'leaf'
848             }
849              
850             =head1 Examples
851              
852             =head2 String substitution
853              
854             [qw/sav sbv/] => {
855             type => 'leaf',
856             value_type => 'string',
857             },
858             compute_string => {
859             type => 'leaf',
860             value_type => 'string',
861             compute => {
862             formula => 'meet $a and $b',
863             variables => { '- sav', b => '- sbv' }
864             },
865             },
866              
867             =head2 Computation with on-the-fly replacement
868              
869             compute_with_replace => {
870             type => 'leaf',
871             value_type => 'string',
872             compute => {
873             formula =>
874             '$replace{$who} is the $replace{$what} of $replace{$country}',
875             variables => {
876             who => '! who',
877             what => '! what',
878             country => '- country',
879             },
880             replace => {
881             chief => 'president',
882             America => 'USA'
883             },
884             },
885             },
886              
887             =head2 Extract data from a value using a Perl regexp
888              
889             Extract the host name from an URL:
890              
891             url => {
892             type => 'leaf',
893             value_type => 'uniline'
894             },
895             extract_host_from_url => {
896             type => 'leaf',
897             value_type => 'uniline',
898             compute => {
899             formula => '$old =~ m!http://([\w\.]+)!; $1 ;',
900             variables => { old => '- url' },
901             use_eval => 1,
902             },
903             },
904              
905             =head2 copy hash example
906              
907             Copying a hash may not be useful, but the using C<&index()> in a variable can be. Here's an example
908             where the hashes contain leaves.
909              
910             The model is set up so that the content of C<copy_from>
911             is copied into C<copy_to> hash:
912              
913             copy_from => {
914             'type' => 'hash',
915             'index_type' => 'string',
916             'cargo' => {
917             'config_class_name' => 'From',
918             'type' => 'node'
919             },
920             },
921             copy_to => {
922             'type' => 'hash',
923             'index_type' => 'string',
924             'cargo' => {
925             'type' => 'leaf',
926             'value_type' => 'uniline',
927             'compute' => {
928             'formula' => '$copied',
929             'variables' => {
930             'copied' => '- copy_from:&index()'
931             }
932             },
933             },
934             },
935              
936             Hash copy is also possible when the hash contains node. Here's an example where
937             the data to be copied is stored within a node. The main class has 2 hash elements:
938              
939             copy_from => {
940             'type' => 'hash',
941             'index_type' => 'string',
942             'cargo' => {
943             'config_class_name' => 'From',
944             'type' => 'node'
945             },
946             },
947             copy_to => {
948             'type' => 'hash',
949             'index_type' => 'string',
950             'cargo' => {
951             'config_class_name' => 'To',
952             'type' => 'node'
953             },
954             },
955              
956             The Class to copy from is quite short:
957              
958             'name' => 'From',
959             'element' => [
960             name => {
961             'type' => 'leaf',
962             'value_type' => 'uniline',
963             }
964             ]
965              
966             Here the class to copy to:
967              
968             'name' => 'To',
969             'element' => [
970             name => {
971             'type' => 'leaf',
972             'value_type' => 'uniline',
973             'compute' => {
974             'formula' => '$copied',
975             'variables' => {
976             'copied' => '! copy_from:&index(-) name'
977             }
978             },
979             }
980             ]
981              
982             =head1 AUTHOR
983              
984             Dominique Dumont, (ddumont at cpan dot org)
985              
986             =head1 SEE ALSO
987              
988             L<Config::Model>,
989             L<Config::Model::Instance>,
990             L<Config::Model::Value>
991              
992             =head1 AUTHOR
993              
994             Dominique Dumont
995              
996             =head1 COPYRIGHT AND LICENSE
997              
998             This software is Copyright (c) 2005-2022 by Dominique Dumont.
999              
1000             This is free software, licensed under:
1001              
1002             The GNU Lesser General Public License, Version 2.1, February 1999
1003              
1004             =cut