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