File Coverage

blib/lib/Config/Model/AnyId.pm
Criterion Covered Total %
statement 406 470 86.3
branch 131 180 72.7
condition 61 92 66.3
subroutine 52 62 83.8
pod 27 48 56.2
total 677 852 79.4


line stmt bran cond sub pod time code
1             #
2             # This file is part of Config-Model
3             #
4             # This software is Copyright (c) 2005-2022 by Dominique Dumont.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10              
11             use 5.020;
12 38     38   24658  
  38         123  
13             use Mouse;
14 38     38   191 with "Config::Model::Role::NodeLoader";
  38         63  
  38         266  
15             with "Config::Model::Role::Utils";
16              
17             use Config::Model::Exception;
18 38     38   14924 use Config::Model::Warper;
  38         113  
  38         909  
19 38     38   204 use Carp qw/cluck croak carp/;
  38         76  
  38         1366  
20 38     38   226 use Log::Log4perl qw(get_logger :levels);
  38         67  
  38         2287  
21 38     38   249 use Storable qw/dclone/;
  38         86  
  38         267  
22 38     38   4852 use Mouse::Util::TypeConstraints;
  38         87  
  38         1696  
23 38     38   237 use Scalar::Util qw/weaken/;
  38         88  
  38         281  
24 38     38   3837  
  38         96  
  38         2127  
25             extends qw/Config::Model::AnyThing/;
26              
27             use feature qw/signatures postderef/;
28 38     38   231 no warnings qw/experimental::signatures experimental::postderef/;
  38         94  
  38         3918  
29 38     38   242  
  38         71  
  38         253352  
30             subtype 'KeyArray' => as 'ArrayRef' ;
31             coerce 'KeyArray' => from 'Str' => via { [$_] } ;
32              
33             my $logger = get_logger("Tree::Element::Id");
34             my $user_logger = get_logger("User");
35             my $deep_check_logger = get_logger('DeepCheck');
36             my $fix_logger = get_logger("Anything::Fix");
37             my $change_logger = get_logger("ChangeTracker");
38              
39             enum 'DataMode' => [qw/preset layered normal/];
40              
41             has data_mode => (
42             is => 'rw',
43             isa => 'HashRef[DataMode]',
44             traits => ['Hash'],
45             handles => {
46             get_data_mode => 'get',
47             set_data_mode => 'set',
48             delete_data_mode => 'delete',
49             clear_data_mode => 'clear',
50             },
51             default => sub { {}; }
52             );
53              
54             # this is cleared and set by set_properties
55             has _warpable_check_content_actions => (
56             is => 'bare', # no direct accessor
57             isa => 'ArrayRef[CodeRef]',
58             traits => ['Array'],
59             handles => {
60             add_warpable_check_content => 'push',
61             clear_warpable_check_content => 'clear',
62             get_all_warpable_content_checks => 'elements',
63             },
64             default => sub { []; }
65             );
66              
67             has _check_content_actions => (
68             is => 'bare', # no direct accessor
69             isa => 'ArrayRef[CodeRef]',
70             traits => ['Array'],
71             handles => {
72             add_check_content => 'push',
73             get_all_content_checks => 'elements',
74             },
75             default => sub { []; }
76             );
77              
78             # needs_content_check defaults to 1 to trap bad data right after loading
79             has needs_content_check => ( is => 'rw', isa => 'Bool', default => 1 );
80              
81             has has_fixes => (
82             is => 'ro',
83             isa => 'Num',
84             default => 0,
85             traits => ['Number'],
86             handles => {
87             inc_fixes => [ add => 1 ],
88             dec_fixes => [ sub => 1 ],
89             add_fixes => 'add',
90             flush_fixes => [ mul => 0 ],
91             }
92             );
93              
94             # Some idea for improvement
95              
96             # suggest => 'foo' or '$bar foo'
97             # creates a method analog to next_id (or next_id but I need to change
98             # run_user_command) that suggest the next id as foo_<nb> where
99             # nb is incremented each time, or compute the passed formula
100             # and performs the same
101              
102             my @common_int_params = qw/min_index max_index max_nb auto_create_ids/;
103             has \@common_int_params => ( is => 'ro', isa => 'Maybe[Int]' );
104              
105             my @common_hash_params = qw/default_with_init/;
106             has \@common_hash_params => ( is => 'ro', isa => 'Maybe[HashRef]' );
107              
108             my @common_list_params = qw/allow_keys default_keys auto_create_keys/;
109             has \@common_list_params => (
110             is => 'ro',
111             isa => 'KeyArray',
112             coerce => 1,
113             default => sub { []; }
114             );
115              
116             my @common_str_params = qw/allow_keys_from allow_keys_matching follow_keys_from
117             migrate_keys_from migrate_values_from
118             duplicates warn_if_key_match warn_unless_key_match/;
119             has \@common_str_params => ( is => 'ro', isa => 'Maybe[Str]' );
120              
121             my @common_params =
122             ( @common_int_params, @common_str_params, @common_list_params, @common_hash_params );
123             my @allowed_warp_params = ( @common_params, qw/level convert/ );
124              
125             around BUILDARGS => sub {
126             my $orig = shift;
127             my $class = shift;
128             my %args = @_;
129             my %h = map { ( $_ => $args{$_} ); } grep { defined $args{$_} } @allowed_warp_params;
130             return $class->$orig( backup => dclone( \%h ), @_ );
131             };
132              
133             has [qw/backup cargo/] => ( is => 'ro', isa => 'HashRef', required => 1 );
134             has warp => ( is => 'ro', isa => 'Maybe[HashRef]' );
135             has [qw/morph/] => ( is => 'ro', isa => 'Bool', default => 0 );
136             has content_warning_list => ( is => 'rw', isa => 'ArrayRef', default => sub { []; } );
137             has [qw/cargo_class max_index index_class index_type/] =>
138             ( is => 'rw', isa => 'Maybe[Str]' );
139              
140             has config_model => (
141             is => 'ro',
142             isa => 'Config::Model',
143             weak_ref => 1,
144             lazy => 1,
145             builder => '_config_model'
146             );
147              
148             my $self = shift;
149             return $self->instance->config_model;
150 109     109   220 }
151 109         1227  
152             my $self = shift;
153             return $self->cargo->{config_class_name};
154             }
155 293     293 1 512  
156 293         1574 my $self = shift;
157              
158             croak "Missing cargo->type parameter for element " . $self->{element_name} || 'unknown'
159             unless defined $self->cargo->{type};
160 511     511 1 2936  
161             if ( $self->cargo->{type} eq 'node' and not $self->cargo->{config_class_name} ) {
162             croak "Missing cargo->config_class_name parameter for element "
163 511 50 0     2144 . $self->element_name || 'unknown';
164             }
165 511 50 66     2742  
166 0   0     0 if ( $self->{cargo}{type} eq 'hash' or $self->{cargo}{type} eq 'list' ) {
167             die "$self->{element_name}: using $self->{cargo}{type} will probably not work";
168             }
169              
170 511 50 33     2823 $self->set_properties();
171 0         0  
172             if ( defined $self->warp ) {
173             $self->{warper} = Config::Model::Warper->new(
174 511         1852 warped_object => $self,
175             %{ $self->warp },
176 508 100       1788 allowed => \@allowed_warp_params
177             );
178             }
179 6         11  
  6         117  
180             return $self;
181             }
182              
183             # this method can be called by the warp mechanism to alter (warp) the
184 508         1350 # feature of the Id object.
185             # mega cleanup
186             for ( @allowed_warp_params ) { delete $self->{$_}; }
187              
188             my %args = ( %{ $self->{backup} }, @args );
189 544     544 0 844  
  544         848  
  544         804  
  544         703  
190             # these are handled by Node or Warper
191 544         1419 for ( qw/level/ ) { delete $args{$_}; }
  9792         11789  
192              
193 544         912 $logger->trace( $self->name, " set_properties called with @args" );
  544         1692  
194              
195             for ( @common_params ) {
196 544         1159 $self->{$_} = delete $args{$_} if defined $args{$_};
  544         1079  
197             }
198 544         1725  
199             $self->set_convert( \%args ) if defined $args{convert};
200 544         4845  
201 8704 100       12867 $self-> clear_warpable_check_content;
202             for ( $self-> get_all_content_checks ) {
203             $self-> add_warpable_check_content($_);
204 544 100       1366 }
205             for ( qw/duplicates/ ) {
206 544         2035 my $method = "check_$_";
207 544         6017 my $weak_self = $self;
208 0         0 weaken($weak_self); # weaken reference loop ($self - check_content - closure - self)
209             $self-> add_check_content( sub { $weak_self->$method(@_);} ) if $self->{$_};
210 544         4548 }
211 544         1095  
212 544         900 Config::Model::Exception::Model->throw(
213 544         2009 object => $self,
214 544 100   17   1685 error => "Undefined index_type"
  17         98  
215             ) unless defined $self->{index_type};
216              
217             Config::Model::Exception::Model->throw(
218             object => $self,
219             error => "Unexpected index_type $self->{index_type}"
220 544 50       1566 )
221             unless ( $self->{index_type} eq 'integer'
222             or $self->{index_type} eq 'string' );
223              
224             my @current_idx = $self->_fetch_all_indexes();
225             if (@current_idx) {
226             my $first_idx = shift @current_idx;
227 544 50 66     1940 my $last_idx = pop @current_idx;
228              
229 544         1670 foreach my $idx ( ( $first_idx, $last_idx ) ) {
230 544 100       1310 my $ok = $self->check_idx($first_idx);
231 15         24 next if $ok;
232 15         53  
233             # here a user input may trigger an exception even if fetch
234 15         29 # or set value check is disabled. That's mostly because,
235 29         69 # we cannot enforce more strict settings without random
236 29 100       75 # deletion of data. For instance, if a hash contains 5
237             # items and the max_nb of items is reduced to 3. Which 2
238             # items should we remove ?
239              
240             # Since we cannot choose, we must raise an exception in
241             # all cases.
242             Config::Model::Exception::WrongValue->throw(
243             error => "Error while setting id property:"
244             . join( "\n\t", @{ $self->{idx_error_list} } ),
245             object => $self
246             );
247             }
248             }
249 1         3  
  1         8  
250             $self->auto_create_elements;
251              
252             if ( defined $self->{duplicates}
253             and defined $self->{cargo}
254             and $self->{cargo}{type} ne 'leaf' ) {
255 543         1896 Config::Model::Exception::Model->throw(
256             object => $self,
257 542 100 66     1746 error => "Cannot specify 'duplicates' with cargo type '$self->{cargo}{type}'",
      100        
258             );
259             }
260 1         5  
261             my $ok_dup = 'forbid|suppress|warn|allow';
262             if ( defined $self->{duplicates} and $self->{duplicates} !~ /^$ok_dup$/ ) {
263             Config::Model::Exception::Model->throw(
264             object => $self,
265             error => "Unexpected 'duplicates' $self->{duplicates} expected $ok_dup",
266 541         921 );
267 541 100 100     1682 }
268 1         7  
269             Config::Model::Exception::Model->throw(
270             object => $self,
271             error => "Unexpected parameters: " . join( ' ', keys %args )
272             ) if scalar keys %args;
273              
274             return;
275 540 50       1310 }
276              
277             my $self = shift;
278             my $idx = shift;
279 540         1063  
280             return unless defined $self->{default_with_init};
281              
282             my $h = $self->{default_with_init};
283 865     865 0 1222 foreach my $def_key ( keys %$h ) {
284 865         1158 $self->create_default_content($def_key);
285             }
286 865 100       2086 return;
287             }
288 4         10  
289 4         11 my $self = shift;
290 8         28 my $idx = shift // die "missing index";
291              
292 4         18 return unless defined $self->{default_with_init};
293              
294             my $def = $self->{default_with_init}{$idx};
295             return unless defined $def; # no default content to create for $idx
296 3671     3671 0 4839  
297 3671   50     6695 return if $self->_defined($idx) ; # object already created
298              
299 3671 100       7987 $self->auto_vivify($idx);
300              
301 37         93 my $v_obj = $self->_fetch_with_id($idx);
302 37 100       75 if ( $v_obj->get_type eq 'leaf' ) {
303             $v_obj->store( $def );
304 35 100       85 }
305             else {
306 8         35 $v_obj->load( $def );
307             }
308 8         27 return;
309 8 100       28 }
310 4         17  
311             my $self = shift;
312             carp $self->name, ": max param is deprecated, use max_index\n";
313 4         16 return $self->max_index;
314             }
315 8         25  
316             my $self = shift;
317             carp $self->name, ": min param is deprecated, use min_index\n";
318             return $self->min_index;
319 0     0 0 0 }
320 0         0  
321 0         0  
322             my $self = shift;
323              
324             #my @ids = $self->fetch_all_indexes ;
325 0     0 0 0 # the returned cargo type might be different from collected type
326 0         0 # when collected type is 'warped_node'.
327 0         0 #return @ids ? $self->fetch_with_id($ids[0])->get_cargo_type
328             # : $self->{cargo_type} ;
329             return $self->{cargo}{type};
330 1614     1614 0 4836 }
331              
332             my $self = shift;
333 2812     2812 1 4731 my $what = shift;
334             return $self->{cargo}{$what};
335             }
336              
337             # internal, does a grab with improved error message
338             my $param = $args{param} || croak "safe_typed_grab: missing param";
339              
340 2812         7727 my $res = eval {
341             $self->grab(
342             step => $self->{$param},
343             type => $self->get_type,
344 2     2 1 6 check => $args{check} || 'yes',
345 2         5 );
346 2         12 };
347              
348             if ($@) {
349             my $e = $@;
350 11     11 0 21 my $msg = $e ? $e->full_message : '';
  11         24  
  11         26  
  11         17  
351 11   33     45 Config::Model::Exception::Model->throw(
352             object => $self,
353 11         18 error => "'$param' parameter: " . $msg
354             );
355             }
356              
357 11   100     68 return $res;
358             }
359              
360             my $self = shift;
361 11 100       111  
362 1         3 if ( $self->{follow_keys_from} ) {
363 1 50       3 my $followed = $self->safe_typed_grab( param => 'follow_keys_from' );
364 1         20 my @res = $followed->fetch_all_indexes;
365             return wantarray ? @res : \@res;
366             }
367              
368             my @res;
369              
370 10         28 push @res, @{ $self->{default_keys} }
371             if defined $self->{default_keys};
372              
373             push @res, keys %{ $self->{default_with_init} }
374 867     867 1 2457 if defined $self->{default_with_init};
375              
376 867 100       1902 return wantarray ? @res : \@res;
377 3         24 }
378 2         10  
379 2 100       18 my $self = shift;
380             return $self->{parent}->name . ' ' . $self->{element_name} . ' id';
381             }
382 864         1335  
383             # internal. Handle model declaration arguments
384 3         10 my $warp_info = delete $args{warp};
385 864 100       2007  
386             for (qw/index_class index_type morph ordered/) {
387 4         18 $self->{$_} = delete $args{$_} if defined $args{$_};
388 864 100       1717 }
389              
390 864 50       2256 $self->{backup} = dclone( \%args );
391              
392             $self->set_properties(%args) if defined $self->{index_type};
393              
394 1650     1650 1 3227 if ( defined $warp_info ) {
395 1650         5096 $self->{warper} = Config::Model::Warper->new(
396             warped_object => $self,
397             %$warp_info,
398             allowed => \@allowed_warp_params
399 0     0 0 0 );
  0         0  
  0         0  
  0         0  
400 0         0 }
401              
402 0         0 return $self;
403 0 0       0 }
404              
405             my $self = shift;
406 0         0 $fix_logger->trace( $self->location . ": apply_fixes called" );
407              
408 0 0       0 $self->deep_check( fix => 1, logger => $fix_logger );
409             return;
410 0 0       0 }
411 0         0  
412             my %check_idx_dispatch =
413             map { ( $_ => 'check_' . $_ ); }
414             qw/follow_keys_from allow_keys allow_keys_from allow_keys_matching
415             warn_if_key_match warn_unless_key_match/;
416              
417             my %mode_move = (
418 0         0 layered => { preset => 1, normal => 1 },
419             preset => { normal => 1 },
420             normal => {},
421             );
422 11     11 0 17  
423 11         85 around notify_change => sub ($orig, $self, %args) {
424             if ($change_logger->is_trace) {
425 11         107 my @a = map { ( $_ => $args{$_} // '<undef>' ); } sort keys %args;
426 11         63 $change_logger->trace( "called for ", $self->name, " from ", join( ' ', caller ),
427             " with ", join( ' ', @a ) );
428             }
429              
430             # $idx may be undef if $self has changed, not necessarily its content
431             my $idx = $args{index};
432             if ( defined $idx ) {
433              
434             # use $idx to trigger move from layered->preset->normal
435             my $imode = $self->instance->get_data_mode;
436             my $old_mode = $self->get_data_mode($idx) || 'normal';
437             $self->set_data_mode( $idx, $imode ) if $mode_move{$old_mode}{$imode};
438             }
439              
440             return if $self->instance->initial_load and not $args{really};
441              
442             $self-> needs_content_check(1);
443             $self->$orig(%args);
444             return;
445             };
446              
447             # the number of checks is becoming confusing. We have
448             # - check_idx to check whether an index is fine. This is called when creating
449             # a new index
450             # - check_content: a more expensive check that runs all content checker registered
451             # in this object. By default, none. A plain AnyId can contains a duplicated_content
452             # checker if configured
453             # - a deep_check (for lack of a better name): a also expensive check that involve index
454             # versus other part of the config tree. By default, no check is done. This is currently
455             # used only by DPkg model which check if the index value is used elsewhere
456              
457             # Using plain check in this class is avoided because it's too generic, but a polymorphic
458             # entry point is still needed, oh well...
459             # since check is not used when creating an index, but called explicitly
460             # so it can be forwarded to deep_check.
461             goto &deep_check; # backward compat
462             }
463              
464             $deep_check_logger->trace("called on ".$self->name);
465              
466             for ( $self->fetch_all_indexes() ) {
467             $self->check_idx(@args, index => $_);
468             }
469              
470             $self->check_content(@args, logger => $deep_check_logger);
471             return;
472             }
473              
474             # check globally the list or hash, called by apply_fix or deep_check
475             my $silent = $args{silent} || 0;
476             my $apply_fix = $args{fix} || 0;
477             my $local_logger = $args{logger} || $logger;
478              
479 0     0 1 0 if ( $self-> needs_content_check ) {
480              
481             $local_logger->trace( "Running check_content on ",$self->location );
482 16     16 0 28 # need to keep track to update GUI
  16         21  
  16         26  
  16         66  
483 16         52 $self-> flush_fixes; # reset before check
484              
485 16         159 my @error;
486 48         95 my @warn;
487              
488             foreach my $sub ( $self-> get_all_content_checks ) {
489 16         59 $sub->( \@error, \@warn, $apply_fix, $silent );
490 16         38 }
491              
492             my $nb = $self->fetch_size;
493             push @error, "Too many items ($nb) limit $self->{max_nb}, "
494 421     421 0 584 if defined $self->{max_nb} and $nb > $self->{max_nb};
  421         557  
  421         571  
  421         559  
495 421   50     1251  
496 421   100     2916 if (not $silent) {
497 421   66     1358 for ( @warn ) {
498             $user_logger->warn( "Warning in '" . $self->location_short . "': $_" )
499 421 100       1427 }
500             }
501 260         1407  
502              
503 260         2627 $self->{content_warning_list} = \@warn;
504             $self->{content_error_list} = \@error;
505 260         5621 $self-> needs_content_check(0);
506              
507             return scalar @error ? 0 : 1;
508 260         766 }
509 17         205 else {
510             $local_logger->debug( $self->location, " has not changed, actual check skipped" )
511             if $local_logger->is_debug;
512 260         2291 my $err = $self->{content_error_list} // [];
513             return scalar @$err ? 0 : 1;
514 260 50 33     782 }
515             }
516 260 50       573  
517 260         518 # internal function to check the validity of the index. Called when creating a new
518 2         43 # index or when set_properties is called (init or during warp)
519             my %args = _resolve_arg_shortcut(\@args, 'index');
520             my $idx = $args{index};
521             my $silent = $args{silent} || 0;
522             my $check = $args{check} || 'yes';
523 260         624 my $apply_fix = $args{fix} // ($check eq 'fix' ? 1 : 0);
524 260         498  
525 260         814 Config::Model::Exception::Internal->throw(
526             object => $self,
527 260 100       917 error => "check_idx method: key or index is not defined"
528             ) unless defined $idx;
529              
530 161 100       491 my @error;
531             my @warn;
532 161   50     1223  
533 161 100       524 foreach my $key_check_name ( keys %check_idx_dispatch ) {
534             next unless $self->{$key_check_name};
535             my $method = $check_idx_dispatch{$key_check_name};
536             $self->$method( $idx, \@error, \@warn, $apply_fix );
537             }
538              
539 1032     1032 0 1542 my $nb = $self->fetch_size;
  1032         1326  
  1032         1798  
  1032         1327  
540 1032         2607 my $new_nb = $nb;
541 1032         2063 $new_nb++ unless $self->_exists($idx);
542 1032   50     3257  
543 1032   100     2393 if ( $idx eq '' ) {
544 1032 50 66     3769 push @error, "Index is empty";
545             }
546 1032 50       1990 elsif ( $self->{index_type} eq 'integer' and $idx =~ /\D/ ) {
547             push @error, "Index is not integer ($idx)";
548             }
549             elsif ( defined $self->{max_index} and $idx > $self->{max_index} ) {
550             push @error, "Index $idx > max_index limit $self->{max_index}";
551 1032         1661 }
552             elsif ( defined $self->{min_index} and $idx < $self->{min_index} ) {
553             push @error, "Index $idx < min_index limit $self->{min_index}";
554 1032         3376 }
555 6192 100       9754  
556 10         20 push @error, "Too many items ($new_nb) limit $self->{max_nb}, " . "rejected id '$idx'"
557 10         103 if defined $self->{max_nb} and $new_nb > $self->{max_nb};
558              
559             if ( scalar @error ) {
560 1032         3245 my @a = $self->_fetch_all_indexes;
561 1032         1499 push @error, "Item ids are '" . join( ',', @a ) . "'", $self->warp_error;
562 1032 100       2366 }
563              
564 1032 100 66     8077 $self->{idx_error_list} = \@error;
    50 100        
    100 100        
    100          
565 1         4 $self->{warning_hash}{$idx} = \@warn;
566              
567             if (@warn and not $silent and $check ne 'no') {
568 0         0 for (@warn) {
569             $user_logger->warn( "Warning in '" . $self->location_short . "': $_" );
570             }
571 2         12 }
572              
573             return scalar @error ? 0 : 1;
574 1         6 }
575              
576             #internal
577             my ( $self, $idx, $error ) = @_;
578 1032 100 100     2614  
579             my $followed = $self->safe_typed_grab( param => 'follow_keys_from' );
580 1032 100       2026 return if $followed->exists($idx);
581 13         40  
582 13         96 push @$error,
583             "key '" . $self->shorten_idx($idx) . "' does not exists in followed object '"
584             . $followed->name
585 1032         2106 . "'. Expected '"
586 1032         2719 . join( "', '", $followed->fetch_all_indexes ) . "'";
587             return;
588 1032 50 66     2682 }
      66        
589 3         8  
590 3         32 #internal
591             my ( $self, $idx, $error ) = @_;
592              
593             my $ok = grep { $_ eq $idx } @{ $self->{allow_keys} };
594 1032 100       3465  
595             push @$error,
596             "Unexpected key '" . $self->shorten_idx($idx) . "'. Expected '" . join( "', '", @{ $self->{allow_keys} } ) . "'"
597             unless $ok;
598             return;
599 1     1 0 4 }
600              
601 1         7 #internal
602 1 50       7 my ( $self, $idx, $error ) = @_;
603             my $match = $self->{allow_keys_matching};
604 1         12  
605             push @$error, "Unexpected key '" . $self->shorten_idx($idx) . "'. Key must match $match"
606             unless $idx =~ /$match/;
607             return;
608             }
609 1         5  
610             #internal
611             my ( $self, $idx, $error ) = @_;
612              
613             my $from = $self->safe_typed_grab( param => 'allow_keys_from' );
614 2     2 0 8 my $ok = grep { $_ eq $idx } $from->fetch_all_indexes;
615              
616 2         4 return if $ok;
  6         12  
  2         7  
617              
618             push @$error,
619 2 100       15 "key '" . $self->shorten_idx($idx) . "' does not exists in '"
  1         5  
620             . $from->name
621 2         6 . "'. Expected '"
622             . join( "', '", $from->fetch_all_indexes ) . "'";
623             return;
624             }
625              
626 2     2 0 5 my ( $self, $idx, $error, $warn ) = @_;
627 2         6 my $re = $self->{warn_if_key_match};
628              
629 2 100       46 push @$warn, "key '" . $self->shorten_idx($idx) . "' should not match $re\n" if $idx =~ /$re/;
630             return;
631 2         6 }
632              
633             my ( $self, $idx, $error, $warn ) = @_;
634             my $re = $self->{warn_unless_key_match};
635              
636 2     2 0 8 push @$warn, "key '" . $self->shorten_idx($idx) . "' should match $re\n" unless $idx =~ /$re/;
637             return;
638 2         12 }
639 2         14  
  8         15  
640             my ( $self, $error, $warn, $apply_fix, $silent ) = @_;
641 2 100       10  
642             my $dup = $self->{duplicates};
643 1         9 return if $dup eq 'allow';
644              
645             $logger->trace("check_duplicates called");
646             my %h;
647             my @issues;
648 1         5 my @to_delete;
649             foreach my $i ( $self->fetch_all_indexes ) {
650             my $v = $self->fetch_with_id( index => $i, check => 'no' )->fetch;
651             next unless $v;
652 2     2 0 6 $h{$v} = 0 unless defined $h{$v};
653 2         5 $h{$v}++;
654             if ( $h{$v} > 1 ) {
655 2 50       43 $logger->debug("got duplicates $i -> $v : $h{$v}");
656 2         5 push @to_delete, $i;
657             push @issues, qq!$i:"$v"!;
658             }
659             }
660 1     1 0 5  
661 1         5 return unless @issues;
662              
663 1 50       24 if ($apply_fix) {
664 1         4 $logger->debug("Fixing duplicates @issues, removing @to_delete");
665             for (reverse @to_delete) { $self->remove($_) }
666             }
667             elsif ( $dup eq 'forbid' ) {
668 17     17 0 56 $logger->debug("Found forbidden duplicates @issues");
669             push @$error, "Forbidden duplicates value @issues";
670 17         43 }
671 17 50       61 elsif ( $dup eq 'warn' ) {
672             $logger->debug("warning condition: found duplicate @issues");
673 17         55 push @$warn, "Duplicated value: @issues";
674 17         137 $self->add_fixes( scalar @issues);
675             }
676 17         0 elsif ( $dup eq 'suppress' ) {
677 17         49 $logger->debug("suppressing duplicates @issues");
678 29         83 for (reverse @to_delete) { $self->remove($_) }
679 29 100       69 }
680 20 100       63 else {
681 20         33 die "Internal error: duplicates is $dup";
682 20 100       47 }
683 8         43 return;
684 8         58 }
685 8         21  
686             my %args = _resolve_arg_shortcut(\@args, 'index');
687             my $check = $self->_check_check( $args{check} );
688             my $idx = $args{index};
689 17 100       80  
690             $logger->trace( $self->name, " called for idx $idx" ) if $logger->is_trace;
691 5 100       27  
    100          
    100          
    50          
692 1         6 $idx = $self->{convert_sub}($idx)
693 1         6 if ( defined $self->{convert_sub} and defined $idx );
  1         6  
694              
695             # try migration only once
696 1         6 $self->_migrate unless $self->{migration_done};
697 1         6  
698             my $ok = 1;
699              
700 2         13 # check index only if it's unknown
701 2         26 $ok = $self->check_idx( index => $idx, check => $check )
702 2         17 unless $self->_defined($idx)
703             or $check eq 'no';
704              
705 1         7 if ( $ok or $check eq 'no' ) {
706 1         18 # create another method
  2         7  
707             $self->create_default_content($idx); # no-op if idx exists
708              
709 0         0 $self->auto_vivify($idx) unless $self->_defined($idx);
710             return $self->_fetch_with_id($idx);
711 5         57 }
712             else {
713             Config::Model::Exception::WrongValue->throw(
714 3675     3675 1 49472 error => join( "\n\t", @{ $self->{idx_error_list} } ),
  3675         4766  
  3675         5668  
  3675         4627  
715 3675         8580 object => $self
716 3675         11642 );
717 3675         7211 }
718              
719 3675 100       9154 return;
720             }
721              
722 3675 100 66     24193 my %args = _resolve_arg_shortcut(\@args, 'path');
723             my $path = delete $args{path};
724             my $autoadd = 1;
725 3675 100       9010 $autoadd = $args{autoadd} if defined $args{autoadd};
726             my $get_obj = delete $args{get_obj} || 0;
727 3675         5040 $path =~ s!^/!!;
728             my ( $item, $new_path ) = split m!/!, $path, 2;
729              
730 3675 100 100     9925 my $dcm = $args{dir_char_mockup};
731              
732             # $item =~ s($dcm)(/)g if $dcm ;
733             if ($dcm) {
734 3675 100 66     9035 while (1) {
735             my $i = index( $item, $dcm );
736 3663         8908 last if $i == -1;
737             substr $item, $i, length($dcm), '/';
738 3663 100       6538 }
739 3663         9662 }
740              
741             return unless ( $self->exists($item) or $autoadd );
742              
743 12         21 $logger->trace("get: path $path, item $item");
  12         134  
744              
745             my $obj = $self->fetch_with_id( index => $item, %args );
746             return $obj if ( ( $get_obj or $obj->get_type ne 'leaf' ) and not defined $new_path );
747             return $obj->get( path => $new_path, get_obj => $get_obj, %args );
748 0         0 }
749              
750             $path =~ s!^/!!;
751 3     3 1 4 my ( $item, $new_path ) = split m!/!, $path, 2;
  3         5  
  3         5  
  3         3  
752 3         7 return $self->fetch_with_id($item)->set( $new_path, @args );
753 3         8 }
754 3         4  
755 3 50       5 my ( $self, $from, $to ) = @_;
756 3   100     8  
757 3         5 my $from_obj = $self->fetch_with_id($from);
758 3         7 my $ok = $self->check_idx($to);
759              
760 3         5 if ( $ok && $self->{cargo}{type} eq 'leaf' ) {
761             $logger->trace( "AnyId: copy leaf value from " . $self->name . " $from to $to" );
762             return $self->fetch_with_id($to)->store( $from_obj->fetch() );
763 3 50       5 }
764 0         0 elsif ($ok) {
765 0         0  
766 0 0       0 # node object
767 0         0 $logger->trace( "AnyId: deep copy node from " . $self->name );
768             my $target = $self->fetch_with_id($to);
769             $logger->trace( "AnyId: deep copy node to " . $target->name );
770             return $target->copy_from($from_obj);
771 3 50 33     8 }
772             else {
773 3         13 Config::Model::Exception::WrongValue->throw(
774             error => join( "\n\t", @{ $self->{idx_error_list} } ),
775 3         21 object => $self
776 3 50 66     11 );
      33        
777 3         10 }
778             return;
779             }
780 1     1 1 2  
  1         1  
  1         2  
  1         1  
  1         2  
781 1         2 my $self = shift;
782 1         2 my @keys = $self->fetch_all_indexes;
783 1         3 return map { $self->fetch_with_id($_); } @keys;
784             }
785              
786             return join(',', $self->fetch_all_values(@args) );
787 7     7 1 38 }
788              
789 7         36 my %args = _resolve_arg_shortcut(\@args, 'idx');
790 7         37 return $self->_fetch_value(%args, sub => 'fetch');
791             }
792 7 100 66     63  
    50          
793 4         20 my %args = _resolve_arg_shortcut(\@args, 'idx');
794 4         43 return $self->_fetch_value(%args, sub => 'fetch_summary');
795             }
796              
797             my $check = $self->_check_check( $args{check} );
798             my $sub = delete $args{sub};
799 3         14  
800 3         28 if ( $self->{cargo}{type} eq 'leaf' ) {
801 3         14 return $self->fetch_with_id($args{idx})->$sub( check => $check, mode => $args{mode} );
802 3         32 }
803             else {
804             Config::Model::Exception::WrongType->throw(
805             object => $self,
806 0         0 function => 'fetch_values',
  0         0  
807             got_type => $self->{cargo}{type},
808             expected_type => 'leaf',
809             info => "with index $args{idx}",
810 0         0 );
811             }
812             return;
813             }
814 46     46 1 57  
815 46         103 my %args = _resolve_arg_shortcut(\@args, 'mode');
816 46         84 my $mode = $args{mode};
  65         119  
817             my $check = $self->_check_check( $args{check} );
818              
819 1     1 1 1648 my @keys = $self->fetch_all_indexes;
  1         3  
  1         2  
  1         1  
820 1         5  
821             # verify content restrictions applied to List (e.g. no duplicate values)
822             my $ok = $check eq 'no' ? 1 : $self->check_content();
823 758     758 1 863  
  758         951  
  758         1327  
  758         879  
824 758         1829 if ( $ok or $check eq 'no' ) {
825 758         2257 return grep { defined $_ }
826             map { $self->fetch_value(idx => $_, check => $check, mode => $mode ); } @keys;
827             }
828 14     14 1 19 else {
  14         23  
  14         23  
  14         15  
829 14         38 Config::Model::Exception::WrongValue->throw(
830 14         53 error => join( "\n\t", @{ $self->{content_error_list} } ),
831             object => $self
832             );
833 772     772   1104 }
  772         933  
  772         1653  
  772         928  
834 772         2227 return;
835 772         1417 }
836              
837 772 50       1697 my $self = shift;
838 772         1897 $self->create_default; # will check itself if creation is necessary
839             $self->_migrate;
840             return $self->_fetch_all_indexes;
841             }
842              
843             my $self = shift;
844             carp "get_all_indexes is deprecated. use fetch_all_indexes";
845 0         0 return $self->fetch_all_indexes;
846             }
847              
848             my $self = shift;
849 0         0 return $self->fetch_all_indexes;
850             }
851              
852 414     414 1 4326 my $self = shift;
  414         636  
  414         760  
  414         511  
853 414         1093 return $self->fetch_size ;
854 414         779 }
855 414         1176  
856             # auto vivify must create according to cargo}{type
857 414         967 # node -> Node or user class
858             # leaf -> Value or user class
859              
860 414 100       1601 # warped node cannot be used. Same effect can be achieved by warping
861             # cargo_args
862 414 100 66     1070  
863 758         2320 my %element_default_class = (
864 412         1013 warped_node => 'WarpedNode',
  758         1848  
865             node => 'Node',
866             leaf => 'Value',
867             );
868 2         4  
  2         10  
869             my %can_override_class = (
870             node => 0,
871             leaf => 1,
872 0         0 );
873              
874             #internal
875             my ( $self, $idx ) = @_;
876 2238     2238 1 10161 my %cargo_args = %{ $self->cargo };
877 2238         7521 my $class = delete $cargo_args{class}; # to override class in cargo
878 2238         6500  
879 2238         5192 my $cargo_type = delete $cargo_args{type};
880              
881             Config::Model::Exception::Model->throw(
882             object => $self,
883 0     0 0 0 message => "unknown '$cargo_type' cargo type: "
884 0         0 . "in cargo_args. Expected "
885 0         0 . join( ' or ', keys %element_default_class )
886             ) unless defined $element_default_class{$cargo_type};
887              
888             my $el_class = 'Config::Model::' . $element_default_class{$cargo_type};
889 0     0 1 0  
890 0         0 if ( defined $class ) {
891             Config::Model::Exception::Model->throw(
892             object => $self,
893             message => "$cargo_type class " . "cannot be overidden by '$class'"
894 2     2 1 5 ) unless $can_override_class{$cargo_type};
895 2         10 $el_class = $class;
896             }
897              
898              
899             my @common_args = (
900             element_name => $self->{element_name},
901             index_value => $idx,
902             instance => $self->{instance},
903             parent => $self->parent,
904             container => $self,
905             %cargo_args,
906             );
907              
908             my $item;
909              
910             # check parameters passed by the user
911             if ( $cargo_type eq 'node' ) {
912             $item = $self->load_node( @common_args, config_class_name => $self->config_class_name );
913             }
914             else {
915             Mouse::Util::load_class($el_class);
916             $item = $el_class->new(@common_args);
917             }
918 923     923 0 2928  
919 923         1270 my $imode = $self->instance->get_data_mode;
  923         6217  
920 923         1970 $self->set_data_mode( $idx, $imode );
921              
922 923         1878 $self->_store( $idx, $item );
923             return;
924             }
925              
926             ## no critic (Subroutines::ProhibitBuiltinHomonyms)
927             my ( $self, $idx ) = @_;
928              
929 923 50       3182 return $self->_defined($idx);
930             }
931 923         2029  
932             ## no critic (Subroutines::ProhibitBuiltinHomonyms)
933 923 100       1925 my ( $self, $idx ) = @_;
934              
935             return $self->_exists($idx);
936             }
937 36 50       113  
938 36         65 ## no critic (Subroutines::ProhibitBuiltinHomonyms)
939             my ( $self, $idx ) = @_;
940              
941             delete $self->{warning_hash}{$idx};
942             my $ret = $self->_delete($idx);
943             # notification is not needed if the value was already delete or missing
944             $self->notify_change( note => "deleted entry $idx" ) if defined $ret;
945             return $ret;
946 923         4427 }
947              
948             my ($self) = @_;
949              
950             $self->{warning_hash} = {};
951 923         1483 $self->_clear;
952             $self->clear_data_mode;
953             $self->notify_change( note => "cleared all entries" );
954 923 100       1916 return;
955 283         1034 }
956              
957             my ($self) = @_;
958 640         2455 carp "clear_values deprecated";
959 640         12053  
960             my $ct = $self->get_cargo_type;
961             Config::Model::Exception::User->throw(
962 923         5676 object => $self,
963 923         3749 message => "clear_values() called on non leaf cargo type: '$ct'"
964             ) if $ct ne 'leaf';
965 923         44927  
966 923         2751 # this will trigger a notify_change
967             for ( $self->fetch_all_indexes ) {
968             $self->fetch_with_id($_)->store(undef);
969             }
970             $self->notify_change( note => "cleared all values" );
971 0     0 1 0 return;
972             }
973 0         0  
974             my ( $self, $idx ) = @_;
975             my $list ;
976             if ( defined $idx ) {
977             $list = $self->{warning_hash}{$idx} ;
978 43     43 1 103 }
979             elsif ( @{ $self->{content_warning_list} } ) {
980 43         140 $list = $self->{content_warning_list} ;
981             }
982             return $list ? join( "\n", @$list ) : '';
983             }
984              
985 53     53 1 3129 my $self = shift;
986              
987 53         154 return @{ $self->{content_warning_list} };
988 53         175 }
989              
990 53 100       286 my $self = shift;
991 53         834 my @list;
992             for (qw/idx_error_list content_error_list/) {
993             push @list, @{ $self->{$_} } if $self->{$_};
994             }
995 18     18 1 2279  
996             return unless @list;
997 18         91 return wantarray ? @list : join( "\n\t", @list );
998 18         93 }
999 18         123  
1000 18         236 __PACKAGE__->meta->make_immutable;
1001 18         57  
1002             1;
1003              
1004             # ABSTRACT: Base class for hash or list element
1005 0     0 1 0  
1006 0         0  
1007             =pod
1008 0         0  
1009 0 0       0 =encoding UTF-8
1010              
1011             =head1 NAME
1012              
1013             Config::Model::AnyId - Base class for hash or list element
1014              
1015 0         0 =head1 VERSION
1016 0         0  
1017             version 2.151
1018 0         0  
1019 0         0 =head1 SYNOPSIS
1020              
1021             use Config::Model;
1022              
1023 0     0 1 0 # define configuration tree object
1024 0         0 my $model = Config::Model->new;
1025 0 0       0 $model->create_config_class(
    0          
1026 0         0 name => "Foo",
1027             element => [
1028 0         0 [qw/foo bar/] => {
1029 0         0 type => 'leaf',
1030             value_type => 'string'
1031 0 0       0 },
1032             ]
1033             );
1034              
1035 59     59 1 791 $model->create_config_class(
1036             name => "MyClass",
1037 59         74 element => [
  59         183  
1038             plain_hash => {
1039             type => 'hash',
1040             index_type => 'string',
1041 0     0 1   cargo => {
1042 0           type => 'leaf',
1043 0           value_type => 'string',
1044 0 0         },
  0            
1045             },
1046             bounded_hash => {
1047 0 0         type => 'hash', # hash id
1048 0 0         index_type => 'integer',
1049              
1050             # hash boundaries
1051             min_index => 1, max_index => 123, max_nb => 2,
1052              
1053             # specify cargo held by hash
1054             cargo => {
1055             type => 'leaf',
1056             value_type => 'string'
1057             },
1058             },
1059             bounded_list => {
1060             type => 'list', # list id
1061              
1062             max_index => 123,
1063             cargo => {
1064             type => 'leaf',
1065             value_type => 'string'
1066             },
1067             },
1068             hash_of_nodes => {
1069             type => 'hash', # hash id
1070             index_type => 'string',
1071             cargo => {
1072             type => 'node',
1073             config_class_name => 'Foo'
1074             },
1075             },
1076             ],
1077             );
1078              
1079             my $inst = $model->instance( root_class_name => 'MyClass' );
1080              
1081             my $root = $inst->config_root;
1082              
1083             # put data
1084             my $steps = 'plain_hash:foo=boo bounded_list=foo,bar,baz
1085             bounded_hash:3=foo bounded_hash:30=baz
1086             hash_of_nodes:"foo node" foo="in foo node" -
1087             hash_of_nodes:"bar node" bar="in bar node" ';
1088             $root->load( steps => $steps );
1089              
1090             # dump resulting tree
1091             print $root->dump_tree;
1092              
1093             =head1 DESCRIPTION
1094              
1095             This class provides hash or list elements for a L<Config::Model::Node>.
1096              
1097             The hash index can either be en enumerated type, a boolean, an integer
1098             or a string.
1099              
1100             =head1 CONSTRUCTOR
1101              
1102             AnyId object should not be created directly.
1103              
1104             =head1 Hash or list model declaration
1105              
1106             A hash or list element must be declared with the following parameters:
1107              
1108             =over
1109              
1110             =item type
1111              
1112             Mandatory element type. Must be C<hash> or C<list> to have a
1113             collection element. The actual element type must be specified by
1114             C<< cargo => type >>.
1115              
1116             =item index_type
1117              
1118             Either C<integer> or C<string>. Mandatory for hash.
1119              
1120             =item ordered
1121              
1122             Whether to keep the order of the hash keys (default no). (a bit like
1123             L<Tie::IxHash>). The hash keys are ordered along their creation. The
1124             order can be modified with L<swap|Config::Model::HashId/"swap ( key1 , key2 )">,
1125             L<move_up|Config::Model::HashId/"move_up ( key )"> or
1126             L<move_down|Config::Model::HashId/"move_down ( key )">.
1127              
1128             =item duplicates
1129              
1130             Specify the policy regarding duplicated values stored in the list or as
1131             hash values (valid only when cargo type is C<leaf>). The policy can be
1132             C<allow> (default), C<suppress>, C<warn> (which offers the possibility
1133             to apply a fix), C<forbid>. Note that duplicates I<check cannot be
1134             performed when the duplicated value is stored>: this happens outside of
1135             this object. Duplicates can be check only after when the value is read.
1136              
1137             =item write_empty_value
1138              
1139             By default, hash entries without data are not saved in configuration
1140             files. Without data means the cargo of the hash key is empty: either
1141             its value is undef or all the values of the contained node are also empty.
1142              
1143             Set this parameter to 1 if the key must be saved in the configuration
1144             file even if the hash contains no value for that key.
1145              
1146             Note that writing hash entries without value may not be supported by
1147             all backends. Use with care. Supported only for hash elements.
1148              
1149             =item cargo
1150              
1151             Hash ref specifying the cargo held by the hash of list. This has must
1152             contain:
1153              
1154             =over 8
1155              
1156             =item type
1157              
1158             Can be C<node> or C<leaf> (default).
1159              
1160             =item config_class_name
1161              
1162             Specifies the type of configuration object held in the hash. Only
1163             valid when C<cargo> C<type> is C<node>.
1164              
1165             =item <other>
1166              
1167             Constructor arguments passed to the cargo object. See
1168             L<Config::Model::Node> when C<< cargo->type >> is C<node>. See
1169             L<Config::Model::Value> when C<< cargo->type >> is C<leaf>.
1170              
1171             =back
1172              
1173             =item min_index
1174              
1175             Specify the minimum value (optional, only for hash and for integer index)
1176              
1177             =item max_index
1178              
1179             Specify the maximum value (optional, only for list or for hash with
1180             integer index)
1181              
1182             =item max_nb
1183              
1184             Specify the maximum number of indexes. (hash only, optional, may also
1185             be used with string index type)
1186              
1187             =item default_keys
1188              
1189             When set, the default parameter (or set of parameters) are used as
1190             default keys hashes and created automatically when the C<keys> or C<exists>
1191             functions are used on an I<empty> hash.
1192              
1193             You can use C<< default_keys => 'foo' >>,
1194             or C<< default_keys => ['foo', 'bar'] >>.
1195              
1196             =item default_with_init
1197              
1198             To perform special set-up on children nodes you can also use
1199              
1200             default_with_init => {
1201             foo => 'X=Av Y=Bv',
1202             bar => 'Y=Av Z=Cv'
1203             }
1204              
1205             When the hash contains leaves, you can also use:
1206              
1207             default_with_init => {
1208             def_1 => 'def_1 stuff',
1209             def_2 => 'def_2 stuff'
1210             }
1211              
1212             =item migrate_keys_from
1213              
1214             Specifies that the keys of the hash are copied from another hash in
1215             the configuration tree only when the hash is read for the first time after
1216             initial load (i.e. once the configuration files are completely read).
1217              
1218             migrate_keys_from => '- another_hash'
1219              
1220             =item migrate_values_from
1221              
1222             Specifies that the values of the hash (or list) are copied from another hash (or list) in
1223             the configuration tree only when the hash (or list) is read for the first time after
1224             initial load (i.e. once the configuration files are completely read).
1225              
1226             migrate_values_from => '- another_hash_or_list'
1227              
1228             =item follow_keys_from
1229              
1230             Specifies that the keys of the hash follow the keys of another hash in
1231             the configuration tree. In other words, the created hash
1232             always has the same keys as the other hash.
1233              
1234             follow_keys_from => '- another_hash'
1235              
1236             =item allow_keys
1237              
1238             Specifies authorized keys:
1239              
1240             allow_keys => ['foo','bar','baz']
1241              
1242             =item allow_keys_from
1243              
1244             A bit like the C<follow_keys_from> parameters. Except that the hash pointed to
1245             by C<allow_keys_from> specified the authorized keys for this hash.
1246              
1247             allow_keys_from => '- another_hash'
1248              
1249             =item allow_keys_matching
1250              
1251             Keys must match the specified regular expression. For instance:
1252              
1253             allow_keys_matching => '^foo\d\d$'
1254              
1255             =item auto_create_keys
1256              
1257             When set, the default parameter (or set of parameters) are used as
1258             keys hashes and created automatically. (valid only for hash elements)
1259              
1260             Called with C<< auto_create_keys => ['foo'] >>, or
1261             C<< auto_create_keys => ['foo', 'bar'] >>.
1262              
1263             =item warn_if_key_match
1264              
1265             Issue a warning if the key matches the specified regular expression
1266              
1267             =item warn_unless_key_match
1268              
1269             Issue a warning unless the key matches the specified regular expression
1270              
1271             =item auto_create_ids
1272              
1273             Specifies the number of elements to create automatically. E.g. C<<
1274             auto_create_ids => 4 >> initializes the list with 4 undef elements.
1275             (valid only for list elements)
1276              
1277             =item convert => [uc | lc ]
1278              
1279             The hash key are converted to uppercase (uc) or lowercase (lc).
1280              
1281             =item warp
1282              
1283             See L</"Warp: dynamic value configuration"> below.
1284              
1285             =back
1286              
1287             =head1 Warp: dynamic value configuration
1288              
1289             The Warp functionality enables an L<HashId|Config::Model::HashId> or
1290             L<ListId|Config::Model::ListId> object to change its default settings
1291             (e.g. C<min_index>, C<max_index> or C<max_nb> parameters) dynamically according to
1292             the value of another C<Value> object. (See
1293             L<Config::Model::Warper> for explanation on warp mechanism)
1294              
1295             For instance, with this model:
1296              
1297             $model ->create_config_class
1298             (
1299             name => 'Root',
1300             'element'
1301             => [
1302             macro => { type => 'leaf',
1303             value_type => 'enum',
1304             name => 'macro',
1305             choice => [qw/A B C/],
1306             },
1307             warped_hash => { type => 'hash',
1308             index_type => 'integer',
1309             max_nb => 3,
1310             warp => {
1311             follow => '- macro',
1312             rules => { A => { max_nb => 1 },
1313             B => { max_nb => 2 }
1314             }
1315             },
1316             cargo => { type => 'node',
1317             config_class_name => 'Dummy'
1318             }
1319             },
1320             ]
1321             );
1322              
1323             Setting C<macro> to C<A> means that C<warped_hash> can only accept
1324             one C<Dummy> class item .
1325              
1326             Setting C<macro> to C<B> means that C<warped_hash> accepts two
1327             C<Dummy> class items.
1328              
1329             Like other warped class, a HashId or ListId can have multiple warp
1330             masters (See L<Config::Model::Warper/"Warp follow argument">:
1331              
1332             warp => { follow => { m1 => '- macro1',
1333             m2 => '- macro2'
1334             },
1335             rules => [ '$m1 eq "A" and $m2 eq "A2"' => { max_nb => 1},
1336             '$m1 eq "A" and $m2 eq "B2"' => { max_nb => 2}
1337             ],
1338             }
1339              
1340             =head2 Warp and auto_create_ids or auto_create_keys
1341              
1342             When a warp is applied with C<auto_create_keys> or C<auto_create_ids>
1343             parameter, the auto_created items are created if they are not already
1344             present. But this warp never removes items that were previously
1345             auto created.
1346              
1347             For instance, when a tied hash is created with
1348             C<< auto_create => [a,b,c] >>, the hash contains C<(a,b,c)>.
1349              
1350             Then, once a warp with C<< auto_create_keys => [c,d,e] >> is applied,
1351             the hash then contains C<(a,b,c,d,e)>. The items created by the first
1352             auto_create_keys are not removed.
1353              
1354             =head2 Warp and max_nb
1355              
1356             When a warp is applied, the items that do not fit the constraint
1357             (e.g. min_index, max_index) are removed.
1358              
1359             For the max_nb constraint, an exception is raised if a warp
1360             leads to a number of items greater than the max_nb constraint.
1361              
1362             =head1 Content check
1363              
1364             By default, this class provides an optional content check that checks
1365             for duplicated values (when C<duplicates> parameter is set).
1366              
1367             Derived classes can register more global checker with the following method.
1368              
1369             =head2 add_check_content
1370              
1371             This method expects a sub ref with signature C<( $self, $error, $warn,
1372             $apply_fix )>. Where C<$error> and C<$warn> are array ref. You can
1373             push error or warning messages there. C<$apply_fix> is a
1374             boolean. When set to 1, the passed method can fix the warning or the
1375             error. Please make sure to weaken C<$self> to avoid memory cycles.
1376              
1377             Example:
1378              
1379             package MyId;
1380             use Mouse;
1381             extends qw/Config::Model::HashId/;
1382             use Scalar::Util qw/weaken/;
1383              
1384             sub setup {
1385             my $self = shift;
1386             weaken($self);
1387             $self-> add_check_content( sub { $self->check_usused_licenses(@_);} )
1388             }
1389              
1390             =head1 Introspection methods
1391              
1392             The following methods returns the current value stored in the Id
1393             object (as declared in the model unless they were warped):
1394              
1395             =over
1396              
1397             =item min_index
1398              
1399             =item max_index
1400              
1401             =item max_nb
1402              
1403             =item index_type
1404              
1405             =item default_keys
1406              
1407             =item default_with_init
1408              
1409             =item follow_keys_from
1410              
1411             =item auto_create_ids
1412              
1413             =item auto_create_keys
1414              
1415             =item ordered
1416              
1417             =item morph
1418              
1419             =item config_model
1420              
1421             =back
1422              
1423             =head2 get_cargo_type
1424              
1425             Returns the object type contained by the hash or list (i.e. returns
1426             C<< cargo -> type >>).
1427              
1428             =head2 get_cargo_info
1429              
1430             Parameters: C<< ( < what > ) >>
1431              
1432             Returns more info on the cargo contained by the hash or list. C<what>
1433             may be C<value_type> or any other cargo info stored in the model.
1434             Returns undef if the requested info is not provided in the model.
1435              
1436             =head2 get_default_keys
1437              
1438             Returns a list (or a list ref) of the current default keys. These keys
1439             can be set by the C<default_keys> or C<default_with_init> parameters
1440             or by the other hash pointed by C<follow_keys_from> parameter.
1441              
1442             =head2 name
1443              
1444             Returns the object name. The name finishes with ' id'.
1445              
1446             =head2 config_class_name
1447              
1448             Returns the config_class_name of collected elements. Valid only
1449             for collection of nodes.
1450              
1451             This method returns undef if C<cargo> C<type> is not C<node>.
1452              
1453             =head2 has_fixes
1454              
1455             Returns the number of fixes that can be applied to the current value.
1456              
1457             =head1 Information management
1458              
1459             =head2 fetch_with_id
1460              
1461             Parameters: C<< ( index => $idx , [ check => 'no' ]) >>
1462              
1463             Fetch the collected element held by the hash or list. Index check is 'yes' by default.
1464             Can be called with one parameter which is used as index.
1465              
1466             =head2 get
1467              
1468             Get a value from a directory like path. Parameters are:
1469              
1470             =over
1471              
1472             =item path
1473              
1474             Poor man's version of XPath style path. This string is in the form:
1475              
1476             /foo/bar/4
1477              
1478             Each word between the '/' is either an element name or a hash key or a list index.
1479              
1480             =item mode
1481              
1482             Either C<default>, C<custom>, C<user>,...
1483             See C<mode> parameter in <Config::Model::Value/"fetch( ... )">
1484              
1485             =item check
1486              
1487             Either C<skip>, C<no>
1488              
1489             =item get_obj
1490              
1491             If the path leads to a leaf, this parameter tell whether to return
1492             the stored value or the value object.
1493              
1494             =item autoadd
1495              
1496             Whether to create missing keys
1497              
1498             =item dir_char_mockup
1499              
1500             When the hash key used contains '/', (for instance a directory value),
1501             the key cannot be used as is with this method. Because '/' is already
1502             used to separate configuration items (this is also important with
1503             L<Config::Model::FuseUI>). This parameter specifies how the forbidden
1504             '/' char is shown in the path. Default is C<< <slash> >>
1505              
1506             =back
1507              
1508             =head2 set
1509              
1510             Parameters: C<( path, value )>
1511              
1512             Set a value with a directory like path.
1513              
1514             =head2 copy
1515              
1516             Parameters: C<( from_index, to_index )>
1517              
1518             Deep copy an element within the hash or list. If the element contained
1519             by the hash or list is a node, all configuration information is
1520             copied from one node to another.
1521              
1522             =head2 fetch_all
1523              
1524             Returns an array containing all elements held by the hash or list.
1525              
1526             =head2 fetch_value
1527              
1528             Parameters: C<< ( idx => ..., mode => ..., check => ...) >>
1529              
1530             Returns the value held by the C<idx> element of the hash or list. This
1531             method is only valid for hash or list containing leaves.
1532              
1533             See L<fetch_all_values> for C<mode> argument documentation and
1534             L<Config::Model::Value/fetch> for C<check> argument documentation.
1535              
1536             =head2 fetch_summary
1537              
1538             Arguments: C<< ( idx => ..., mode => ..., check => ...) >>
1539              
1540             Like L</fetch_value>, but returns a truncated value when the value is
1541             a string or uniline that is too long to be displayed.
1542              
1543             =head2 fetch_all_values
1544              
1545             Parameters: C<< ( mode => ..., check => ...) >>
1546              
1547             Returns an array containing all defined values held by the hash or
1548             list. (undefined values are simply discarded). This method is only
1549             valid for hash or list containing leaves.
1550              
1551             With C<mode> parameter, this method returns either:
1552              
1553             =over
1554              
1555             =item custom
1556              
1557             The value entered by the user
1558              
1559             =item preset
1560              
1561             The value entered in preset mode
1562              
1563             =item standard
1564              
1565             The value entered in preset mode or checked by default.
1566              
1567             =item default
1568              
1569             The default value (defined by the configuration model)
1570              
1571             =back
1572              
1573             See L<Config::Model::Value/fetch> for C<check> argument documentation.
1574              
1575             =head2 fetch
1576              
1577             Similar to L</fetch_all_values>, with the same parameters, Returns the
1578             result as a string with comma separated list values.
1579              
1580             =head2 fetch_all_indexes
1581              
1582             Returns an array containing all indexes of the hash or list. Hash keys
1583             are sorted alphabetically, except for ordered hashed.
1584              
1585             =head2 children
1586              
1587             Like fetch_all_indexes. This method is
1588             polymorphic for all non-leaf objects of the configuration tree.
1589              
1590             =head2 defined
1591              
1592             Parameters: C<( index )>
1593              
1594             Returns true if the value held at C<index> is defined.
1595              
1596             =head2 exists
1597              
1598             Parameters: C<( index )>
1599              
1600             Returns true if the value held at C<index> exists (i.e the key exists
1601             but the value may be undefined). This method may not make sense for
1602             list element.
1603              
1604             =head2 has_data
1605              
1606             Return true if the array or hash is not empty.
1607              
1608             =head2 delete
1609              
1610             Parameters: C<( index )>
1611              
1612             Delete the C<index>ed value
1613              
1614             =head2 clear
1615              
1616             Delete all values (also delete underlying value or node objects).
1617              
1618             =head2 clear_values
1619              
1620             Delete all values (without deleting underlying value objects).
1621              
1622             =head2 warning_msg
1623              
1624             Parameters: C<( [index] )>
1625              
1626             Returns warnings concerning indexes of this hash.
1627             Without parameter, returns a string containing all warnings or undef. With an index, return the warnings
1628             concerning this index or undef.
1629              
1630             =head2 has_warning
1631              
1632             Returns the current number of warning.
1633              
1634             =head2 error_msg
1635              
1636             Returns the error messages of this object (if any)
1637              
1638             =head1 AUTHOR
1639              
1640             Dominique Dumont, ddumont [AT] cpan [DOT] org
1641              
1642             =head1 SEE ALSO
1643              
1644             L<Config::Model>,
1645             L<Config::Model::Instance>,
1646             L<Config::Model::Node>,
1647             L<Config::Model::WarpedNode>,
1648             L<Config::Model::HashId>,
1649             L<Config::Model::ListId>,
1650             L<Config::Model::CheckList>,
1651             L<Config::Model::Value>
1652              
1653             =head1 AUTHOR
1654              
1655             Dominique Dumont
1656              
1657             =head1 COPYRIGHT AND LICENSE
1658              
1659             This software is Copyright (c) 2005-2022 by Dominique Dumont.
1660              
1661             This is free software, licensed under:
1662              
1663             The GNU Lesser General Public License, Version 2.1, February 1999
1664              
1665             =cut