File: Instance.pm

package info (click to toggle)
libconfig-model-perl 2.155-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,172 kB
  • sloc: perl: 15,117; makefile: 19
file content (1080 lines) | stat: -rw-r--r-- 26,754 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
package Config::Model::Instance;

#use Scalar::Util qw(weaken) ;
use strict;

use 5.10.1;
use Mouse;
use Mouse::Util::TypeConstraints;
use MouseX::StrictConstructor;
with "Config::Model::Role::NodeLoader";

use File::Path;
use Path::Tiny;
use Log::Log4perl qw(get_logger :levels);

use Config::Model::TypeConstraints;
use Config::Model::Exception;
use Config::Model::Node;
use Config::Model::Loader;
use Config::Model::SearchElement;
use Config::Model::Iterator;
use Config::Model::ObjTreeScanner;

use warnings ;

use Carp qw/carp croak confess cluck/;

my $logger        = get_logger("Instance");
my $change_logger = get_logger("Anything::Change");
my $user_logger = get_logger("User");

has [qw/root_class_name/] => ( is => 'ro', isa => 'Str', required => 1 );

sub location { return "in instance" }

has config_model => (
    is       => 'ro',
    isa      => 'Config::Model',
    weak_ref => 1,
    required => 1
);

has check => (
    is      => 'ro',
    isa     => 'Str',
    default => 'yes',
    reader  => 'read_check',
);

# used by cme -create option
has auto_create => (
    is      => 'ro',
    isa     => 'Bool',
    default => 0,
);

# a unique (instance wise) placeholder for various tree objects
# to store information
has _safe => (
    is      => 'rw',
    isa     => 'HashRef',
    traits  => ['Hash'],
    default => sub { {} },
    handles => {
        data => 'accessor',
    },
);

has appli_info => (
    is      => 'rw',
    isa     => 'HashRef',
    traits  => ['Hash'],
    default => sub { {} },
    handles => {
        get_appli_info => 'get',
        # currying See Moose::Manual::Delegation
        get_support_info => [qw/get support_info/],
    },
);


# preset mode:  to load values found by HW scan or other automatic scheme
# layered mode: to load values found in included files (e.g. a la multistrap)
# canonical mode: write config data back using model order instead of user order
has [qw/preset layered canonical/] => (
    is      => 'ro',
    isa     => 'Bool',
    default => 0,
);

has changes => (
    is      => 'ro',
    isa     => 'ArrayRef',
    traits  => ['Array'],
    default => sub { [] },
    handles => {
        add_change => 'push',
        c_count    => 'count',
        has_changes => 'count',

        #needs_save => 'count' ,
        clear_changes => 'clear',
    } );

sub needs_save {
    my $self = shift;
    my $arg  = shift;
    if ( defined $arg ) {
        if ($arg) {
            croak "replace needs_save(1) call with add_change";
            $self->add_change();    # may not work
        }
        else {
            croak "replace needs_save(0) call with clear_changes";
            $self->clear_changes;
        }
    }
    return $self->c_count;
}

has errors => (
    is      => 'ro',
    isa     => 'HashRef',
    traits  => ['Hash'],
    default => sub { {} },
    handles => {
        _set_error   => 'set',
        cancel_error => 'delete',
        has_error    => 'count',
        clear_errors => 'clear',
        error_paths  => 'keys'
    } );

sub add_error {
    my $self = shift;
    $self->_set_error( shift, '' );
}

sub error_messages {
    my $self = shift;
    my @errs = map { "$_: " . $self->config_root->grab($_)->error_msg } $self->error_paths;
    return wantarray ? @errs : join( "\n", @errs );
}

sub has_warning {
    my $self = shift;

    my $count_leaf_warnings = sub {
        my ( $scanner, $data_ref, $node, $element_name, $index, $leaf_object ) = @_;
        $$data_ref += $leaf_object->has_warning;
    };

    my $count_list_warnings = sub {
        my ( $scanner, $data_ref, $node, $element_name, $index, $leaf_object ) = @_;
        $$data_ref += $node->fetch_element($element_name)->has_warning;
    };

    my $scan = Config::Model::ObjTreeScanner->new(
        leaf_cb           => $count_leaf_warnings,
        list_element_hook => $count_list_warnings,
        hash_element_hook => $count_list_warnings,
    );

    my $result = 0;
    $scan->scan_node( \$result, $self->config_root );

    return $result;
}

has on_change_cb => (
    is  => 'rw',
    traits    => ['Code'],
    isa       => 'CodeRef',
    default   => sub { sub { } },
);

has on_message_cb => (
    traits    => ['Code'],
    is        => 'rw',
    isa       => 'CodeRef',
    default   => sub { sub { say @_; } },
    handles   => {
        show_message => 'execute',
    },
);

# initial_load mode: when data is loaded the first time
has initial_load => (
    is      => 'rw',
    isa     => 'Bool',
    default => 0,
    trigger => \&_trace_initial_load,
    traits  => [qw/Bool/],
    handles => {
        initial_load_start => 'set',
        initial_load_stop  => 'unset',
    } );

sub _trace_initial_load {
    my ( $self, $n, $o ) = @_;
    $logger->debug("switched to $n");
}

# This array holds a set of sub ref that will be invoked when
# the user requires to write all configuration tree in their
# backend storage.
has _write_back => (
    is      => 'ro',
    isa     => 'HashRef',
    traits  => ['Hash'],
    handles => {
        count_write_back     => 'count',    # mostly for tests
        has_no_write_back    => 'is_empty',
        nodes_to_write_back  => 'keys',
        write_back_node_info => 'get',
        delete_write_back    => 'delete',
        clear_write_back     => 'clear',
    },
    default => sub { {} },
);

sub register_write_back {
    my ($self, $path, $backend, $wb) = @_;
    push @{ $self->_write_back->{$path} //= [] }, [$backend, $wb];
}

# used for auto_read auto_write feature
has [qw/name application backend_arg backup/] => (
    is  => 'ro',
    isa => 'Maybe[Str]',
);

has 'root_dir' => (
    is => 'ro',
    isa => 'Config::Model::TypeContraints::Path',
    coerce => 1
);

has root_path => (
    is  => 'ro',
    isa => 'Path::Tiny',
    lazy_build => 1,
);

sub _build_root_path {
    my $self = shift;
    my $root_dir = $self->root_dir // '';
    return $root_dir ? path($root_dir) : Path::Tiny->cwd;
}

has [qw/config_dir config_file/] => (
    is  => 'ro',
    isa => 'Config::Model::TypeContraints::Path',
    coerce => 1
);

has tree => (
    is      => 'ro',
    isa     => 'Config::Model::Node',
    builder => '_build_tree',
    lazy    => 1,
    clearer => '_clear_config',
    reader  => 'config_root',
    handles => [qw/apply_fixes deep_check grab grab_value/],
);

sub reset_config {
    my $self = shift;
    $self->_clear_config;
    $self->clear_changes;
    return $self->config_root;
}

sub _build_tree {
    my $self = shift;

    return $self->load_node (
        config_class_name => $self->{root_class_name},
        instance          => $self,
        container         => $self,
        config_file       => $self->{config_file},
    );
}

sub preset_start {
    my $self = shift;
    $logger->info("Starting preset mode");
    carp "Cannot start preset mode during layered mode"
        if $self->{layered};
    $self->{preset} = 1;
}

sub preset_stop {
    my $self = shift;
    $logger->info("Stopping preset mode");
    $self->{preset} = 0;
}

sub preset_clear {
    my $self = shift;

    my $leaf_cb = sub {
        my ( $scanner, $data_ref, $node, $element_name, $index, $leaf_object ) = @_;
        $$data_ref ||= $leaf_object->clear_preset;
    };

    $self->_stuff_clear($leaf_cb);
}

sub layered_start {
    my $self = shift;
    $logger->info("Starting layered mode");
    carp "Cannot start layered mode during preset mode"
        if $self->{preset};
    $self->{layered} = 1;
}

sub layered_stop {
    my $self = shift;
    $logger->info("Stopping layered mode");
    $self->{layered} = 0;
}

sub layered_clear {
    my $self = shift;

    my $leaf_cb = sub {
        my ( $scanner, $data_ref, $node, $element_name, $index, $leaf_object ) = @_;
        $$data_ref ||= $leaf_object->clear_layered;
    };

    $self->_stuff_clear($leaf_cb);
}

sub get_data_mode {
    my $self = shift;
    return
          $self->{layered} ? 'layered'
        : $self->{preset}  ? 'preset'
        :                    'normal';
}

sub _stuff_clear {
    my ( $self, $leaf_cb ) = @_;

    # this sub may remove hash keys that were entered by user if the
    # corresponding hash value has no data.
    # it also clear auto_created ids if there's no data in there
    my $h_cb = sub {
        my ( $scanner, $data_ref, $node, $element_name, @keys ) = @_;
        my $obj = $node->fetch_element($element_name);

        # Since remove method uses splice(array) on list elements, the
        # removal must be done in reverse order to avoid messing up
        # the indexes of the array (i.e. the last indexes becomes
        # greater than the length of the array).
        foreach my $k (reverse @keys) {
            my $has_data = 0;
            $scanner->scan_hash( \$has_data, $node, $element_name, $k );
            $obj->remove($k) unless $has_data;
            $$data_ref ||= $has_data;
        }
    };

    my $wiper = Config::Model::ObjTreeScanner->new(
        fallback        => 'all',
        auto_vivify     => 0,
        check           => 'skip',
        leaf_cb         => $leaf_cb,
        hash_element_cb => $h_cb,
        list_element_cb => $h_cb,
    );

    $wiper->scan_node( undef, $self->config_root );

}

sub modify {
    my $self = shift ;
    my %args   = @_ eq 1 ? ( step => $_[0] ) : @_;
    my $force = delete $args{force_save} || delete $args{force};
    my $quiet = delete $args{quiet};
    $self->load(%args);
    $self->say_changes() unless $quiet;
    $self->write_back( force => $force );
    return $self;
}

sub load {
    my $self   = shift;
    my $loader = Config::Model::Loader->new( start_node => $self->config_root );
    my %args   = @_ eq 1 ? ( step => $_[0] ) : @_;
    $loader->load( %args );
    return $self;
}

sub search_element {
    my $self = shift;
    $self->config_root->search_element(@_);
}

sub wizard_helper {
    carp __PACKAGE__, "::wizard_helper helped is deprecated. Call iterator instead";
    goto &iterator;
}

sub iterator {
    my $self = shift;
    my @args = @_;

    my $tree_root = $self->config_root;

    return Config::Model::Iterator->new( root => $tree_root, @args );
}

sub read_directory {
    carp "read_directory is deprecated";
    return shift->root_dir;
}

sub write_directory {
    my $self = shift;
    carp "write_directory is deprecated";
    return $self->root_dir;
}

sub write_root_dir {
    my $self = shift;
    carp "deprecated";
    return $self->root_dir;
}

# FIXME: record changes to implement undo/redo ?
sub notify_change {
    my $self = shift;
    my %args = @_;
    if ( $change_logger->is_debug ) {
        $change_logger->debug( "in instance ", $self->name, ' for path ', $args{path} );
    }

    foreach my $obsolete (qw/note_only msg/) {
        if ( my $m = delete $args{$obsolete} ) {
            carp "notify_change: param $obsolete is obsolete ($m)";
            $args{note} //='';
            $args{note} .= $m;
        }
    }

    $self->add_change( \%args );
    $self->on_change_cb->( %args );
}

sub _truncate {
    my @lines = @_;
    foreach my $l (@lines) {
        next unless defined $l;
        $l =~ s/\n/ /g;
        substr ($l, 60) = '[...]' if length $l > 60; # limit string length
    }
    return @lines;
}

sub list_changes {
    my $self = shift;
    my $l    = $self->changes;
    my @all;

    foreach my $c (@$l) {
        my $path = $c->{path} ;

        my $vt = $c->{value_type} || '';
        my ( $o, $n ) = _truncate( $c->{old}, $c->{new} );

        my $note = $c->{note} ? " # $c->{note}" : '';

        if ( defined $n and not defined $o ) {
            push @all, "$path has new value: '$n'$note";
        }
        elsif ( not defined $n and defined $o) {
            push @all, "$path deleted value: '$o'$note";
        }
        elsif ( defined $o and defined $n ) {
            push @all, "$path: '$o' -> '$n'$note";
        }
        elsif ( defined $c->{note} ) {
            push @all, "$path: ".$c->{note};
        }
        else {
            # something's unexpected with the call to notify_change
            push @all, "changed ".join(' ', each %$c);
        }
    }

    return wantarray ? @all : join( "\n", @all );
}

sub say_changes {
    my $self    = shift;
    my @changes = $self->list_changes;
    return $self unless @changes;

    my $msg =  "\n" .
        join( "\n- ", "Changes applied to " . ($self->application // $self->name) . " configuration:", @changes ) .
        "\n";

    $user_logger->info($msg);
    return $self;
}

sub write_back {
    my $self = shift;
    my %args =
          scalar @_ > 1 ? @_
        : scalar @_ == 1 ? ( config_dir => $_[0] )
        :                  ();

    my $force_write   = delete $args{force}   || 0;

    if (delete $args{root}) {
        say "write_back: root argument is no longer supported";
    }

    # make sure that root node is loaded
    $self->config_root->init;

    if ($force_write) {
        # make sure that the whole tree is loaded
        my $dump = $self->config_root->dump_tree;
    }

    foreach my $k ( keys %args ) {
        if ($k eq 'config_dir') {
            $args{$k} ||= '';
            $args{$k} .= '/' if $args{$k} and $args{$k} !~ m(/$);
        }
        elsif ( $k ne 'config_file' ) {
            croak "write_back: wrong parameters $k";
        }
    }

    if ($self->has_no_write_back ) {
        my $info = $self->application ? "the model of application ".$self->application
            : "model ".$self->root_class_name ;
        croak "Don't know how to save data of $self->{name} instance. ",
            "Either $info has no configured ",
            "read/write backend or no node containing a backend was loaded. ",
            "Try with -force option or add read/write backend to $info\n";
    }

    foreach my $path ( sort $self->nodes_to_write_back ) {
        $logger->info("write_back called on node $path");

        if ( $path and $self->{config_file} ) {
            $logger->warn("write_back: cannot override config_file in non root node ($path)");
            delete  $self->{config_file}
        }

        $self->_write_back_node(%args, path => $path, force_write => $force_write) ;
    }
    $self->clear_changes;
}

sub _write_back_node {
    my $self = shift;
    my %args = @_;

    my $path = delete $args{path};
    my $force_write   = delete $args{force_write};

    my $node = $self->config_root->grab(
        step => $path,
        type => 'node',
        mode => 'loose',
        autoadd => 0,
    );

    foreach my $wb_info (@{ $self->write_back_node_info($path) }) {
        my ($backend, $cb) = @$wb_info;

        my @wb_args = (
            %args,
            config_file   => $self->{config_file},
            force         => $force_write,
            backup        => $self->backup,
        );

        if (defined $node and ($node->needs_save or $force_write)) {
            my $dir = $args{config_dir};
            mkpath( $dir, 0, oct(755) ) if $dir and not -d $dir;

            # exit when write is successfull
            my $res = $cb->(@wb_args);
            $logger->info( "write_back called with $backend backend, result is ",
                           defined $res ? $res : '<undef>' );
        }

        if (not defined $node) {
            $logger->debug("deleting file for deleted node $path");
            $cb->(@wb_args, force_delete => 1);
            $self->delete_write_back($path);
        }
    }

    $logger->trace( "write_back on node '$path' done" );
}

sub save {
    goto &write_back;
}

sub update {
    my ($self, %args) = @_;

    my @msgs ;
    my $hook = sub {
        my ($scanner, $data_ref,$node,@element_list) = @_;
        if ($node->can('update')) {
            my $loc = $node->location;
            say "Calling update on node '$loc'" if $loc and not $args{quiet};
            push (@msgs, $node->update(%args))
        } ;
    };

    my $root = $self->config_root ;

    Config::Model::ObjTreeScanner->new(
        node_content_hook => $hook,
        check => ($args{quiet} ? 'no' : 'yes'),
        leaf_cb => sub { }
    )->scan_node( \@msgs, $root );

    return @msgs;
}

sub DEMOLISH {
    my $self = shift;
    $self->clear_write_back; # avoid reference loops
}

__PACKAGE__->meta->make_immutable;

1;

# ABSTRACT: Instance of configuration tree

__END__

=head1 SYNOPSIS

 use Config::Model;
 use File::Path ;

 # setup a dummy popcon conf file
 my $wr_dir = '/tmp/etc/';
 my $conf_file = "$wr_dir/popularity-contest.conf" ;

 unless (-d $wr_dir) {
     mkpath($wr_dir, { mode => 0755 }) 
       || die "can't mkpath $wr_dir: $!";
 }
 open(my $conf,"> $conf_file" ) || die "can't open $conf_file: $!";
 $conf->print( qq!MY_HOSTID="aaaaaaaaaaaaaaaaaaaa"\n!,
   qq!PARTICIPATE="yes"\n!,
   qq!USEHTTP="yes" # always http\n!,
   qq!DAY="6"\n!);
 $conf->close ;

 my $model = Config::Model->new;

 # PopCon model is provided. Create a new Config::Model::Instance object
 my $inst = $model->instance (root_class_name   => 'PopCon',
                              root_dir          => '/tmp',
                             );
 my $root = $inst -> config_root ;

 print $root->describe;



=head1 DESCRIPTION

This module provides an object that holds a configuration tree.

=head1 CONSTRUCTOR

An instance object is created by calling L<instance
method|Config::Model/"Configuration instance"> on an existing
model. This model can be specified by its application name:

 my $inst = $model->instance (
   # run 'cme list' to get list of applications
   application => 'foo',
   # optional
   instance_name => 'test1'
 );

 my $inst = $model->instance (
   root_class_name => 'SomeRootClass',
   instance_name => 'test1'
 );

The directory (or directories) holding configuration files is
specified within the configuration model. For test purpose you can
change the "root" directory with C<root_dir> parameter.

Constructor parameters are:

=over

=item root_dir

Pseudo root directory where to read I<and> write configuration
files (L<Path::Tiny> object or string). Configuration directory
specified in model or with C<config_dir> option is appended to this
root directory

=item root_path

L<Path::Tiny> object created with C<root_dir> value or with current
directory if C<root_dir> is empty.

=item config_dir

Directory to read or write configuration file. This parameter must be
supplied if not provided by the configuration model. (string)

=item backend_arg

Specify a backend argument that may be retrieved by some
backend. Instance is used as a relay and does not use this data.

=item check

Specify whether to check value while reading config files. Either:

=over

=item yes

Check value and throws an error for bad values.

=item skip

Check value and skip bad value.

=item no

Do not check.

=back

=item canonical

When true: write config data back using model order. By default, write
items back using the order found in the configuration file. This
feature is experimental and not supported by all backends.

=item on_change_cb

Call back this function whenever C<notify_change> is called. Called with
arguments: C<< name => <root node element name>, index => <index_value> >>

=item on_message_cb

Call back this function when L<show_message> is called. By default,
messages are displayed on STDOUT.

=item error_paths

Returns a list of tree items that currently have an error.

=item error_messages

Returns a list of error messages from the tree content.

=back

Note that the root directory specified within the configuration model
is overridden by C<root_dir> parameter.

If you need to load configuration data that are not correct, you can
use C<< force_load => 1 >>. Then, wrong data are discarded (equivalent to
C<< check => 'no' >> ).

=head1 METHODS

=head2 Manage configuration data

=head2 modify

Calls L</"load"> and then L</save>.

Takes the same parameter as C<load> plus:

=over

=item C<force_write>

Force saving configuration file even if no value was modified
(default is 0)

=item C<quiet>

Do no display the changes brought by the modification steps

=back

=head2 load

Load configuration tree with configuration data. See
L<Config::Model::Loader/"load"> for parameters.
Returns <$self>.

=head2 save

Save the content of the configuration tree to
configuration files. (See L</write_back> for more details)

Use C<< force => 1 >> option to force saving configuration data.

=head2 config_root

Returns the L<root object|Config::Model::Node> of the configuration tree.

=head2 apply_fixes

Scan the tree and apply fixes that are attached to warning specifications. 
See C<warn_if_match> or C<warn_unless_match> in L<Config::Model::Value/>.

=head2 deep_check

Scan the tree and deep check on all elements that support this. Currently only hash or
list element have this feature.

=head2 needs_save

Returns 1 (or more) if the instance contains data that needs to be
saved. I.e some change were done in the tree that needs to be saved.

=head2 has_changes

Returns true if the instance contains unsasved changes.

=head2 list_changes

In list context, returns a array ref of strings describing the changes.
In scalar context, returns a big string. Useful to print.

=head2 say_changes

Print all changes on STDOUT and return C<$self>.

=head2 clear_changes

Clear list of changes. Note that changes pending in the configuration
tree is not affected. This clears only the list shown to user. Use
only for tests.

=head2 has_warning

Returns the number of warning found in the elements of this configuration instance.

=head2 update

Parameters: C<< ( quiet => (0|1), %args ) >>

Try to run update command on all nodes of the configuration tree. Node
without C<update> method are ignored. C<update> prints a message
otherwise (unless C<quiet> is true).

=head2 grab

Use the steps parameter to retrieve and returns an object from the
configuration tree.  Forwarded to L<Config::Model::Role::Grab/grab>

=head2 grab_value

Use the steps parameter to retrieve and returns the value of a leaf
object from the configuration tree.  Forwarded to
L<Config::Model::Role::Grab/grab_value>

=head2 searcher

Returns an object dedicated to search an element in the configuration
model.

This method returns a L<Config::Model::Searcher> object. See
L<Config::Model::Searcher> for details on how to handle a search.

=head2 iterator 

This method returns a L<Config::Model::Iterator> object. See
L<Config::Model::Iterator> for details.

Arguments are explained in  L<Config::Model::Iterator>
L<constructor arguments|Config::Model::Iterator/"Creating an iterator">.

=head2 application

Returns the application name of the instance. (E.g C<popcon>, C<dpkg> ...)

=head2 wizard_helper

Deprecated. Call L</iterator> instead.

=head1 Internal methods

=head2 name

Returns the instance name.

=head2 read_check

Returns which kind of check is performed while reading configuration
files. (see C<check> parameter in L</CONSTRUCTOR> section)

=head2 show_message

Parameters: C<( string )>

Display the message on STDOUT unless a custom function was passed to
C<on_message_cb> parameter.

=head2 reset_config

Destroy current configuration tree (with data) and returns a new tree with
data (and annotations) loaded from disk.

=head2 config_model

Returns the model (L<Config::Model> object) of the configuration tree.

=head2 annotation_saver

Returns the object loading and saving annotations. See
L<Config::Model::Annotation> for details.

=head2 preset_start

All values stored in preset mode are shown to the user as default
values. This feature is useful to enter configuration data entered by
an automatic process (like hardware scan)

=head2 preset_stop

Stop preset mode

=head2 preset

Get preset mode

=head2 preset_clear

Clear all preset values stored.

=head2 layered_start

All values stored in layered mode are shown to the user as default
values. This feature is useful to enter configuration data entered by
an automatic process (like hardware scan)

=head2 layered_stop

Stop layered mode

=head2 layered

Get layered mode

=head2 layered_clear

Clear all layered values stored.

=head2 get_data_mode

Returns 'normal' or 'preset' or 'layered'. Does not take into account
initial_load.

=head2 initial_load_start

Start initial_load mode. This mode tracks the first modifications of
the tree done with data read from the configuration file.

Instance is built with initial_load as 1. Read backend clears this
value once the first read is done.

Other modifications, when initial_load is zero, are assumed to be user
modifications.

=head2 initial_load_stop

Stop initial_load mode. Instance is built with initial_load as 1. Read backend
clears this value once the first read is done.

=head2 initial_load

Get initial_load mode

=head2 data

This method provides a way to store some arbitrary data in the
instance object.

E.g:

  $instance->data(foo => 'bar');

Later:

  my $foo = $instance->data('foo'); # $foo contains 'bar'

=head1 Read and write backend features

Usually, a program based on config model must first create the
configuration model, then load all configuration data. 

This feature enables you to declare with the model a way to load
configuration data (and to write it back). See
L<Config::Model::BackendMgr> for details.

=head2 backend_arg

Get L<cme> command line argument that may be used by the backend to
get the configuration file. These method is typically used in the read
and write method of a backend to know where is the configuration file
to edit.

=head2 root_dir

Returns a L<Path::Tiny> object for the root directory where
configuration data is read from or written to.

=head2 root_path

Same as C<root_dir>

=head2 register_write_back

Parameters: C<( node_location )>

Register a node path that is called back with
C<write_back> method.

=head2 notify_change

Notify that some data has changed in the tree. See
L<Config::Model::AnyThing/notify_change> for more details.

=head2 write_back

In summary, save the content of the configuration tree to
configuration files.

In more details, C<write_back> tries to run all subroutines registered
with C<register_write_back> to write the configuration information.
(See L<Config::Model::BackendMgr> for details).

You can specify here another config directory to write configuration
data back with C<config_dir> parameter. This overrides the model
specifications.

C<write_back> croaks if no write call-back are known.

Use C<< force => 1 >> option to force saving configuration data. This
is useful to write back a file even no change are done at semantic
level, i.e. to reformat a file or remove unnecessary data.

=head1 AUTHOR

Dominique Dumont, (ddumont at cpan dot org)

=head1 SEE ALSO

L<Config::Model>, 
L<Config::Model::Node>, 
L<Config::Model::Loader>,
L<Config::Model::Searcher>,
L<Config::Model::Value>,

=cut