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   25621  
  38         124  
13             use Mouse;
14 38     38   223 with "Config::Model::Role::NodeLoader";
  38         82  
  38         260  
15             with "Config::Model::Role::Utils";
16              
17             use Config::Model::Exception;
18 38     38   14504 use Config::Model::Warper;
  38         80  
  38         1004  
19 38     38   200 use Carp qw/cluck croak carp/;
  38         79  
  38         1183  
20 38     38   209 use Log::Log4perl qw(get_logger :levels);
  38         92  
  38         2382  
21 38     38   261 use Storable qw/dclone/;
  38         87  
  38         267  
22 38     38   4953 use Mouse::Util::TypeConstraints;
  38         85  
  38         1925  
23 38     38   258 use Scalar::Util qw/weaken/;
  38         88  
  38         269  
24 38     38   4040  
  38         99  
  38         2146  
25             extends qw/Config::Model::AnyThing/;
26              
27             use feature qw/signatures postderef/;
28 38     38   257 no warnings qw/experimental::signatures experimental::postderef/;
  38         84  
  38         4080  
29 38     38   247  
  38         101  
  38         261916  
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   271 }
151 109         1164  
152             my $self = shift;
153             return $self->cargo->{config_class_name};
154             }
155 293     293 1 545  
156 293         1660 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 3046  
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     2208 . $self->element_name || 'unknown';
164             }
165 511 50 66     2460  
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     2874 $self->set_properties();
171 0         0  
172             if ( defined $self->warp ) {
173             $self->{warper} = Config::Model::Warper->new(
174 511         1854 warped_object => $self,
175             %{ $self->warp },
176 508 100       1819 allowed => \@allowed_warp_params
177             );
178             }
179 6         11  
  6         120  
180             return $self;
181             }
182              
183             # this method can be called by the warp mechanism to alter (warp) the
184 508         1392 # 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 840  
  544         879  
  544         864  
  544         738  
190             # these are handled by Node or Warper
191 544         1392 for ( qw/level/ ) { delete $args{$_}; }
  9792         12078  
192              
193 544         867 $logger->trace( $self->name, " set_properties called with @args" );
  544         1725  
194              
195             for ( @common_params ) {
196 544         1228 $self->{$_} = delete $args{$_} if defined $args{$_};
  544         1159  
197             }
198 544         1576  
199             $self->set_convert( \%args ) if defined $args{convert};
200 544         4812  
201 8704 100       14426 $self-> clear_warpable_check_content;
202             for ( $self-> get_all_content_checks ) {
203             $self-> add_warpable_check_content($_);
204 544 100       1505 }
205             for ( qw/duplicates/ ) {
206 544         2448 my $method = "check_$_";
207 544         5928 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         4811 }
211 544         1120  
212 544         717 Config::Model::Exception::Model->throw(
213 544         2039 object => $self,
214 544 100   17   1712 error => "Undefined index_type"
  17         85  
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       1590 )
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     2003 my $last_idx = pop @current_idx;
228              
229 544         1701 foreach my $idx ( ( $first_idx, $last_idx ) ) {
230 544 100       1475 my $ok = $self->check_idx($first_idx);
231 15         26 next if $ok;
232 15         31  
233             # here a user input may trigger an exception even if fetch
234 15         25 # or set value check is disabled. That's mostly because,
235 29         66 # we cannot enforce more strict settings without random
236 29 100       66 # 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         6  
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         1828 Config::Model::Exception::Model->throw(
256             object => $self,
257 542 100 66     1757 error => "Cannot specify 'duplicates' with cargo type '$self->{cargo}{type}'",
      100        
258             );
259             }
260 1         6  
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         923 );
267 541 100 100     1690 }
268 1         8  
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       1431 }
276              
277             my $self = shift;
278             my $idx = shift;
279 540         1172  
280             return unless defined $self->{default_with_init};
281              
282             my $h = $self->{default_with_init};
283 865     865 0 1192 foreach my $def_key ( keys %$h ) {
284 865         1164 $self->create_default_content($def_key);
285             }
286 865 100       2104 return;
287             }
288 4         9  
289 4         11 my $self = shift;
290 8         20 my $idx = shift // die "missing index";
291              
292 4         15 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 4732  
297 3671   50     6847 return if $self->_defined($idx) ; # object already created
298              
299 3671 100       8115 $self->auto_vivify($idx);
300              
301 37         79 my $v_obj = $self->_fetch_with_id($idx);
302 37 100       80 if ( $v_obj->get_type eq 'leaf' ) {
303             $v_obj->store( $def );
304 35 100       79 }
305             else {
306 8         26 $v_obj->load( $def );
307             }
308 8         23 return;
309 8 100       26 }
310 4         14  
311             my $self = shift;
312             carp $self->name, ": max param is deprecated, use max_index\n";
313 4         15 return $self->max_index;
314             }
315 8         22  
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 5202 }
331              
332             my $self = shift;
333 2812     2812 1 4652 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         7646 my $res = eval {
341             $self->grab(
342             step => $self->{$param},
343             type => $self->get_type,
344 2     2 1 5 check => $args{check} || 'yes',
345 2         4 );
346 2         10 };
347              
348             if ($@) {
349             my $e = $@;
350 11     11 0 20 my $msg = $e ? $e->full_message : '';
  11         18  
  11         23  
  11         23  
351 11   33     33 Config::Model::Exception::Model->throw(
352             object => $self,
353 11         18 error => "'$param' parameter: " . $msg
354             );
355             }
356              
357 11   100     53 return $res;
358             }
359              
360             my $self = shift;
361 11 100       86  
362 1         2 if ( $self->{follow_keys_from} ) {
363 1 50       3 my $followed = $self->safe_typed_grab( param => 'follow_keys_from' );
364 1         16 my @res = $followed->fetch_all_indexes;
365             return wantarray ? @res : \@res;
366             }
367              
368             my @res;
369              
370 10         20 push @res, @{ $self->{default_keys} }
371             if defined $self->{default_keys};
372              
373             push @res, keys %{ $self->{default_with_init} }
374 867     867 1 1795 if defined $self->{default_with_init};
375              
376 867 100       1765 return wantarray ? @res : \@res;
377 3         16 }
378 2         9  
379 2 100       16 my $self = shift;
380             return $self->{parent}->name . ' ' . $self->{element_name} . ' id';
381             }
382 864         1099  
383             # internal. Handle model declaration arguments
384 3         8 my $warp_info = delete $args{warp};
385 864 100       1728  
386             for (qw/index_class index_type morph ordered/) {
387 4         16 $self->{$_} = delete $args{$_} if defined $args{$_};
388 864 100       1555 }
389              
390 864 50       2136 $self->{backup} = dclone( \%args );
391              
392             $self->set_properties(%args) if defined $self->{index_type};
393              
394 1650     1650 1 3068 if ( defined $warp_info ) {
395 1650         4374 $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 20  
423 11         70 around notify_change => sub ($orig, $self, %args) {
424             if ($change_logger->is_trace) {
425 11         112 my @a = map { ( $_ => $args{$_} // '<undef>' ); } sort keys %args;
426 11         56 $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 26 # need to keep track to update GUI
  16         23  
  16         30  
  16         23  
483 16         51 $self-> flush_fixes; # reset before check
484              
485 16         147 my @error;
486 48         111 my @warn;
487              
488             foreach my $sub ( $self-> get_all_content_checks ) {
489 16         62 $sub->( \@error, \@warn, $apply_fix, $silent );
490 16         32 }
491              
492             my $nb = $self->fetch_size;
493             push @error, "Too many items ($nb) limit $self->{max_nb}, "
494 421     421 0 616 if defined $self->{max_nb} and $nb > $self->{max_nb};
  421         564  
  421         571  
  421         516  
495 421   50     3128  
496 421   100     1202 if (not $silent) {
497 421   66     1181 for ( @warn ) {
498             $user_logger->warn( "Warning in '" . $self->location_short . "': $_" )
499 421 100       1337 }
500             }
501 260         1344  
502              
503 260         2560 $self->{content_warning_list} = \@warn;
504             $self->{content_error_list} = \@error;
505 260         5815 $self-> needs_content_check(0);
506              
507             return scalar @error ? 0 : 1;
508 260         722 }
509 17         183 else {
510             $local_logger->debug( $self->location, " has not changed, actual check skipped" )
511             if $local_logger->is_debug;
512 260         2630 my $err = $self->{content_error_list} // [];
513             return scalar @$err ? 0 : 1;
514 260 50 33     767 }
515             }
516 260 50       542  
517 260         498 # internal function to check the validity of the index. Called when creating a new
518 2         44 # 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         680 my $apply_fix = $args{fix} // ($check eq 'fix' ? 1 : 0);
524 260         482  
525 260         824 Config::Model::Exception::Internal->throw(
526             object => $self,
527 260 100       836 error => "check_idx method: key or index is not defined"
528             ) unless defined $idx;
529              
530 161 100       436 my @error;
531             my @warn;
532 161   50     1158  
533 161 100       480 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 1628 my $nb = $self->fetch_size;
  1032         1440  
  1032         1970  
  1032         1282  
540 1032         2649 my $new_nb = $nb;
541 1032         2184 $new_nb++ unless $self->_exists($idx);
542 1032   50     3384  
543 1032   100     2432 if ( $idx eq '' ) {
544 1032 50 66     3828 push @error, "Index is empty";
545             }
546 1032 50       2155 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         1652 }
552             elsif ( defined $self->{min_index} and $idx < $self->{min_index} ) {
553             push @error, "Index $idx < min_index limit $self->{min_index}";
554 1032         3307 }
555 6192 100       9970  
556 10         30 push @error, "Too many items ($new_nb) limit $self->{max_nb}, " . "rejected id '$idx'"
557 10         80 if defined $self->{max_nb} and $new_nb > $self->{max_nb};
558              
559             if ( scalar @error ) {
560 1032         3216 my @a = $self->_fetch_all_indexes;
561 1032         1574 push @error, "Item ids are '" . join( ',', @a ) . "'", $self->warp_error;
562 1032 100       2595 }
563              
564 1032 100 66     8270 $self->{idx_error_list} = \@error;
    50 100        
    100 100        
    100          
565 1         3 $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         5 }
575              
576             #internal
577             my ( $self, $idx, $error ) = @_;
578 1032 100 100     2637  
579             my $followed = $self->safe_typed_grab( param => 'follow_keys_from' );
580 1032 100       2136 return if $followed->exists($idx);
581 13         38  
582 13         84 push @$error,
583             "key '" . $self->shorten_idx($idx) . "' does not exists in followed object '"
584             . $followed->name
585 1032         2119 . "'. Expected '"
586 1032         2657 . join( "', '", $followed->fetch_all_indexes ) . "'";
587             return;
588 1032 50 66     2619 }
      66        
589 3         5  
590 3         26 #internal
591             my ( $self, $idx, $error ) = @_;
592              
593             my $ok = grep { $_ eq $idx } @{ $self->{allow_keys} };
594 1032 100       3582  
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         5 #internal
602 1 50       5 my ( $self, $idx, $error ) = @_;
603             my $match = $self->{allow_keys_matching};
604 1         11  
605             push @$error, "Unexpected key '" . $self->shorten_idx($idx) . "'. Key must match $match"
606             unless $idx =~ /$match/;
607             return;
608             }
609 1         4  
610             #internal
611             my ( $self, $idx, $error ) = @_;
612              
613             my $from = $self->safe_typed_grab( param => 'allow_keys_from' );
614 2     2 0 6 my $ok = grep { $_ eq $idx } $from->fetch_all_indexes;
615              
616 2         4 return if $ok;
  6         11  
  2         6  
617              
618             push @$error,
619 2 100       9 "key '" . $self->shorten_idx($idx) . "' does not exists in '"
  1         5  
620             . $from->name
621 2         4 . "'. Expected '"
622             . join( "', '", $from->fetch_all_indexes ) . "'";
623             return;
624             }
625              
626 2     2 0 6 my ( $self, $idx, $error, $warn ) = @_;
627 2         4 my $re = $self->{warn_if_key_match};
628              
629 2 100       36 push @$warn, "key '" . $self->shorten_idx($idx) . "' should not match $re\n" if $idx =~ /$re/;
630             return;
631 2         7 }
632              
633             my ( $self, $idx, $error, $warn ) = @_;
634             my $re = $self->{warn_unless_key_match};
635              
636 2     2 0 6 push @$warn, "key '" . $self->shorten_idx($idx) . "' should match $re\n" unless $idx =~ /$re/;
637             return;
638 2         8 }
639 2         10  
  8         15  
640             my ( $self, $error, $warn, $apply_fix, $silent ) = @_;
641 2 100       6  
642             my $dup = $self->{duplicates};
643 1         6 return if $dup eq 'allow';
644              
645             $logger->trace("check_duplicates called");
646             my %h;
647             my @issues;
648 1         3 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         4 $h{$v}++;
654             if ( $h{$v} > 1 ) {
655 2 50       21 $logger->debug("got duplicates $i -> $v : $h{$v}");
656 2         6 push @to_delete, $i;
657             push @issues, qq!$i:"$v"!;
658             }
659             }
660 1     1 0 5  
661 1         3 return unless @issues;
662              
663 1 50       17 if ($apply_fix) {
664 1         3 $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 40 $logger->debug("Found forbidden duplicates @issues");
669             push @$error, "Forbidden duplicates value @issues";
670 17         49 }
671 17 50       54 elsif ( $dup eq 'warn' ) {
672             $logger->debug("warning condition: found duplicate @issues");
673 17         50 push @$warn, "Duplicated value: @issues";
674 17         138 $self->add_fixes( scalar @issues);
675             }
676 17         0 elsif ( $dup eq 'suppress' ) {
677 17         53 $logger->debug("suppressing duplicates @issues");
678 29         103 for (reverse @to_delete) { $self->remove($_) }
679 29 100       82 }
680 20 100       64 else {
681 20         40 die "Internal error: duplicates is $dup";
682 20 100       50 }
683 8         45 return;
684 8         66 }
685 8         29  
686             my %args = _resolve_arg_shortcut(\@args, 'index');
687             my $check = $self->_check_check( $args{check} );
688             my $idx = $args{index};
689 17 100       79  
690             $logger->trace( $self->name, " called for idx $idx" ) if $logger->is_trace;
691 5 100       26  
    100          
    100          
    50          
692 1         8 $idx = $self->{convert_sub}($idx)
693 1         7 if ( defined $self->{convert_sub} and defined $idx );
  1         5  
694              
695             # try migration only once
696 1         8 $self->_migrate unless $self->{migration_done};
697 1         9  
698             my $ok = 1;
699              
700 2         12 # check index only if it's unknown
701 2         24 $ok = $self->check_idx( index => $idx, check => $check )
702 2         19 unless $self->_defined($idx)
703             or $check eq 'no';
704              
705 1         7 if ( $ok or $check eq 'no' ) {
706 1         7 # create another method
  2         9  
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         58 }
712             else {
713             Config::Model::Exception::WrongValue->throw(
714 3675     3675 1 52535 error => join( "\n\t", @{ $self->{idx_error_list} } ),
  3675         4666  
  3675         5703  
  3675         4319  
715 3675         8702 object => $self
716 3675         12295 );
717 3675         7233 }
718              
719 3675 100       9279 return;
720             }
721              
722 3675 100 66     23743 my %args = _resolve_arg_shortcut(\@args, 'path');
723             my $path = delete $args{path};
724             my $autoadd = 1;
725 3675 100       8726 $autoadd = $args{autoadd} if defined $args{autoadd};
726             my $get_obj = delete $args{get_obj} || 0;
727 3675         5065 $path =~ s!^/!!;
728             my ( $item, $new_path ) = split m!/!, $path, 2;
729              
730 3675 100 100     10081 my $dcm = $args{dir_char_mockup};
731              
732             # $item =~ s($dcm)(/)g if $dcm ;
733             if ($dcm) {
734 3675 100 66     8979 while (1) {
735             my $i = index( $item, $dcm );
736 3663         8976 last if $i == -1;
737             substr $item, $i, length($dcm), '/';
738 3663 100       6623 }
739 3663         9191 }
740              
741             return unless ( $self->exists($item) or $autoadd );
742              
743 12         20 $logger->trace("get: path $path, item $item");
  12         113  
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 3 my ( $item, $new_path ) = split m!/!, $path, 2;
  3         4  
  3         7  
  3         4  
752 3         6 return $self->fetch_with_id($item)->set( $new_path, @args );
753 3         6 }
754 3         4  
755 3 50       5 my ( $self, $from, $to ) = @_;
756 3   100     8  
757 3         4 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       4 }
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     9 }
772             else {
773 3         20 Config::Model::Exception::WrongValue->throw(
774             error => join( "\n\t", @{ $self->{idx_error_list} } ),
775 3         22 object => $self
776 3 50 66     21 );
      33        
777 3         11 }
778             return;
779             }
780 1     1 1 2  
  1         1  
  1         1  
  1         2  
  1         1  
781 1         2 my $self = shift;
782 1         3 my @keys = $self->fetch_all_indexes;
783 1         2 return map { $self->fetch_with_id($_); } @keys;
784             }
785              
786             return join(',', $self->fetch_all_values(@args) );
787 7     7 1 31 }
788              
789 7         25 my %args = _resolve_arg_shortcut(\@args, 'idx');
790 7         41 return $self->_fetch_value(%args, sub => 'fetch');
791             }
792 7 100 66     54  
    50          
793 4         19 my %args = _resolve_arg_shortcut(\@args, 'idx');
794 4         37 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         11  
800 3         29 if ( $self->{cargo}{type} eq 'leaf' ) {
801 3         39 return $self->fetch_with_id($args{idx})->$sub( check => $check, mode => $args{mode} );
802 3         31 }
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 72  
815 46         101 my %args = _resolve_arg_shortcut(\@args, 'mode');
816 46         91 my $mode = $args{mode};
  65         103  
817             my $check = $self->_check_check( $args{check} );
818              
819 1     1 1 1964 my @keys = $self->fetch_all_indexes;
  1         2  
  1         2  
  1         2  
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 872  
  758         946  
  758         1432  
  758         930  
824 758         1916 if ( $ok or $check eq 'no' ) {
825 758         2277 return grep { defined $_ }
826             map { $self->fetch_value(idx => $_, check => $check, mode => $mode ); } @keys;
827             }
828 14     14 1 19 else {
  14         25  
  14         19  
  14         30  
829 14         38 Config::Model::Exception::WrongValue->throw(
830 14         52 error => join( "\n\t", @{ $self->{content_error_list} } ),
831             object => $self
832             );
833 772     772   1030 }
  772         973  
  772         1546  
  772         902  
834 772         2069 return;
835 772         1469 }
836              
837 772 50       1804 my $self = shift;
838 772         1791 $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 5296 my $self = shift;
  414         578  
  414         723  
  414         524  
853 414         1096 return $self->fetch_size ;
854 414         903 }
855 414         1186  
856             # auto vivify must create according to cargo}{type
857 414         977 # node -> Node or user class
858             # leaf -> Value or user class
859              
860 414 100       1429 # warped node cannot be used. Same effect can be achieved by warping
861             # cargo_args
862 414 100 66     1154  
863 758         2340 my %element_default_class = (
864 412         1077 warped_node => 'WarpedNode',
  758         1750  
865             node => 'Node',
866             leaf => 'Value',
867             );
868 2         4  
  2         12  
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 8259 my %cargo_args = %{ $self->cargo };
877 2238         6962 my $class = delete $cargo_args{class}; # to override class in cargo
878 2238         6162  
879 2238         4912 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 6 ) unless $can_override_class{$cargo_type};
895 2         7 $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 3015  
919 923         1332 my $imode = $self->instance->get_data_mode;
  923         6483  
920 923         2045 $self->set_data_mode( $idx, $imode );
921              
922 923         1860 $self->_store( $idx, $item );
923             return;
924             }
925              
926             ## no critic (Subroutines::ProhibitBuiltinHomonyms)
927             my ( $self, $idx ) = @_;
928              
929 923 50       3250 return $self->_defined($idx);
930             }
931 923         2239  
932             ## no critic (Subroutines::ProhibitBuiltinHomonyms)
933 923 100       1944 my ( $self, $idx ) = @_;
934              
935             return $self->_exists($idx);
936             }
937 36 50       116  
938 36         60 ## 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         4677 }
947              
948             my ($self) = @_;
949              
950             $self->{warning_hash} = {};
951 923         1494 $self->_clear;
952             $self->clear_data_mode;
953             $self->notify_change( note => "cleared all entries" );
954 923 100       2100 return;
955 283         1082 }
956              
957             my ($self) = @_;
958 640         2533 carp "clear_values deprecated";
959 640         11993  
960             my $ct = $self->get_cargo_type;
961             Config::Model::Exception::User->throw(
962 923         5576 object => $self,
963 923         3782 message => "clear_values() called on non leaf cargo type: '$ct'"
964             ) if $ct ne 'leaf';
965 923         46391  
966 923         2554 # 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 114 }
979             elsif ( @{ $self->{content_warning_list} } ) {
980 43         146 $list = $self->{content_warning_list} ;
981             }
982             return $list ? join( "\n", @$list ) : '';
983             }
984              
985 53     53 1 2659 my $self = shift;
986              
987 53         168 return @{ $self->{content_warning_list} };
988 53         174 }
989              
990 53 100       304 my $self = shift;
991 53         876 my @list;
992             for (qw/idx_error_list content_error_list/) {
993             push @list, @{ $self->{$_} } if $self->{$_};
994             }
995 18     18 1 2256  
996             return unless @list;
997 18         99 return wantarray ? @list : join( "\n\t", @list );
998 18         90 }
999 18         128  
1000 18         293 __PACKAGE__->meta->make_immutable;
1001 18         69  
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.152
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 1061 $model->create_config_class(
1036             name => "MyClass",
1037 59         94 element => [
  59         212  
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