File Coverage

blib/lib/Config/Model/CheckList.pm
Criterion Covered Total %
statement 329 380 86.5
branch 160 214 74.7
condition 60 104 57.6
subroutine 53 60 88.3
pod 29 47 61.7
total 631 805 78.3


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 19     19   129 use 5.010;
  19         42  
  19         150  
13 19     19   8124  
  19         64  
14             use Config::Model::Exception;
15 19     19   103 use Config::Model::IdElementReference;
  19         34  
  19         411  
16 19     19   89 use Config::Model::Warper;
  19         43  
  19         480  
17 19     19   92 use List::MoreUtils qw/any none/;
  19         34  
  19         452  
18 19     19   101 use Carp;
  19         33  
  19         314  
19 19     19   13029 use Log::Log4perl qw(get_logger :levels);
  19         32  
  19         1332  
20 19     19   117 use Storable qw/dclone/;
  19         36  
  19         185  
21 19     19   2382  
  19         57  
  19         33842  
22             extends qw/Config::Model::AnyThing/;
23              
24             with "Config::Model::Role::WarpMaster";
25             with "Config::Model::Role::Grab";
26             with "Config::Model::Role::HelpAsText";
27             with "Config::Model::Role::ComputeFunction";
28              
29             my $logger = get_logger("Tree.Element.CheckList");
30             my $user_logger = get_logger("User");
31              
32             my @introspect_params = qw/refer_to computed_refer_to/;
33              
34             my @accessible_params = qw/default_list upstream_default_list choice ordered/;
35             my @allowed_warp_params = ( @accessible_params, qw/level/ );
36              
37             has [qw/backup data preset layered/] => ( is => 'rw', isa => 'HashRef', default => sub { {}; } );
38             has computed_refer_to => ( is => 'rw', isa => 'Maybe[HashRef]' );
39             has [qw/refer_to/] => ( is => 'rw', isa => 'Str' );
40             has [qw/ordered_data choice/] => ( is => 'rw', isa => 'ArrayRef', default => sub { []; } );
41             has [qw/ordered/] => ( is => 'ro', isa => 'Bool' );
42              
43             has [qw/warp help/] => ( is => 'rw', isa => 'Maybe[HashRef]' );
44              
45             around BUILDARGS => sub {
46             my $orig = shift;
47             my $class = shift;
48             my %args = @_;
49             my %h = map { ( $_ => $args{$_} ); } grep { defined $args{$_} } @allowed_warp_params;
50             return $class->$orig( backup => dclone( \%h ), @_ );
51             };
52              
53             my $self = shift;
54              
55 57     57 1 119 if ( defined $self->refer_to or defined $self->computed_refer_to ) {
56             $self->submit_to_refer_to();
57 57 100 100     417 }
58 29         106  
59             $self->set_properties(); # set will use backup data
60              
61 57         293 if ( defined $self->warp ) {
62             my $warp_info = $self->warp;
63 57 100       705 $self->{warper} = Config::Model::Warper->new(
64 3         10 warped_object => $self,
65 3         77 %$warp_info,
66             allowed => \@allowed_warp_params
67             );
68             }
69              
70             $self->cl_init;
71              
72 57         235 $logger->info( "Created check_list element " . $self->element_name );
73             return $self;
74 57         460 }
75 57         964  
76             my $self = shift;
77              
78             $self->warp if ( $self->{warp} );
79 57     57 0 112  
80             if ( defined $self->{ref_object} ) {
81 57 100       181 my $level = $self->parent->get_element_property(
82             element => $self->{element_name},
83 57 100       180 property => 'level',
84             );
85             $self->{ref_object}->get_choice_from_referred_to if $level ne 'hidden';
86 29         290 }
87             }
88 29 100       269  
89             my $self = shift;
90             my $name = $self->{parent}->name . ' ' . $self->{element_name};
91             return $name;
92             }
93 524     524 1 772  
94 524         1580  
95 524         3864 # warning : call to 'set' are not cumulative. Default value are always
96             # restored. Lest keeping track of what was modified with 'set' is
97             # too hard for the user.
98 159     159 0 1169 my $self = shift;
99              
100             # cleanup all parameters that are handled by warp
101             for (@allowed_warp_params) {
102             delete $self->{$_},
103             }
104 62     62 0 137  
105             if ( $logger->is_trace() ) {
106             my %h = @_;
107 62         180 my $keys = join( ',', keys %h );
108 310         600 $logger->trace("set_properties called on $self->{element_name} with $keys");
109             }
110              
111 62 50       273 # merge data passed to the constructor with data passed to set
112 0         0 my %args = ( %{ $self->{backup} }, @_ );
113 0         0  
114 0         0 # these are handled by Node or Warper
115             for (qw/level/) {
116             delete $args{$_}
117             }
118 62         543  
  62         347  
119             $self->{ordered} = delete $args{ordered} || 0;
120              
121 62         166 if ( defined $args{choice} ) {
122 62         149 my @choice = @{ delete $args{choice} };
123             $self->{default_choice} = \@choice;
124             $self->setup_choice(@choice);
125 62   100     337 }
126              
127 62 100       195 if ( defined $args{default} ) {
128 30         54 $logger->warn($self->name, ": default param is deprecated, use default_list");
  30         157  
129 30         80 $args{default_list} = delete $args{default};
130 30         115 }
131              
132             if ( defined $args{default_list} ) {
133 62 50       188 $self->{default_list} = delete $args{default_list};
134 0         0 }
135 0         0  
136             # store default data in a hash (more convenient)
137             $self->{default_data} = { map { $_ => 1 } @{ $self->{default_list} } };
138 62 100       197  
139 3         8 if ( defined $args{upstream_default_list} ) {
140             $self->{upstream_default_list} = delete $args{upstream_default_list};
141             }
142              
143 62         125 # store upstream default data in a hash (more convenient)
  6         16  
  62         301  
144             $self->{upstream_default_data} =
145 62 100       218 { map { $_ => 1 } @{ $self->{upstream_default_list} } };
146 3         8  
147             Config::Model::Exception::Model->throw(
148             object => $self,
149             error => "Unexpected parameters :" . join( ' ', keys %args ) ) if scalar keys %args;
150              
151 62         110 if ( $self->has_warped_slaves ) {
  16         26  
  62         203  
152             my $hash = $self->get_checked_list_as_hash; # force scalar context
153 62 50       227 $self->trigger_warp($hash, $self->fetch);
154             }
155             }
156              
157 62 50       306 my $self = shift;
158 0         0 my @choice = ref $_[0] ? @{ $_[0] } : @_;
159 0         0  
160             $logger->trace("CheckList $self->{element_name}: setup_choice with @choice");
161              
162             # store all enum values in a hash. This way, checking
163             # whether a value is present in the enum set is easier
164 134     134 0 307 delete $self->{choice_hash} if defined $self->{choice_hash};
165 134 50       558 for (@choice) {
  0         0  
166             $self->{choice_hash}{$_} = 1;
167 134         809 }
168              
169             $self->{choice} = \@choice;
170              
171 134 100       1356 # cleanup current preset and data if it does not fit current choices
172 134         302 foreach my $field (qw/preset data layered/) {
173 832         1458 next unless defined $self->{$field}; # do not create if not present
174             foreach my $item ( keys %{ $self->{$field} } ) {
175             delete $self->{$field}{$item} unless defined $self->{choice_hash}{$item};
176 134         397 }
177             }
178             }
179 134         320  
180 402 50       864 # Need to extract Config::Model::Reference (used by Value, and maybe AnyId).
181 402         473  
  402         1114  
182 109 50       240 my $self = shift;
183              
184             if ( defined $self->refer_to ) {
185             $self->{ref_object} = Config::Model::IdElementReference->new(
186             refer_to => $self->refer_to,
187             config_elt => $self,
188             );
189             }
190 29     29 0 91 elsif ( defined $self->computed_refer_to ) {
191             $self->{ref_object} = Config::Model::IdElementReference->new(
192 29 100       120 computed_refer_to => $self->computed_refer_to,
    50          
193 28         421 config_elt => $self,
194             );
195             my $var = $self->{computed_refer_to}{variables};
196              
197             # refer_to registration is done for all element that are used as
198             # variable for complex reference (ie '- $foo' , {foo => '- bar'} )
199 1         18 foreach my $path ( values %$var ) {
200              
201             # is ref during test case
202             #print "path is '$path'\n";
203 1         5 next if $path =~ /\$/; # next if path also contain a variable
204             my $master = $self->grab($path);
205             next unless $master->can('register_dependency');
206             $master->register_dependency($self);
207 1         5 }
208             }
209             else {
210             croak "checklist submit_to_refer_to: undefined refer_to or computed_refer_to";
211 1 50       6 }
212 1         7 }
213 1 50       13  
214 1         6 my $self = shift;
215             $self->setup_choice(@_);
216             }
217              
218 0         0 my $self = shift;
219             return 'check_list';
220             }
221              
222              
223 104     104 0 216 my $self = shift;
224 104         350 return 'leaf';
225             }
226              
227              
228 138     138 1 236 # no operation. THere's no check_value method because a check list
229 138         319 # supposed to be always correct. Hence apply_fixes is empty.
230             }
231              
232 121     121 0 373 my $self = shift;
233             my %args = @_;
234              
235 121     121 1 199 return if $self->instance->initial_load and not $args{really};
236 121         245  
237             $self->SUPER::notify_change( %args, value_type => $self->value_type );
238              
239       12 0   # shake all warped or computed objects that depends on me
240             foreach my $s ( $self->get_warped_slaves ) {
241             $logger->debug( "calling notify_change on slave ", $s->name )
242             if $logger->is_debug;
243             $s->needs_check(1);
244             }
245             }
246 59     59 1 141  
247 59         212  
248             # does not check the validity, but check the item of the check_list
249 59 100 66     329 my $self = shift;
250             my @list = ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_;
251 55         225 my %args = ref $_[0] eq 'ARRAY' ? @_[ 1, $#_ ] : ( check => 'yes' );
252             my $check = $self->_check_check( $args{check} );
253              
254 55         283 if ( defined $self->{ref_object} ) {
255 8 50       87 $self->{ref_object}->get_choice_from_referred_to;
256             }
257 8         54  
258             my @changed;
259             for (@list) {
260             push @changed, $_ if $self->_store( $_, 1, $check )
261             }
262              
263             $self->notify_change( note => "check @changed" )
264 11     11 1 1770 unless $self->instance->initial_load;
265 11 50       55 }
  0         0  
266 11 50       48  
267 11         57 my $self = shift;
268             my $choice = shift;
269 11 100       43  
270 3         18 my $inst = $self->instance;
271             my $data_name =
272             $inst->preset ? 'preset'
273 11         23 : $inst->layered ? 'layered'
274 11         31 : 'data';
275 14 50       63 my $old_v = $self->{$data_name}{$choice};
276             my $changed = 0;
277             if ($old_v) {
278 9 50       97 $changed = 1;
279             }
280             delete $self->{$data_name}{$choice};
281              
282             if ( $self->{ordered} and $changed ) {
283 244     244 1 270 my $ord = $self->{ordered_data};
284 244         285 my @new = grep { $_ ne $choice } @$ord;
285             $self->{ordered_data} = \@new;
286 244         346 }
287 244 100       531 return $changed;
    50          
288             }
289              
290             # internal
291 244         371 my ( $self, $choice, $value, $check ) = @_;
292 244         259  
293 244 100       331 my $inst = $self->instance;
294 34         41  
295             if ( $value != 0 and $value != 1 ) {
296 244         308 Config::Model::Exception::WrongValue->throw(
297             error => "store: check item value must be boolean, " . "not '$value'.",
298 244 50 33     374 object => $self
299 0         0 );
300 0         0 return;
  0         0  
301 0         0 }
302              
303 244         333 my $ok = $self->{choice_hash}{$choice} || 0;
304             my $changed = 0;
305              
306             if ($ok) {
307             my $data_name =
308 706     706   13296 $inst->preset ? 'preset'
309             : $inst->layered ? 'layered'
310 706         1202 : 'data';
311             my $old_v = $self->{$data_name}{$choice} ;
312 706 50 66     1438 if ( not defined $old_v or $old_v ne $value ) {
313 0         0 # no change notif when going from undef to 0 as the
314             # logical value does not change
315             {
316             no warnings qw/uninitialized/;
317 0         0 $changed = (!$old_v xor !$value);
318             }
319             $self->{$data_name}{$choice} = $value;
320 706   100     1266 }
321 706         810  
322             if ( $self->{ordered} and $value ) {
323 706 100       986 my $ord = $self->{ordered_data};
324 697 100       1639 push @$ord, $choice unless scalar grep { $choice eq $_ } @$ord;
    100          
325             }
326             }
327             else {
328 697         965 my $err_str =
329 697 100 100     1385 "Unknown check_list item '$choice'. Expected '"
330             . join( "', '", @{ $self->{choice} } ) . "'";
331             $err_str .= "\n\t" . $self->{ref_object}->reference_info
332             if defined $self->{ref_object};
333 19     19   187 if ($check eq 'yes') {
  19         40  
  19         54264  
  489         547  
334 489   75     1183 Config::Model::Exception::WrongValue->throw( error => $err_str, object => $self );
335             }
336 489         805 elsif ($check eq 'skip') {
337             $user_logger->warn($err_str);
338             }
339 697 100 100     1440 }
340 17         25  
341 17 100       34 if ( $ok
  41         82  
342             and $changed
343             and $self->has_warped_slaves
344             and not( $self->instance->layered or $self->instance->preset ) ) {
345             my $h = $self->get_checked_list_as_hash;
346             my $str = $self->fetch;
347 9         28 $self->trigger_warp($h , $str);
  9         45  
348             }
349 9 100       32  
350 9 100       37 return $changed;
    50          
351 4         51 }
352              
353             my $self = shift;
354 5         24 my $arg = shift;
355             my @list = ref $arg eq 'ARRAY' ? @$arg : ($arg, @_);
356             my %args = ref $arg eq 'ARRAY' ? ( check => 'yes', @_ ) : (check => 'yes');
357             my $check = $self->_check_check( $args{check} );
358 702 100 100     2092 return \@list, $check, \%args;
      100        
      33        
      66        
359             }
360              
361             my $self = shift;
362 4         74 my ($list, $check) = $self->get_arguments(@_);
363 4         15  
364 4         19 if ( defined $self->{ref_object} ) {
365             $self->{ref_object}->get_choice_from_referred_to;
366             }
367 702         2462  
368             my @changed;
369             for ( @$list ) {
370             push @changed, $_ if $self->_store( $_, 0, $check )
371 54     54 0 4266 }
372 54         106  
373 54 100       267 $self->notify_change( note => "uncheck @changed" )
374 54 100       269 unless $self->instance->initial_load;
375 54         212 }
376 54         203  
377             my $self = shift;
378             my @set = $self->get_checked_list(qw/mode custom/) ;
379             return scalar @set;
380 4     4 1 14 }
381 4         19  
382             {
383 4 50       17 my %accept_mode = map { ( $_ => 1 ) }
384 0         0 qw/custom standard preset default layered upstream_default non_upstream_default user backend/;
385              
386             my ($self, $mode) = @_;
387 4         10 if ( $mode and not defined $accept_mode{$mode} ) {
388 4         10 my $good_ones = join( ' or ', sort keys %accept_mode );
389 4 100       14 return "expected $good_ones as mode parameter, not $mode";
390             }
391             }
392 4 50       44 }
393              
394             my $self = shift;
395             my $choice = shift;
396             my %args = @_;
397 3     3 1 1471 my $mode = $args{mode} || '';
398 3         11 my $check = $self->_check_check( $args{check} );
399 3         15  
400             my $ok = $self->{choice_hash}{$choice} || 0;
401              
402             if ($ok) {
403              
404             if ( my $err = $self->is_bad_mode($mode) ) {
405             croak "is_checked: $err";
406             }
407 170     170 1 334  
408 170 50 66     784 my $dat = $self->{data}{$choice};
409 0         0 my $pre = $self->{preset}{$choice};
410 0         0 my $def = $self->{default_data}{$choice};
411             my $ud = $self->{upstream_default_data}{$choice};
412             my $lay = $self->{layered}{$choice};
413             my $std_v = $pre // $def // 0;
414             my $non_up_def = $dat // $pre // $lay // $def // 0;
415             my $user_v = $dat // $pre // $lay // $def // $ud // 0;
416 12     12 1 19  
417 12         18 my $result =
418 12         18 $mode eq 'custom' ? ( $dat && !$std_v ? 1 : 0 )
419 12   50     46 : $mode eq 'preset' ? $pre
420 12         42 : $mode eq 'layered' ? $lay
421             : $mode eq 'upstream_default' ? $ud
422 12   50     42 : $mode eq 'default' ? $def
423             : $mode eq 'standard' ? $std_v
424 12 50       24 : $mode eq 'non_upstream_default' ? $ud
    0          
425             : $mode eq 'user' ? $user_v
426 12 50       28 : $mode eq 'backend' ? $dat // $std_v
427 0         0 : $dat // $std_v;
428              
429             return $result;
430 12         29 }
431 12         21 elsif ( $check eq 'yes' ) {
432 12         18 my $err_str =
433 12         19 "Unknown check_list item '$choice'. Expected '"
434 12         18 . join( "', '", @{ $self->{choice} } ) . "'";
435 12   33     47 $err_str .= "\n\t" . $self->{ref_object}->reference_info
      50        
436 12   33     28 if defined $self->{ref_object};
      33        
      0        
      0        
437 12   33     25 Config::Model::Exception::WrongValue->throw(
      33        
      0        
      0        
      0        
438             error => $err_str,
439 12 0 0     76 object => $self
    50 0        
    50 33        
    50          
    50          
    50          
    50          
    50          
    50          
    50          
440             );
441             }
442             }
443              
444             # get_choice is always called when using check_list, so having a
445             # warp safety check here makes sense
446              
447             my $self = shift;
448              
449             if ( defined $self->{ref_object} ) {
450             $self->{ref_object}->get_choice_from_referred_to;
451 12         47 }
452              
453             if ( not defined $self->{choice} ) {
454             my $msg = "check_list element has no defined choice. " . $self->warp_error;
455             Config::Model::Exception::UnavailableElement->throw(
456 0         0 info => $msg,
  0         0  
457             object => $self->parent,
458 0 0       0 element => $self->element_name,
459 0         0 );
460             }
461              
462             return @{ $self->{choice} };
463             }
464              
465             my $self = shift;
466             return @{ $self->{default_choice} || [] };
467             }
468              
469             carp "get_builtin_choice is deprecated, use get_upstream_default_choice";
470 234     234 1 2097 goto &get_upstream_default_choice;
471             }
472 234 100       591  
473 74         364 my $self = shift;
474             return @{ $self->{upstream_default_data} || [] };
475             }
476 234 100       594  
477 1         9 my $self = shift;
478 1         7 my $help = $self->{help};
479              
480             return $help unless @_;
481              
482             my $on_value = shift;
483             return $help->{$on_value} if defined $help and defined $on_value;
484              
485 233         328 return;
  233         1268  
486             }
487              
488             my $self = shift;
489 104     104 0 187  
490 104 100       173 my @items = ('type: check_list');
  104         595  
491             if ( defined $self->refer_to ) {
492             push @items, "refer_to: " . $self->refer_to;
493             }
494 0     0 0 0 push @items, "ordered: " . ( $self->ordered ? 'yes' : 'no' );
495 0         0 return @items;
496             }
497              
498             my $self = shift;
499 0     0 0 0 # also triggers notify changes
500 0 0       0 for ($self->get_choice) {
  0         0  
501             $self->clear_item($_)
502             }
503             }
504 3     3 1 463  
505 3         12  
506             my $self = shift;
507 3 50       12 $self->{layered} = {};
508             }
509 3         9  
510 3 100 66     17 my %old_mode = ( built_in_list => 'upstream_default_list', );
511              
512 2         5 my $self = shift;
513             my %args = @_ > 1 ? @_ : ( mode => $_[0] );
514             my $mode = $args{mode} || '';
515              
516 1     1 1 3 foreach my $k ( keys %old_mode ) {
517             next unless $mode eq $k;
518 1         3 $mode = $old_mode{$k};
519 1 50       6 carp $self->location, " warning: deprecated mode parameter: $k, ", "expected $mode\n";
520 1         4 }
521              
522 1 50       7 if ( my $err = $self->is_bad_mode($mode)) {
523 1         3 croak "get_checked_list_as_hash: $err";
524             }
525              
526             my $dat = $self->{data};
527 8     8 1 10918 my $pre = $self->{preset};
528             my $def = $self->{default_data};
529 8         32 my $lay = $self->{layered};
530 208         306 my $ud = $self->{upstream_default_data};
531              
532             # fill empty hash result
533             my %h = map { $_ => 0 } $self->get_choice;
534 0     0 0 0  
535             my %predef = ( %$def, %$pre );
536             my %std = ( %$ud, %$lay, %$def, %$pre );
537 3     3 0 7  
538 3         19 # use _std_backup if all data values are null (no checked items by user)
539             my %old_dat = ( none { $_; } values %$dat ) ? %{ $self->{_std_backup} || {} } : %$dat;
540              
541             if ( not $mode and any { $_; } values %predef and none { $_; } values %old_dat ) {
542              
543             # changed from nothing to default checked list that must be written
544 158     158 1 1290 $self->{_std_backup} = { %$def, %$pre };
545 158 100       589 $self->notify_change( note => "use default checklist" );
546 158   100     554 }
547              
548 158         470 # custom test must compare the whole list at once, not just one item at a time.
549 158 50       440 my %result =
550 0         0 $mode eq 'custom' ? ( ( grep { $dat->{$_} xor $std{$_} } keys %h ) ? ( %$pre, %$dat ) : () )
551 0         0 : $mode eq 'preset' ? (%$pre)
552             : $mode eq 'layered' ? (%$lay)
553             : $mode eq 'upstream_default' ? (%$ud)
554 158 50       456 : $mode eq 'default' ? (%$def)
555 0         0 : $mode eq 'standard' ? %std
556             : $mode eq 'user' ? ( %h, %std, %$dat )
557             : ( %predef, %$dat );
558 158         326  
559 158         235 return wantarray ? %result : \%result;
560 158         310 }
561 158         275  
562 158         259 my $self = shift;
563              
564             my %h = $self->get_checked_list_as_hash(@_);
565 158         445 my @good_order = $self->{ordered} ? @{ $self->{ordered_data} } : sort keys %h;
  2406         3937  
566             my @res = grep { $h{$_} } @good_order;
567 158         614 return wantarray ? @res : \@res;
568 158         443 }
569              
570             my $self = shift;
571 158 100   219   1163 return join( ',', $self->get_checked_list(@_) );
  219 100       803  
  49         252  
572             }
573 158 100 100 5   1201  
  19   100     68  
  5         15  
574             my $self = shift;
575             return join( ',', $self->get_checked_list('custom') );
576 4         24 }
577 4         19  
578             my $self = shift;
579             return join( ',', $self->get_checked_list('preset') );
580             }
581              
582 158 100 100     1441 my $self = shift;
  384 100       953  
    100          
    100          
    100          
    100          
    100          
    100          
583             return join( ',', $self->get_checked_list('layered') );
584             }
585              
586             my $self = shift;
587             my $path = shift;
588             if ($path) {
589             Config::Model::Exception::User->throw(
590             object => $self,
591 158 100       1243 message => "get() called with a value with non-empty path: '$path'"
592             );
593             }
594             return $self->fetch(@_);
595 150     150 1 10775 }
596              
597 150         450 my ($self, $path, $list, %args) = @_;
598 150 100       981  
  15         44  
599 150         352 my $check_validity = $self->_check_check( $args{check} );
  1127         1490  
600 150 100       820 if ($path) {
601             Config::Model::Exception::User->throw(
602             object => $self,
603             message => "set() called with a value with non-empty path: '$path'"
604 68     68 1 1406 );
605 68         227 }
606              
607             my @list = split /,/, $list;
608             return $self->set_checked_list( \@list, check => $check_validity );
609 1     1 0 3 }
610 1         8  
611             goto &store;
612             }
613              
614 0     0 0 0 my $self = shift;
615 0         0 my %args =
616             @_ == 1 ? ( value => $_[0] )
617             : @_ == 3 ? ( 'value', @_ )
618             : @_;
619 0     0 0 0 my $check_validity = $self->_check_check( $args{check} );
620 0         0  
621             my @set = split /\s*,\s*/, $args{value};
622             foreach (@set) { s/^"|"$//g; s/\\"/"/g; }
623             $self->set_checked_list(\@set, check => $check_validity);
624 0     0 1 0 }
625 0         0  
626 0 0       0  
627 0         0 my $self = shift;
628             my ($list, $check) = $self->get_arguments(@_);
629              
630             $logger->trace("called with @$list");
631             my %set = map { $_ => 1 } @$list;
632 0         0 my @changed;
633              
634             foreach my $c ( $self->get_choice ) {
635             my $v = delete $set{$c} // 0;
636 3     3 1 1051 push @changed, "$c:$v" if $self->_store( $c, $v, $check );
637             }
638 3         18  
639 3 50       11 # Items left in %set are unknown. _store will handle the error
640 0         0 foreach my $item (keys %set) {
641             $self->_store( $item, 1, $check );
642             }
643              
644             $self->{ordered_data} = $list;
645              
646 3         16 $self->notify_change( note => "set_checked_list @changed" )
647 3         14 if @changed and not $self->instance->initial_load;
648             }
649              
650             my $self = shift;
651 17     17 1 75 my %check_list = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
652             my %args = ref $_[0] eq 'HASH' ? @_[ 1, $#_ ] : ( check => 'yes' );
653             my $check_validity = $self->_check_check( $args{check} );
654              
655 22     22 1 6911 foreach my $c ( $self->get_choice ) {
656 22 100       168 if ( defined $check_list{$c} ) {
    100          
657             $self->_store( $c, $check_list{$c}, $check_validity );
658             }
659             else {
660 22         109 $self->clear_item($c);
661             }
662 22         244 }
663 22         67 }
  59         131  
  59         126  
664 22         87  
665             my $self = shift;
666              
667 0     0 1 0 my %args = @_ > 1 ? @_ : ( data => shift );
668             my $data = $args{data};
669             my $check_validity = $self->_check_check( $args{check} );
670 47     47 1 11403  
671 47         197 if ( ref($data) eq 'ARRAY' ) {
672             $self->set_checked_list($data, check => $check_validity);
673 47         333 }
674 47         427 elsif ( ref($data) eq 'HASH' ) {
  139         350  
675 47         101 $self->set_checked_list_as_hash($data, check => $check_validity);
676             }
677 47         290 elsif ( not ref($data) ) {
678 641   100     1527 $self->set_checked_list([$data], check => $check_validity );
679 641 100       1017 }
680             else {
681             Config::Model::Exception::LoadData->throw(
682             object => $self,
683 47         172 message => "check_list load_data called with unexpected type. ".
684 4         15 "Expected plain scalar, array or hash ref",
685             wrong_data => $data,
686             );
687 46         128 }
688             }
689 46 100 100     615  
690             my ( $self, $a, $b ) = @_;
691              
692             foreach my $param ( $a, $b ) {
693             unless ( $self->is_checked($param) ) {
694 5     5 1 4193 my $err_str = "swap: choice $param must be set";
695 5 100       33 Config::Model::Exception::WrongValue->throw(
  3         16  
696 5 100       32 error => $err_str,
697 5         24 object => $self
698             );
699 5         26 }
700 73 100       113 }
701 37         74  
702             # perform swap in ordered list
703             foreach ( @{ $self->{ordered_data} } ) {
704 36         62 if ( $_ eq $a ) {
705             $_ = $b;
706             }
707             elsif ( $_ eq $b ) {
708             $_ = $a;
709             }
710 12     12 1 27 }
711             }
712 12 50       56  
713 12         29 my ( $self, $c ) = @_;
714 12         63  
715             unless ( $self->is_checked($c) ) {
716 12 100       57 my $err_str = "swap: choice $c must be set";
    100          
    50          
717 7         27 Config::Model::Exception::WrongValue->throw(
718             error => $err_str,
719             object => $self
720 3         16 );
721             }
722              
723 2         6 # perform move in ordered list
724             my $list = $self->{ordered_data};
725             for ( my $i = 1 ; $i < @$list ; $i++ ) {
726 0         0 if ( $list->[$i] eq $c ) {
727             $list->[$i] = $list->[ $i - 1 ];
728             $list->[ $i - 1 ] = $c;
729             last;
730             }
731             }
732             }
733              
734             my ( $self, $c ) = @_;
735              
736 1     1 1 578 unless ( $self->is_checked($c) ) {
737             my $err_str = "swap: choice $c must be set";
738 1         3 Config::Model::Exception::WrongValue->throw(
739 2 50       6 error => $err_str,
740 0         0 object => $self
741 0         0 );
742             }
743              
744             # perform move in ordered list
745             my $list = $self->{ordered_data};
746             for ( my $i = 0 ; $i + 1 < @$list ; $i++ ) {
747             if ( $list->[$i] eq $c ) {
748             $list->[$i] = $list->[ $i + 1 ];
749 1         3 $list->[ $i + 1 ] = $c;
  1         3  
750 5 100       12 last;
    100          
751 1         2 }
752             }
753             }
754 1         3  
755             # dummy to match Value call
756              
757             1;
758              
759             # ABSTRACT: Handle check list element
760 3     3 1 1574  
761              
762 3 50       9 =pod
763 0         0  
764 0         0 =encoding UTF-8
765              
766             =head1 NAME
767              
768             Config::Model::CheckList - Handle check list element
769              
770             =head1 VERSION
771 3         6  
772 3         12 version 2.151
773 12 100       41  
774 2         5 =head1 SYNOPSIS
775 2         4  
776 2         6 use Config::Model;
777              
778             # define configuration tree object
779             my $model = Config::Model->new;
780             $model->create_config_class(
781             name => "MyClass",
782 2     2 1 982  
783             element => [
784 2 50       8  
785 0         0 # type check_list uses Config::Model::CheckList
786 0         0 my_check_list => {
787             type => 'check_list',
788             choice => [ 'A', 'B', 'C', 'D' ],
789             help => {
790             A => 'A effect is this',
791             D => 'D does that',
792             }
793 2         5 },
794 2         7 ],
795 6 100       18 );
796 1         3  
797 1         3 my $inst = $model->instance( root_class_name => 'MyClass' );
798 1         2  
799             my $root = $inst->config_root;
800              
801             # put data
802             $root->load( steps => 'my_check_list=A' );
803              
804 2     2 0 5 my $obj = $root->grab('my_check_list');
805              
806             my $v = $root->grab_value('my_check_list');
807             print "check_list value '$v' with help '", $obj->get_help($v), "'\n";
808              
809             # more data
810             $obj->check('D');
811             $v = $root->grab_value('my_check_list');
812             print "check_list new value is '$v'\n"; # prints check_list new value is 'A,D'
813              
814             =head1 DESCRIPTION
815              
816             This class provides a check list element for a L<Config::Model::Node>.
817             In other words, this class provides a list of booleans items. Each item
818             can be set to 1 or 0.
819              
820             The available items in the check list can be :
821              
822             =over
823              
824             =item *
825              
826             A fixed list (with the C<choice> parameter)
827              
828             =item *
829              
830             A dynamic list where the available choice are the keys of another hash
831             of the configuration tree. See L</"Choice reference"> for details.
832              
833             =back
834              
835             =head1 CONSTRUCTOR
836              
837             CheckList object should not be created directly.
838              
839             =head1 CheckList model declaration
840              
841             A check list element must be declared with the following parameters:
842              
843             =over
844              
845             =item type
846              
847             Always C<checklist>.
848              
849             =item choice
850              
851             A list ref containing the check list items (optional)
852              
853             =item refer_to
854              
855             This parameter is used when the keys of a hash are used to specify the
856             possible choices of the check list. C<refer_to> point to a hash or list
857             element in the configuration tree. See L<Choice reference> for
858             details. (optional)
859              
860             =item computed_refer_to
861              
862             Like C<refer_to>, but use a computed value to find the hash or list
863             element in the configuration tree. See L<Choice reference> for
864             details. (optional)
865              
866             =item default_list
867              
868             List ref to specify the check list items which are "on" by default.
869             (optional)
870              
871             =item ordered
872              
873             Specify whether the order of checked items must be preserved.
874              
875             =item help
876              
877             Hash ref to provide information on the check list items.
878              
879             =item warp
880              
881             Used to provide dynamic modifications of the check list properties
882             See L<Config::Model::Warper> for details
883              
884             =back
885              
886             For example:
887              
888             =over
889              
890             =item *
891              
892             A check list with help:
893              
894             choice_list => {
895             type => 'check_list',
896             choice => ['A' .. 'Z'],
897             help => { A => 'A help', E => 'E help' } ,
898             },
899              
900             =item *
901              
902             A check list with default values:
903              
904             choice_list_with_default => {
905             type => 'check_list',
906             choice => ['A' .. 'Z'],
907             default_list => [ 'A', 'D' ],
908             },
909              
910             =item *
911              
912             A check list whose available choice and default change depending on
913             the value of the C<macro> parameter:
914              
915             warped_choice_list => {
916             type => 'check_list',
917             warp => {
918             follow => '- macro',
919             rules => {
920             AD => {
921             choice => [ 'A' .. 'D' ],
922             default_list => ['A', 'B' ]
923             },
924             AH => { choice => [ 'A' .. 'H' ] },
925             }
926             }
927             },
928              
929             =back
930              
931             =head1 Introspection methods
932              
933             The following methods returns the checklist parameter :
934              
935             =over
936              
937             =item refer_to
938              
939             =item computed_refer_to
940              
941             =back
942              
943             =head1 Choice reference
944              
945             The choice items of a check_list can be given by another configuration
946             element. This other element can be:
947              
948             =over
949              
950             =item *
951              
952             The keys of a hash
953              
954             =item *
955              
956             Another checklist. In this case only the checked items of the other
957             checklist are available.
958              
959             =back
960              
961             This other hash or other checklist is indicated by the C<refer_to> or
962             C<computed_refer_to> parameter. C<refer_to> uses the syntax of the
963             C<steps> parameter of L<grab(...)|Config::Role::Grab/grab">
964              
965             See L<refer_to parameter|Config::Model::IdElementReference/"refer_to parameter">.
966              
967             =head2 Reference examples
968              
969             =over
970              
971             =item *
972              
973             A check list where the available choices are the keys of C<my_hash>
974             configuration parameter:
975              
976             refer_to_list => {
977             type => 'check_list',
978             refer_to => '- my_hash'
979             },
980              
981             =item *
982              
983             A check list where the available choices are the checked items of
984             C<other_check_list> configuration parameter:
985              
986             other_check_list => {
987             type => 'check_list',
988             choice => [qw/A B C/]
989             },
990             refer_to_list => {
991             type => 'check_list',
992             refer_to => '- other_check_list'
993             },
994              
995             =item *
996              
997             A check list where the available choices are the keys of C<my_hash>
998             and C<my_hash2> and C<my_hash3> configuration parameter:
999              
1000             refer_to_3_lists => {
1001             type => 'check_list',
1002             refer_to => '- my_hash + - my_hash2 + - my_hash3'
1003             },
1004              
1005             =item *
1006              
1007             A check list where the available choices are the specified choice and
1008             the choice of C<refer_to_3_lists> and a hash whose name is specified
1009             by the value of the C<indirection> configuration parameter (this
1010             example is admittedly convoluted):
1011              
1012             refer_to_check_list_and_choice => {
1013             type => 'check_list',
1014             computed_refer_to => {
1015             formula => '- refer_to_2_list + - $var',
1016             variables => {
1017             var => '- indirection '
1018             }
1019             },
1020             choice => [qw/A1 A2 A3/],
1021             },
1022              
1023             =back
1024              
1025             =head1 Methods
1026              
1027             =head2 get_type
1028              
1029             Returns C<check_list>.
1030              
1031             =head2 cargo_type
1032              
1033             Returns 'leaf'.
1034              
1035             =head2 check
1036              
1037             Set choice. Parameter is either a list of choices to set or
1038             a list ref and some optional parameter. I.e:
1039              
1040             check (\@list, check => 'skip') ;
1041              
1042             C<check> parameter decide on behavior in case of invalid
1043             choice value: either die (if yes) or discard bad value (if skip)
1044              
1045             =head2 uncheck
1046              
1047             Unset choice. Parameter is either a list of choices to unset or
1048             a list ref and some optional parameter. I.e:
1049              
1050             uncheck (\@list, check => 'skip') ;
1051              
1052             C<check> parameter decide on behavior in case of invalid
1053             choice value: either die (if yes) or discard bad value (if skip)
1054              
1055             =head2 is_checked
1056              
1057             Parameters: C<< ( choice, [ check => yes|skip ] , [ mode => ... ]) >>
1058              
1059             Return 1 if the given C<choice> was set. Returns 0 otherwise.
1060              
1061             C<check> parameter decide on behavior in case of invalid
1062             choice value: either die (if yes) or discard bad value (if skip)
1063              
1064             C<mode> is either: custom standard preset default layered upstream_default
1065              
1066             =head2 has_data
1067              
1068             Return true if the check_list contains a set of checks different from default
1069             or upstream default set of check.
1070              
1071             =head2 get_choice
1072              
1073             Returns an array of all items names that can be checked (i.e.
1074             that can have value 0 or 1).
1075              
1076             =head2 get_help
1077              
1078             Parameters: C<(choice_value)>
1079              
1080             Return the help string on this choice value
1081              
1082             =head2 get_info
1083              
1084             Returns a list of information related to the check list. See
1085             L<Config::Model::Value/get_info> for more details.
1086              
1087             =head2 clear
1088              
1089             Reset the check list (can also be called as C<clear_values>)
1090              
1091             =head2 clear_item
1092              
1093             Parameters: C<(choice_value)>
1094              
1095             Reset an element of the checklist.
1096              
1097             =head2 get_checked_list_as_hash
1098              
1099             Accept a parameter (referred below as C<mode> parameter) similar to
1100             C<mode> in L<Config::Model::Value/fetch>.
1101              
1102             Returns a hash (or a hash ref) of all items. The boolean value is the
1103             value of the hash.
1104              
1105             Example:
1106              
1107             { A => 0, B => 1, C => 0 , D => 1}
1108              
1109             By default, this method returns all items set by the user, or
1110             items set in preset mode or checked by default.
1111              
1112             With a C<mode> parameter set to a value from the list below, this method
1113             returns:
1114              
1115             =over
1116              
1117             =item backend
1118              
1119             The value written in config file, (ie. set by user or by layered data
1120             or preset or default)
1121              
1122             =item custom
1123              
1124             The list entered by the user. An empty list is returned if the list of
1125             checked items is identical to the list of items checked by default. The
1126             whole list of checked items is returned as soon as B<one> item is different
1127             from standard value.
1128              
1129             =item preset
1130              
1131             The list entered in preset mode
1132              
1133             =item standard
1134              
1135             The list set in preset mode or checked by default.
1136              
1137             =item default
1138              
1139             The default list (defined by the configuration model)
1140              
1141             =item layered
1142              
1143             The list specified in layered mode.
1144              
1145             =item upstream_default
1146              
1147             The list implemented by upstream project (defined in the configuration
1148             model)
1149              
1150             =item user
1151              
1152             The set that is active in the application. (ie. set by user or
1153             by layered data or preset or default or upstream_default)
1154              
1155             =item non_upstream_default
1156              
1157             The choice set by user or by layered data or preset or default.
1158              
1159             =back
1160              
1161             =head2 get_checked_list
1162              
1163             Parameters: C<< ( < mode > ) >>
1164              
1165             Returns a list (or a list ref) of all checked items (i.e. all items
1166             set to 1).
1167              
1168             =head2 fetch
1169              
1170             Parameters: C<< ( < mode > ) >>
1171              
1172             Returns a string listing the checked items (i.e. "A,B,C")
1173              
1174             =head2 get
1175              
1176             Parameters: C<< ( path [, < mode> ] ) >>
1177              
1178             Get a value from a directory like path.
1179              
1180             =head1 Method to check or clear items in the check list
1181              
1182             All these methods accept an optional C<check> parameter that can be:
1183              
1184             =over
1185              
1186             =item yes
1187              
1188             A wrong item to check trigger an exception (default)
1189              
1190             =item skip
1191              
1192             A wrong item trigger a warning
1193              
1194             =item no
1195              
1196             A wrong item is ignored
1197              
1198             =back
1199              
1200             =head2 set
1201              
1202             Parameters: C<< ( path, items_to_set, [ check => [ yes | no | skip ] ] ) >>
1203              
1204             Set a checklist with a directory like path. Since a checklist is a leaf, the path
1205             should be empty.
1206              
1207             The values are a comma separated list of items to set in the check list.
1208              
1209             Example :
1210              
1211             $leaf->set('','A,C,Z');
1212             $leaf->set('','A,C,Z', check => 'skip');
1213              
1214             =head2 set_checked_list
1215              
1216             Set all passed items to checked (1). All other available items
1217             in the check list are set to 0.
1218              
1219             Example, for a check list that contains A B C and D check items:
1220              
1221             # set cl to A=0 B=1 C=0 D=1
1222             $cl->set_checked_list('B','D')
1223             $cl->set_checked_list( [ 'B','D' ])
1224             $cl->set_checked_list( [ 'B','D' ], check => 'yes')
1225              
1226             =head2 store_set
1227              
1228             Alias to L</set_checked_list>, so a list and a check_list can use the same store method
1229              
1230             =head2 store
1231              
1232             Set all items listed in a string to checked. The items must be
1233             separated by commas. All other available items in the check list are
1234             set to 0.
1235              
1236             Example:
1237              
1238             $cl->store('B, D')
1239             $cl->store( value => 'B,C' )
1240             $cl->store( value => 'B,C', check => 'yes' )
1241              
1242             =head2 load
1243              
1244             Alias to L</store>.
1245              
1246             =head2 set_checked_list_as_hash
1247              
1248             Set check_list items. Missing items in the given hash of parameters
1249             are cleared (i.e. set to undef).
1250              
1251             Example for a check list containing A B C D
1252              
1253             $cl->set_checked_list_as_hash( { A => 1, B => 0} , check => 'yes' )
1254             # result A => 1 B => 0 , C and D are undef
1255              
1256             =head2 load_data
1257              
1258             Load items as an array or hash ref. Array is forwarded to
1259             L<set_checked_list> , and hash is forwarded to L<set_checked_list_as_hash>.
1260              
1261             Example:
1262              
1263             $cl->load_data(['A','B']) # cannot use check param here
1264             $cl->load_data( data => ['A','B'])
1265             $cl->load_data( data => ['A','B'], check => 'yes')
1266             $cl->load_data( { A => 1, B => 1 } )
1267             $cl->load_data( data => { A => 1, B => 1 }, check => 'yes')
1268              
1269             =head2 is_bad_mode
1270              
1271             Accept a mode parameter. This function checks if the mode is accepted
1272             by L</fetch> method. Returns an error message if not. For instance:
1273              
1274             if (my $err = $val->is_bad_mode('foo')) {
1275             croak "my_function: $err";
1276             }
1277              
1278             This method is intented as a helper ti avoid duplicating the list of
1279             accepted modes for functions that want to wrap fetch methods (like
1280             L<Config::Model::Dumper> or L<Config::Model::DumpAsData>)
1281              
1282             =head1 Ordered checklist methods
1283              
1284             All the methods below are valid only for ordered checklists.
1285              
1286             =head2 swap
1287              
1288             Parameters: C<< ( choice_a, choice_b) >>
1289              
1290             Swap the 2 given choice in the list. Both choice must be already set.
1291              
1292             =head2 move_up
1293              
1294             Parameters: C<< ( choice ) >>
1295              
1296             Move the choice up in the checklist.
1297              
1298             =head2 move_down
1299              
1300             Parameters: C<< ( choice ) >>
1301              
1302             Move the choice down in the checklist.
1303              
1304             =head1 AUTHOR
1305              
1306             Dominique Dumont, (ddumont at cpan dot org)
1307              
1308             =head1 SEE ALSO
1309              
1310             L<Config::Model>,
1311             L<Config::Model::Instance>,
1312             L<Config::Model::Node>,
1313             L<Config::Model::AnyId>,
1314             L<Config::Model::ListId>,
1315             L<Config::Model::HashId>,
1316             L<Config::Model::Value>
1317              
1318             =head1 AUTHOR
1319              
1320             Dominique Dumont
1321              
1322             =head1 COPYRIGHT AND LICENSE
1323              
1324             This software is Copyright (c) 2005-2022 by Dominique Dumont.
1325              
1326             This is free software, licensed under:
1327              
1328             The GNU Lesser General Public License, Version 2.1, February 1999
1329              
1330             =cut