File Coverage

blib/lib/Config/Model/ValueComputer.pm
Criterion Covered Total %
statement 218 229 95.2
branch 86 116 74.1
condition 23 27 85.1
subroutine 29 29 100.0
pod 2 4 50.0
total 358 405 88.4


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