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   126 use 5.010;
  19         42  
  19         146  
13 19     19   8629  
  19         65  
14             use Config::Model::Exception;
15 19     19   105 use Config::Model::IdElementReference;
  19         37  
  19         413  
16 19     19   89 use Config::Model::Warper;
  19         44  
  19         460  
17 19     19   89 use List::MoreUtils qw/any none/;
  19         34  
  19         523  
18 19     19   102 use Carp;
  19         34  
  19         291  
19 19     19   13316 use Log::Log4perl qw(get_logger :levels);
  19         40  
  19         1327  
20 19     19   125 use Storable qw/dclone/;
  19         40  
  19         169  
21 19     19   2381  
  19         42  
  19         34113  
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 108 if ( defined $self->refer_to or defined $self->computed_refer_to ) {
56             $self->submit_to_refer_to();
57 57 100 100     379 }
58 29         114  
59             $self->set_properties(); # set will use backup data
60              
61 57         276 if ( defined $self->warp ) {
62             my $warp_info = $self->warp;
63 57 100       671 $self->{warper} = Config::Model::Warper->new(
64 3         10 warped_object => $self,
65 3         83 %$warp_info,
66             allowed => \@allowed_warp_params
67             );
68             }
69              
70             $self->cl_init;
71              
72 57         213 $logger->info( "Created check_list element " . $self->element_name );
73             return $self;
74 57         466 }
75 57         955  
76             my $self = shift;
77              
78             $self->warp if ( $self->{warp} );
79 57     57 0 113  
80             if ( defined $self->{ref_object} ) {
81 57 100       168 my $level = $self->parent->get_element_property(
82             element => $self->{element_name},
83 57 100       177 property => 'level',
84             );
85             $self->{ref_object}->get_choice_from_referred_to if $level ne 'hidden';
86 29         288 }
87             }
88 29 100       195  
89             my $self = shift;
90             my $name = $self->{parent}->name . ' ' . $self->{element_name};
91             return $name;
92             }
93 524     524 1 768  
94 524         1400  
95 524         2071 # 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 977 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 118  
105             if ( $logger->is_trace() ) {
106             my %h = @_;
107 62         155 my $keys = join( ',', keys %h );
108 310         543 $logger->trace("set_properties called on $self->{element_name} with $keys");
109             }
110              
111 62 50       231 # 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         461  
  62         238  
119             $self->{ordered} = delete $args{ordered} || 0;
120              
121 62         134 if ( defined $args{choice} ) {
122 62         156 my @choice = @{ delete $args{choice} };
123             $self->{default_choice} = \@choice;
124             $self->setup_choice(@choice);
125 62   100     297 }
126              
127 62 100       184 if ( defined $args{default} ) {
128 30         46 $logger->warn($self->name, ": default param is deprecated, use default_list");
  30         141  
129 30         64 $args{default_list} = delete $args{default};
130 30         103 }
131              
132             if ( defined $args{default_list} ) {
133 62 50       187 $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       156  
139 3         5 if ( defined $args{upstream_default_list} ) {
140             $self->{upstream_default_list} = delete $args{upstream_default_list};
141             }
142              
143 62         109 # store upstream default data in a hash (more convenient)
  6         17  
  62         272  
144             $self->{upstream_default_data} =
145 62 100       194 { map { $_ => 1 } @{ $self->{upstream_default_list} } };
146 3         16  
147             Config::Model::Exception::Model->throw(
148             object => $self,
149             error => "Unexpected parameters :" . join( ' ', keys %args ) ) if scalar keys %args;
150              
151 62         116 if ( $self->has_warped_slaves ) {
  16         26  
  62         170  
152             my $hash = $self->get_checked_list_as_hash; # force scalar context
153 62 50       188 $self->trigger_warp($hash, $self->fetch);
154             }
155             }
156              
157 62 50       246 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 226 delete $self->{choice_hash} if defined $self->{choice_hash};
165 134 50       482 for (@choice) {
  0         0  
166             $self->{choice_hash}{$_} = 1;
167 134         703 }
168              
169             $self->{choice} = \@choice;
170              
171 134 100       1211 # cleanup current preset and data if it does not fit current choices
172 134         288 foreach my $field (qw/preset data layered/) {
173 832         1472 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         346 }
177             }
178             }
179 134         280  
180 402 50       789 # Need to extract Config::Model::Reference (used by Value, and maybe AnyId).
181 402         822  
  402         1118  
182 109 50       251 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 57 elsif ( defined $self->computed_refer_to ) {
191             $self->{ref_object} = Config::Model::IdElementReference->new(
192 29 100       142 computed_refer_to => $self->computed_refer_to,
    50          
193 28         437 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         12 foreach my $path ( values %$var ) {
200              
201             # is ref during test case
202             #print "path is '$path'\n";
203 1         4 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         4 }
208             }
209             else {
210             croak "checklist submit_to_refer_to: undefined refer_to or computed_refer_to";
211 1 50       7 }
212 1         6 }
213 1 50       13  
214 1         18 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 186 my $self = shift;
224 104         378 return 'leaf';
225             }
226              
227              
228 138     138 1 234 # no operation. THere's no check_value method because a check list
229 138         310 # supposed to be always correct. Hence apply_fixes is empty.
230             }
231              
232 121     121 0 324 my $self = shift;
233             my %args = @_;
234              
235 121     121 1 195 return if $self->instance->initial_load and not $args{really};
236 121         269  
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 117  
247 59         158  
248             # does not check the validity, but check the item of the check_list
249 59 100 66     258 my $self = shift;
250             my @list = ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_;
251 55         186 my %args = ref $_[0] eq 'ARRAY' ? @_[ 1, $#_ ] : ( check => 'yes' );
252             my $check = $self->_check_check( $args{check} );
253              
254 55         256 if ( defined $self->{ref_object} ) {
255 8 50       69 $self->{ref_object}->get_choice_from_referred_to;
256             }
257 8         43  
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 1849 unless $self->instance->initial_load;
265 11 50       47 }
  0         0  
266 11 50       36  
267 11         37 my $self = shift;
268             my $choice = shift;
269 11 100       41  
270 3         10 my $inst = $self->instance;
271             my $data_name =
272             $inst->preset ? 'preset'
273 11         19 : $inst->layered ? 'layered'
274 11         24 : 'data';
275 14 50       38 my $old_v = $self->{$data_name}{$choice};
276             my $changed = 0;
277             if ($old_v) {
278 9 50       77 $changed = 1;
279             }
280             delete $self->{$data_name}{$choice};
281              
282             if ( $self->{ordered} and $changed ) {
283 244     244 1 252 my $ord = $self->{ordered_data};
284 244         268 my @new = grep { $_ ne $choice } @$ord;
285             $self->{ordered_data} = \@new;
286 244         338 }
287 244 100       481 return $changed;
    50          
288             }
289              
290             # internal
291 244         310 my ( $self, $choice, $value, $check ) = @_;
292 244         251  
293 244 100       324 my $inst = $self->instance;
294 34         36  
295             if ( $value != 0 and $value != 1 ) {
296 244         309 Config::Model::Exception::WrongValue->throw(
297             error => "store: check item value must be boolean, " . "not '$value'.",
298 244 50 33     368 object => $self
299 0         0 );
300 0         0 return;
  0         0  
301 0         0 }
302              
303 244         321 my $ok = $self->{choice_hash}{$choice} || 0;
304             my $changed = 0;
305              
306             if ($ok) {
307             my $data_name =
308 706     706   13838 $inst->preset ? 'preset'
309             : $inst->layered ? 'layered'
310 706         1167 : 'data';
311             my $old_v = $self->{$data_name}{$choice} ;
312 706 50 66     1342 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     1195 }
321 706         763  
322             if ( $self->{ordered} and $value ) {
323 706 100       938 my $ord = $self->{ordered_data};
324 697 100       1593 push @$ord, $choice unless scalar grep { $choice eq $_ } @$ord;
    100          
325             }
326             }
327             else {
328 697         945 my $err_str =
329 697 100 100     1340 "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   186 if ($check eq 'yes') {
  19         45  
  19         54645  
  489         504  
334 489   75     1117 Config::Model::Exception::WrongValue->throw( error => $err_str, object => $self );
335             }
336 489         762 elsif ($check eq 'skip') {
337             $user_logger->warn($err_str);
338             }
339 697 100 100     1276 }
340 17         28  
341 17 100       28 if ( $ok
  41         74  
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         23 $self->trigger_warp($h , $str);
  9         35  
348             }
349 9 100       30  
350 9 100       26 return $changed;
    50          
351 4         58 }
352              
353             my $self = shift;
354 5         17 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     2062 return \@list, $check, \%args;
      100        
      33        
      66        
359             }
360              
361             my $self = shift;
362 4         49 my ($list, $check) = $self->get_arguments(@_);
363 4         8  
364 4         13 if ( defined $self->{ref_object} ) {
365             $self->{ref_object}->get_choice_from_referred_to;
366             }
367 702         2512  
368             my @changed;
369             for ( @$list ) {
370             push @changed, $_ if $self->_store( $_, 0, $check )
371 54     54 0 4286 }
372 54         84  
373 54 100       235 $self->notify_change( note => "uncheck @changed" )
374 54 100       242 unless $self->instance->initial_load;
375 54         166 }
376 54         186  
377             my $self = shift;
378             my @set = $self->get_checked_list(qw/mode custom/) ;
379             return scalar @set;
380 4     4 1 20 }
381 4         13  
382             {
383 4 50       16 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         6 if ( $mode and not defined $accept_mode{$mode} ) {
388 4         11 my $good_ones = join( ' or ', sort keys %accept_mode );
389 4 100       10 return "expected $good_ones as mode parameter, not $mode";
390             }
391             }
392 4 50       48 }
393              
394             my $self = shift;
395             my $choice = shift;
396             my %args = @_;
397 3     3 1 1673 my $mode = $args{mode} || '';
398 3         10 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 306  
408 170 50 66     691 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 20  
417 12         20 my $result =
418 12         19 $mode eq 'custom' ? ( $dat && !$std_v ? 1 : 0 )
419 12   50     42 : $mode eq 'preset' ? $pre
420 12         41 : $mode eq 'layered' ? $lay
421             : $mode eq 'upstream_default' ? $ud
422 12   50     34 : $mode eq 'default' ? $def
423             : $mode eq 'standard' ? $std_v
424 12 50       23 : $mode eq 'non_upstream_default' ? $ud
    0          
425             : $mode eq 'user' ? $user_v
426 12 50       30 : $mode eq 'backend' ? $dat // $std_v
427 0         0 : $dat // $std_v;
428              
429             return $result;
430 12         23 }
431 12         17 elsif ( $check eq 'yes' ) {
432 12         21 my $err_str =
433 12         19 "Unknown check_list item '$choice'. Expected '"
434 12         20 . join( "', '", @{ $self->{choice} } ) . "'";
435 12   33     41 $err_str .= "\n\t" . $self->{ref_object}->reference_info
      50        
436 12   33     26 if defined $self->{ref_object};
      33        
      0        
      0        
437 12   33     23 Config::Model::Exception::WrongValue->throw(
      33        
      0        
      0        
      0        
438             error => $err_str,
439 12 0 0     62 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         46 }
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 2089 goto &get_upstream_default_choice;
471             }
472 234 100       501  
473 74         307 my $self = shift;
474             return @{ $self->{upstream_default_data} || [] };
475             }
476 234 100       537  
477 1         15 my $self = shift;
478 1         8 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         334 return;
  233         1115  
486             }
487              
488             my $self = shift;
489 104     104 0 175  
490 104 100       163 my @items = ('type: check_list');
  104         530  
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 549  
505 3         7  
506             my $self = shift;
507 3 50       8 $self->{layered} = {};
508             }
509 3         6  
510 3 100 66     21 my %old_mode = ( built_in_list => 'upstream_default_list', );
511              
512 2         4 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       8 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 10897 my $pre = $self->{preset};
528             my $def = $self->{default_data};
529 8         19 my $lay = $self->{layered};
530 208         266 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 5  
538 3         18 # 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 1480 $self->{_std_backup} = { %$def, %$pre };
545 158 100       506 $self->notify_change( note => "use default checklist" );
546 158   100     502 }
547              
548 158         394 # custom test must compare the whole list at once, not just one item at a time.
549 158 50       386 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       370 : $mode eq 'default' ? (%$def)
555 0         0 : $mode eq 'standard' ? %std
556             : $mode eq 'user' ? ( %h, %std, %$dat )
557             : ( %predef, %$dat );
558 158         352  
559 158         242 return wantarray ? %result : \%result;
560 158         283 }
561 158         224  
562 158         226 my $self = shift;
563              
564             my %h = $self->get_checked_list_as_hash(@_);
565 158         337 my @good_order = $self->{ordered} ? @{ $self->{ordered_data} } : sort keys %h;
  2406         3823  
566             my @res = grep { $h{$_} } @good_order;
567 158         563 return wantarray ? @res : \@res;
568 158         387 }
569              
570             my $self = shift;
571 158 100   319   944 return join( ',', $self->get_checked_list(@_) );
  319 100       782  
  49         226  
572             }
573 158 100 100 10   974  
  10   100     57  
  13         21  
574             my $self = shift;
575             return join( ',', $self->get_checked_list('custom') );
576 4         22 }
577 4         11  
578             my $self = shift;
579             return join( ',', $self->get_checked_list('preset') );
580             }
581              
582 158 100 100     1208 my $self = shift;
  384 100       961  
    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       1213 message => "get() called with a value with non-empty path: '$path'"
592             );
593             }
594             return $self->fetch(@_);
595 150     150 1 12250 }
596              
597 150         356 my ($self, $path, $list, %args) = @_;
598 150 100       888  
  15         36  
599 150         309 my $check_validity = $self->_check_check( $args{check} );
  1127         1438  
600 150 100       817 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 1425 );
605 68         203 }
606              
607             my @list = split /,/, $list;
608             return $self->set_checked_list( \@list, check => $check_validity );
609 1     1 0 3 }
610 1         5  
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 973 push @changed, "$c:$v" if $self->_store( $c, $v, $check );
637             }
638 3         13  
639 3 50       10 # 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         9 $self->notify_change( note => "set_checked_list @changed" )
647 3         7 if @changed and not $self->instance->initial_load;
648             }
649              
650             my $self = shift;
651 17     17 1 95 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 6741 foreach my $c ( $self->get_choice ) {
656 22 100       148 if ( defined $check_list{$c} ) {
    100          
657             $self->_store( $c, $check_list{$c}, $check_validity );
658             }
659             else {
660 22         111 $self->clear_item($c);
661             }
662 22         197 }
663 22         64 }
  59         158  
  59         108  
664 22         104  
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 11039  
671 47         147 if ( ref($data) eq 'ARRAY' ) {
672             $self->set_checked_list($data, check => $check_validity);
673 47         287 }
674 47         365 elsif ( ref($data) eq 'HASH' ) {
  139         317  
675 47         88 $self->set_checked_list_as_hash($data, check => $check_validity);
676             }
677 47         245 elsif ( not ref($data) ) {
678 641   100     1537 $self->set_checked_list([$data], check => $check_validity );
679 641 100       989 }
680             else {
681             Config::Model::Exception::LoadData->throw(
682             object => $self,
683 47         173 message => "check_list load_data called with unexpected type. ".
684 4         8 "Expected plain scalar, array or hash ref",
685             wrong_data => $data,
686             );
687 46         104 }
688             }
689 46 100 100     486  
690             my ( $self, $a, $b ) = @_;
691              
692             foreach my $param ( $a, $b ) {
693             unless ( $self->is_checked($param) ) {
694 5     5 1 4084 my $err_str = "swap: choice $param must be set";
695 5 100       23 Config::Model::Exception::WrongValue->throw(
  3         14  
696 5 100       23 error => $err_str,
697 5         17 object => $self
698             );
699 5         18 }
700 73 100       105 }
701 37         65  
702             # perform swap in ordered list
703             foreach ( @{ $self->{ordered_data} } ) {
704 36         51 if ( $_ eq $a ) {
705             $_ = $b;
706             }
707             elsif ( $_ eq $b ) {
708             $_ = $a;
709             }
710 12     12 1 23 }
711             }
712 12 50       43  
713 12         24 my ( $self, $c ) = @_;
714 12         51  
715             unless ( $self->is_checked($c) ) {
716 12 100       54 my $err_str = "swap: choice $c must be set";
    100          
    50          
717 7         23 Config::Model::Exception::WrongValue->throw(
718             error => $err_str,
719             object => $self
720 3         9 );
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 588 unless ( $self->is_checked($c) ) {
737             my $err_str = "swap: choice $c must be set";
738 1         4 Config::Model::Exception::WrongValue->throw(
739 2 50       4 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         2 $list->[ $i + 1 ] = $c;
  1         4  
750 5 100       13 last;
    100          
751 1         3 }
752             }
753             }
754 1         4  
755             # dummy to match Value call
756              
757             1;
758              
759             # ABSTRACT: Handle check list element
760 3     3 1 1778  
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         5  
772 3         12 version 2.152
773 12 100       29  
774 2         7 =head1 SYNOPSIS
775 2         4  
776 2         4 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 1225  
783             element => [
784 2 50       6  
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         9 },
794 2         8 ],
795 6 100       16 );
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 8 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