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   381 use MouseX::StrictConstructor;
  59         118  
  59         1569  
13 59     59   24093  
  59         135  
  59         318  
14             # use Scalar::Util qw(weaken) ;
15             use Carp;
16 59     59   8025 use Parse::RecDescent 1.90.0;
  59         1418  
  59         4125  
17 59     59   328 use Data::Dumper ();
  59         886  
  59         318  
18 59     59   1975 use Log::Log4perl qw(get_logger :levels);
  59         131  
  59         1078  
19 59     59   268  
  59         104  
  59         496  
20             use vars qw($compute_grammar $compute_parser);
21 59     59   7274  
  59         111  
  59         3555  
22             use feature qw/postderef signatures/;
23 59     59   319 no warnings qw/experimental::postderef experimental::signatures/;
  59         122  
  59         4366  
24 59     59   337  
  59         117  
  59         92921  
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   390 my $need_quote = 0;
58             $need_quote = 1 if $self->{use_eval} and $self->{value_type} !~ /(integer|number|boolean)/;
59 188         337 return $need_quote;
60 188 100 66     729 }
61 188         1709  
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   380  
75             return defined $sui && $sui eq "''" ? ''
76 185         518 : 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 564  
86             $logger->trace("called with formula: $self->{formula}");
87             # must make a first pass at computation to subsitute index and
88 188   66     717 # element values. leaves $xxx outside of &index or &element untouched
89             my $result_r =
90 188         1422230 $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         2956 return;
96             }
97 188 50       416740  
98 188 50       1914 my $check = $args{check} || 'yes';
99 188         992  
100             my $pre_formula = $self->{pre_formula};
101             $logger->trace("called with pre_formula: $pre_formula");
102 592     592 1 856 my $variables = $self->compute_variables( check => $check );
  592         846  
  592         878  
  592         757  
103 592   100     1955  
104             die "internal error" unless defined $variables;
105 592         1122  
106 592         2074 my $result;
107 592         4820 my @parser_args = (
108             $self->{value_object}, $variables, $self->{replace}, $check, $self->{need_quote},
109 592 50       1425 $self->undef_replacement
110             );
111 592         798  
112             if ( $self->{use_eval}
113             or $self->{value_type} =~ /(integer|number|boolean)/ ) {
114 592         3052 $logger->trace("will use eval");
115             my $all_defined = 1;
116             my %__vars;
117 592 100 100     3603 foreach my $key ( sort keys %$variables ) {
118              
119 68         179 # no need to get variable if not used in formula;
120 68         424 next unless index( $pre_formula, $key ) > 0;
121 68         92 my $vr = _value_from_object( $key, @parser_args );
122 68         160 my $v = $$vr;
123             $v = $self->undef_replacement unless defined $v;
124             $logger->debug( "compute: var $key -> ", ( defined $v ? $v : '<undef>' ) );
125 95 50       240 # security: $v are stored in %__vars hash, so they are
126 95         180 # used by eval'ed code, but not directly eval'ed
127 95         163 if ( defined $v ) { $__vars{$key} = $v }
128 95 100       202 else { $all_defined = 0; }
129 95 100       319 }
130              
131             if ($all_defined) {
132 95 100       646 my $formula = $pre_formula;
  87         222  
133 8         18 $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       149 if ($@) {
137 61         104 Config::Model::Exception::Formula->throw(
138 61 100       328 object => $self->{value_object},
  104         472  
139 61         236 error => "Eval of formula '$formula' failed:\n$@"
140 61     1   5252 . "Make sure that your element is indeed "
  1     1   8  
  1     1   3  
  1     1   177  
  1     1   6  
  1     1   1  
  1     1   85  
  1         6  
  1         2  
  1         79  
  1         5  
  1         2  
  1         84  
  1         7  
  1         2  
  1         92  
  1         6  
  1         2  
  1         95  
  1         6  
  1         4  
  1         86  
141 61 50       338 . "'$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         1838 #$result = $self->{computed_formula} = $formula;
153 524         6190 }
154              
155 524         20513 $logger->debug( "compute result is '" . ( defined $result ? $result : '<undef>' ) . "'" );
156              
157             return $result;
158             }
159              
160 592 100       3091 my $check = $args{check} || 'yes';
161             $logger->trace("compute_info called with $self->{formula}");
162 592         6054  
163             my $orig_variables = $self->{variables};
164             my $variables = $self->compute_variables;
165 8     8 0 18 my $str = "value is computed from '$self->{formula}'";
  8         16  
  8         14  
  8         13  
166 8   50     62  
167 8         65 return $str unless defined $variables;
168              
169 8         57 if (%$variables) {
170 8         23 $str .= ", where ";
171 8         34 foreach my $k ( sort keys %$variables ) {
172             my $u_val = $variables->{$k};
173 8 50       36 if ( ref($u_val) ) {
174             foreach (sort keys %$u_val) {
175 8 100       25 $str .= "\n\t\t'\$$k" . "{$_} is converted to '$orig_variables->{$k}{$_}'";
176 7         15 }
177 7         19 }
178 14         26 else {
179 14 50       27 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         18 Config::Model::Exception::Model->throw(
186 14 100       28 object => $self,
187 11         20 error => "Compute variable:\n" . $msg
  11         35  
188 11 50       40 );
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         49 #$str .= " (evaluated as '$self->{computed_formula}')"
203 14 100       42 # 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         47  
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 976 if $logger->is_trace;
  658         914  
  658         1171  
  658         830  
217 658   100     1843  
218             # apply a compute on all variables until no $var is left
219             my $var_left = scalar( keys %variables ) + 1;
220              
221 658         914 while ($var_left) {
  658         1867  
222 658 100       1604 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         4855  
227             #next if ref($value); # skip replacement rules
228 658         1450 $logger->trace("key '$key', value '$value', left $var_left");
229 663         1085 next unless $value =~ /\$|&/;
230 663         1397  
231 258         588 my $pre_res_r =
232 258 100       545 $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 256         875 $variables{$key} = $$pre_res_r;
236 256 100       2339 $logger->trace( "variable after pre_compute: ", join( " ", keys %variables ) )
237             if $logger->is_trace;
238              
239             if ( $$pre_res_r =~ /\$/ ) {
240 40         309 # variables needs to be evaluated
241 40         93497 my $res_ref =
242 40         359 $compute_parser->compute( $$pre_res_r, 1, $self->{value_object}, \%variables,
243 40 50       109 $self->{replace}, $check );
244              
245             #return undef unless defined $res ;
246 40 100       395 $variables{$key} = $$res_ref;
247             $logger->trace( "variable after compute: ", join( " ", keys %variables ) )
248             if $logger->is_trace;
249             }
250 30         247 {
251             no warnings "uninitialized"; ## no critic (TestingAndDebugging::ProhibitNoWarnings)
252             $logger->trace("result $key -> '$variables{$key}' left '$var_left'");
253 30         1293 }
254 30 50       92 }
255              
256             my @var_left = grep { defined $variables{$_} && $variables{$_} =~ /[\$&]/ }
257             sort keys %variables;
258 59     59   519  
  59         122  
  59         69000  
  40         273  
259 40         187 $var_left = @var_left;
260              
261             Config::Model::Exception::Formula->throw(
262             object => $self->{value_object},
263 663 100       1789 error => "Can't resolve user variable: '" . join( "','", @var_left ) . "'"
  258         1166  
264             ) unless ( $var_left < $old_var_left );
265             }
266 663         1060  
267             $logger->trace("done");
268             return \%variables;
269             }
270 663 50       1976  
271             my ( $replace_h, $pre_value ) = @_;
272              
273             $logger->trace("value: _pre_replace called with value '$pre_value'");
274 658         1686 my $result =
275 658         4553 exists $replace_h->{$pre_value}
276             ? $replace_h->{$pre_value}
277             : '$replace{' . $pre_value . '}';
278             return \$result;
279 23     23   41918 }
280              
281 23         152 my ( $replace_h, $value, $value_object, $variables, $replace, $check, $need_quote, $undef_is )
282             = @_;
283              
284 23 100       263 if ( $logger->is_trace ) {
285             my $str = defined $value ? $value : '<undef>';
286 23         382 $logger->trace("value: _replace called with value '$str'");
287             }
288              
289             my $result;
290 24     24   4935 if ( defined $value and $value =~ /\$/ ) {
291              
292             # must keep original variable
293 24 50       76 $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 24         159 }
299 24 100 100     152 return \$result;
    100          
300             }
301              
302 5         17 my ( $up, $function, $return, $value_object, $variables_h, $replace_h, $check, $need_quote ) =
303             @_;
304              
305 10         28 $logger->trace("handling &$function($up) ");
306 10 100       32  
307             my $target = $value_object->eval_function($function, $up, $check);
308 24         396 $return = \$target ;
309              
310             # print "\&foo(...) result = ",$$return," \n";
311              
312 20     20   29160 # make sure that result of function is quoted (avoid bareword errors)
313             $$return = '"' . $$return . '"' if $need_quote;
314              
315 20         120 $logger->debug("&$function(...) returns $$return");
316             return $return;
317 20         225 }
318 20         37  
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       52 my $method_name =
324             $f_name eq 'element' ? 'element_name'
325 20         79 : $f_name eq 'index' ? 'index_value'
326 20         490 : $f_name eq 'location' ? 'location'
327             : undef;
328              
329             Config::Model::Exception::Formula->throw(
330 19     19   44253 object => $value_object,
331             error => "Unknown computation function &$f_name, " . "expected &element or &index"
332 19         109 ) unless defined $method_name;
333              
334 19 50       227 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       57  
341             $return = \$result;
342              
343             Config::Model::Exception::Formula->throw(
344             object => $value_object,
345 19         108 error => "Missing $f_name attribute (method '$method_name' on "
346             . ref($value_object) . ")\n"
347 19         65 ) unless defined $result;
348 19 100       84 return $return;
349 4         10 }
350              
351             my ( $value_ref, $return,
352 19         37 $value_object, $variables_h, $replace_h, $check, $need_quote, $undef_is )
353             = @_;
354 19 50       49  
355             my @values = map { $$_ } @{$value_ref};
356              
357             if ( $logger->is_debug ) {
358             my @display = map { defined $_ ? $_ : '<undef>' } @values;
359 19         314 $logger->debug( "_compute called with values '", join( "','", @display ) );
360             }
361              
362             my $result = '';
363 556     556   737319  
364             # return undef if one value is undef
365             foreach my $v (@values) {
366             if ( defined $v or defined $undef_is ) {
367 556         1026 $result .= defined $v ? $v : $undef_is;
  1259         2789  
  556         1279  
368             }
369 556 100       1948 else {
370 28 50       107 $result = undef;
  56         117  
371 28         94 last;
372             }
373             }
374 556         4122  
375             return \$result;
376             }
377 556         1236  
378 1232 100 66     2828 my ( $name, $value_object, $variables_h, $replace_h, $check, $need_quote ) = @_;
379 1216 50       2791  
380             $logger->warn("Warning: No variable definition found for \$$name")
381             unless exists $variables_h->{$name};
382 16         35  
383 16         27 # $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 556         9031 if ( $logger->is_debug ) {
388             my $str = defined $path ? $path : '<undef>';
389             $logger->debug("replace \$$name with path $str...");
390             }
391 196     196   132494  
392             if ( defined $path and $path =~ /[\$&]/ ) {
393             $logger->trace("skip name $name path '$path'");
394 196 50       507 $my_res = "\$$name"; # restore name that contain '$var'
395             }
396             elsif ( defined $path ) {
397 196         424  
398 196         293 $logger->trace("fetching var object '$name' with '$path'");
399              
400 196 100       560 $my_res = eval { $value_object->grab_value( step => $path, check => $check ); };
401 22 50       102  
402 22         66 if ($@) {
403             my $e = $@;
404             my $msg = $e ? $e->full_message : '';
405 196 100 100     2144 Config::Model::Exception::Model->throw(
    100          
406 5         28 object => $value_object,
407 5         43 error => "Compute argument '$name', error with '$path':\n" . $msg
408             );
409             }
410              
411 184         745 $logger->trace( "fetched var object '$name' with '$path', result '",
412             defined $my_res ? $my_res : '<undef>', "'" );
413 184         1390 }
  184         684  
414              
415 184 50       419 # 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       790 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 196         3551 # 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.152
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