This is an automated email from the git hooks/post-receive script. dod pushed a commit to annotated tag upstream/2.095 in repository libconfig-model-perl.
commit db7a25a3252eb67226f2e0ff7323b738bf5cf1f3 Author: Dominique Dumont <d...@debian.org> Date: Tue Dec 6 19:00:57 2016 +0100 New upstream version 2.095 --- Build.PL | 5 +- Changes | 16 + MANIFEST | 4 + META.json | 13 +- META.yml | 9 +- README.md | 6 + lib/Config/Model.pm | 6 +- lib/Config/Model/Annotation.pm | 16 +- lib/Config/Model/AnyId.pm | 18 +- lib/Config/Model/AnyThing.pm | 457 +------------------ lib/Config/Model/Backend/Any.pm | 4 +- lib/Config/Model/Backend/Fstab.pm | 4 +- lib/Config/Model/Backend/IniFile.pm | 4 +- lib/Config/Model/Backend/Json.pm | 4 +- lib/Config/Model/Backend/PlainFile.pm | 4 +- lib/Config/Model/Backend/ShellVar.pm | 4 +- lib/Config/Model/Backend/Yaml.pm | 4 +- lib/Config/Model/BackendMgr.pm | 22 +- lib/Config/Model/CheckList.pm | 8 +- lib/Config/Model/Cookbook/CreateModelFromDoc.pod | 2 +- lib/Config/Model/Describe.pm | 4 +- lib/Config/Model/DumpAsData.pm | 4 +- lib/Config/Model/Dumper.pm | 4 +- lib/Config/Model/Exception.pm | 46 +- lib/Config/Model/FuseUI.pm | 4 +- lib/Config/Model/HashId.pm | 45 +- lib/Config/Model/IdElementReference.pm | 6 +- lib/Config/Model/Instance.pm | 11 +- lib/Config/Model/Iterator.pm | 4 +- lib/Config/Model/ListId.pm | 6 +- lib/Config/Model/Lister.pm | 4 +- lib/Config/Model/Loader.pm | 86 +++- lib/Config/Model/Manual/ModelCreationAdvanced.pod | 2 +- .../Model/Manual/ModelCreationIntroduction.pod | 2 +- lib/Config/Model/Node.pm | 26 +- lib/Config/Model/ObjTreeScanner.pm | 4 +- lib/Config/Model/Report.pm | 8 +- lib/Config/Model/{AnyThing.pm => Role/Grab.pm} | 507 ++------------------- lib/Config/Model/Role/HelpAsText.pm | 97 ++++ lib/Config/Model/Role/NodeLoader.pm | 4 +- lib/Config/Model/Role/WarpMaster.pm | 4 +- lib/Config/Model/SearchElement.pm | 4 +- lib/Config/Model/SimpleUI.pm | 14 +- lib/Config/Model/TermUI.pm | 4 +- lib/Config/Model/TreeSearcher.pm | 6 +- lib/Config/Model/Utils/GenClassPod.pm | 4 +- lib/Config/Model/Value.pm | 6 +- lib/Config/Model/Value/LayeredInclude.pm | 4 +- lib/Config/Model/ValueComputer.pm | 8 +- lib/Config/Model/WarpedNode.pm | 12 +- lib/Config/Model/Warper.pm | 10 +- t/annotation.t | 26 +- t/backend_mgr.t | 106 +++-- t/dump_load_model.pm | 7 + t/hash_id_of_values.t | 10 + t/load.t | 9 + .../multi-ini-examples/max-overflow/etc/bar.conf | 1 + t/model_tests.d/multi-ini-test-conf.pl | 71 +++ 58 files changed, 642 insertions(+), 1148 deletions(-) diff --git a/Build.PL b/Build.PL index afc6bc2..6e95fbe 100644 --- a/Build.PL +++ b/Build.PL @@ -69,6 +69,7 @@ my $build = $class->new( 'build_requires' => { 'Config::Model::Tester' => '2.053', 'Module::Build' => '0.34', + 'Path::Tiny' => '0.070', 'Test::Differences' => '0', 'Test::Exception' => '0', 'Test::File::Contents' => '0', @@ -110,8 +111,10 @@ my $build = $class->new( 'MouseX::StrictConstructor' => '0', 'POSIX' => '0', 'Parse::RecDescent' => 'v1.90.0', - 'Path::Tiny' => '0', + 'Path::Tiny' => '0.070', 'Pod::POM' => '0', + 'Pod::Simple' => '3.23', + 'Pod::Text' => '0', 'Scalar::Util' => '0', 'Storable' => '0', 'Text::Diff' => '0', diff --git a/Changes b/Changes index 16ecf0a..a43c95c 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,19 @@ +2.095 2016-12-06 + + New feature usable by cme: + * loader: add .insort() command for hash element + * Hash element: add insort method + + Term UI improvement + * better format the output of 'desc' command (transform + pod doc to text). This requires Pod::Text and + Pod::Simple 3.23 + + Bug fix: + * track and save annotation changes (gh #12) + * Node: propagate check override in init() (which fixes + loading of a systemd config that contains an error) + 2.094 2016-11-09 Fix compatibility with older Term::ReadLine::Gnu: diff --git a/MANIFEST b/MANIFEST index 29518f4..54dac94 100644 --- a/MANIFEST +++ b/MANIFEST @@ -44,6 +44,8 @@ lib/Config/Model/Manual/ModelCreationIntroduction.pod lib/Config/Model/Node.pm lib/Config/Model/ObjTreeScanner.pm lib/Config/Model/Report.pm +lib/Config/Model/Role/Grab.pm +lib/Config/Model/Role/HelpAsText.pm lib/Config/Model/Role/NodeLoader.pm lib/Config/Model/Role/WarpMaster.pm lib/Config/Model/SearchElement.pm @@ -122,6 +124,8 @@ t/model_tests.d/fstab-test-conf.pl t/model_tests.d/layer-examples/mini/etc/foo-config.pl t/model_tests.d/layer-examples/mini/home/joe/foo/config.pl t/model_tests.d/layer-test-conf.pl +t/model_tests.d/multi-ini-examples/max-overflow/etc/bar.conf +t/model_tests.d/multi-ini-test-conf.pl t/model_tests.d/multistrap-examples/arm/home/foo/my_arm.conf t/model_tests.d/multistrap-examples/arm/usr/share/multistrap/crosschroot.conf t/model_tests.d/multistrap-examples/from_scratch/usr/share/multistrap/crosschroot.conf diff --git a/META.json b/META.json index 1e308ff..00009de 100644 --- a/META.json +++ b/META.json @@ -4,13 +4,13 @@ "Dominique Dumont" ], "dynamic_config" : 0, - "generated_by" : "Dist::Zilla version 6.008, CPAN::Meta::Converter version 2.150010", + "generated_by" : "Dist::Zilla version 6.008, CPAN::Meta::Converter version 2.150005", "license" : [ "lgpl_2_1" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", - "version" : 2 + "version" : "2" }, "name" : "Config-Model", "prereqs" : { @@ -56,8 +56,10 @@ "MouseX::StrictConstructor" : "0", "POSIX" : "0", "Parse::RecDescent" : "v1.90.0", - "Path::Tiny" : "0", + "Path::Tiny" : "0.070", "Pod::POM" : "0", + "Pod::Simple" : "3.23", + "Pod::Text" : "0", "Scalar::Util" : "0", "Storable" : "0", "Text::Diff" : "0", @@ -69,6 +71,7 @@ "test" : { "requires" : { "Config::Model::Tester" : "2.053", + "Path::Tiny" : "0.070", "Test::Differences" : "0", "Test::Exception" : "0", "Test::File::Contents" : "0", @@ -91,7 +94,7 @@ "web" : "http://github.com/dod38fr/config-model" } }, - "version" : "2.094", - "x_serialization_backend" : "Cpanel::JSON::XS version 3.022" + "version" : "2.095", + "x_serialization_backend" : "JSON::XS version 3.03" } diff --git a/META.yml b/META.yml index b785367..ddae9fc 100644 --- a/META.yml +++ b/META.yml @@ -5,6 +5,7 @@ author: build_requires: Config::Model::Tester: '2.053' Module::Build: '0.34' + Path::Tiny: '0.070' Test::Differences: '0' Test::Exception: '0' Test::File::Contents: '0' @@ -15,7 +16,7 @@ build_requires: configure_requires: Module::Build: '0.34' dynamic_config: 0 -generated_by: 'Dist::Zilla version 6.008, CPAN::Meta::Converter version 2.150010' +generated_by: 'Dist::Zilla version 6.008, CPAN::Meta::Converter version 2.150005' license: lgpl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -50,8 +51,10 @@ requires: MouseX::StrictConstructor: '0' POSIX: '0' Parse::RecDescent: v1.90.0 - Path::Tiny: '0' + Path::Tiny: '0.070' Pod::POM: '0' + Pod::Simple: '3.23' + Pod::Text: '0' Scalar::Util: '0' Storable: '0' Text::Diff: '0' @@ -62,5 +65,5 @@ resources: bugtracker: https://github.com/dod38fr/config-model/issues homepage: https://github.com/dod38fr/config-model/wiki repository: git://github.com/dod38fr/config-model.git -version: '2.094' +version: '2.095' x_serialization_backend: 'YAML::Tiny version 1.69' diff --git a/README.md b/README.md index 2f81091..9163497 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +# Config-Model + +Configuration schema on steroids. + +[![](https://travis-ci.org/dod38fr/config-model.svg?branch=master)](https://travis-ci.org/dod38fr/config-model) + # What is Config-Model project [Config::Model](https://metacpan.org/pod/Config::Model) is: diff --git a/lib/Config/Model.pm b/lib/Config/Model.pm index dd68c32..2fb9f36 100644 --- a/lib/Config/Model.pm +++ b/lib/Config/Model.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model; -$Config::Model::VERSION = '2.094'; +$Config::Model::VERSION = '2.095'; use strict ; use warnings; use 5.10.1; @@ -1678,7 +1678,7 @@ Config::Model - Create tools to validate, migrate and edit configuration files =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -2730,7 +2730,7 @@ CPANTS The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution. -L<http://cpants.perl.org/dist/overview/Config-Model> +L<http://cpants.cpanauthors.org/dist/Config-Model> =item * diff --git a/lib/Config/Model/Annotation.pm b/lib/Config/Model/Annotation.pm index 96430c8..47d3f0b 100644 --- a/lib/Config/Model/Annotation.pm +++ b/lib/Config/Model/Annotation.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Annotation; -$Config::Model::Annotation::VERSION = '2.094'; +$Config::Model::Annotation::VERSION = '2.095'; use Mouse; use English; @@ -164,7 +164,7 @@ Config::Model::Annotation - Read and write configuration annotations =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -224,8 +224,12 @@ version 2.094 =head1 DESCRIPTION This module provides an object that read and write annotations (a bit -like comments) to and from a configuration tree and save them in a file (not -configuration file) +like comments) to and from a configuration tree and save them in a +file (not configuration file). This module can be used to save +annotation for configuration files that do not support comments. + +THis module should not be used for configuration files that support +comments. Depending on the effective id of the process, the annotation is saved in: @@ -257,6 +261,10 @@ Save annotations in a file (See L<DESCRIPTION>) Loads annotations from a file (See L<DESCRIPTION>) +=head1 CAVEATS + +This module is currently not used. + =head1 AUTHOR Dominique Dumont, (ddumont at cpan dot org) diff --git a/lib/Config/Model/AnyId.pm b/lib/Config/Model/AnyId.pm index a6e880f..b74845f 100644 --- a/lib/Config/Model/AnyId.pm +++ b/lib/Config/Model/AnyId.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::AnyId; -$Config::Model::AnyId::VERSION = '2.094'; +$Config::Model::AnyId::VERSION = '2.095'; use 5.010; use Mouse; @@ -24,6 +24,11 @@ use Scalar::Util qw/weaken/; extends qw/Config::Model::AnyThing/; +use Mouse::Util::TypeConstraints; + +subtype 'KeyArray' => as 'ArrayRef' ; +coerce 'KeyArray' => from 'Str' => via { [$_] } ; + my $logger = get_logger("Tree::Element::Id"); my $deep_check_logger = get_logger('DeepCheck'); my $fix_logger = get_logger("Anything::Fix"); @@ -103,7 +108,12 @@ my @common_hash_params = qw/default_with_init/; has \@common_hash_params => ( is => 'ro', isa => 'Maybe[HashRef]' ); my @common_list_params = qw/allow_keys default_keys auto_create_keys/; -has \@common_list_params => ( is => 'ro', isa => 'Maybe[ArrayRef]' ); +has \@common_list_params => ( + is => 'ro', + isa => 'KeyArray', + coerce => 1, + default => sub { []; } +); my @common_str_params = qw/allow_keys_from allow_keys_matching follow_keys_from migrate_keys_from migrate_values_from @@ -1001,7 +1011,7 @@ Config::Model::AnyId - Base class for hash or list element =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -1163,7 +1173,7 @@ be used with string index type) When set, the default parameter (or set of parameters) are used as default keys hashes and created automatically when the C<keys> or C<exists> -functions are used on an I<empty> hash.. +functions are used on an I<empty> hash. You can use C<< default_keys => 'foo' >>, or C<< default_keys => ['foo', 'bar'] >>. diff --git a/lib/Config/Model/AnyThing.pm b/lib/Config/Model/AnyThing.pm index 1678eb5..e43cbaa 100644 --- a/lib/Config/Model/AnyThing.pm +++ b/lib/Config/Model/AnyThing.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::AnyThing; -$Config::Model::AnyThing::VERSION = '2.094'; +$Config::Model::AnyThing::VERSION = '2.095'; use Mouse; # FIXME: must cleanup warp mechanism to implement this @@ -195,18 +195,22 @@ sub xpath { sub annotation { my $self = shift; - $self->{annotation} = join( "\n", grep ( defined $_, @_ ) ) - if @_ - and not $self->instance->preset - and not $self->instance->layered; + my $old_note = $self->{annotation} || ''; + if (@_ and not $self->instance->preset and not $self->instance->layered) { + my $new = $self->{annotation} = join( "\n", grep ( defined $_, @_ ) ); + $self->notify_change(note => 'updated annotation') unless $new eq $old_note; + } + return $self->{annotation} || ''; } sub clear_annotation { my $self = shift; + $self->notify_change(note => 'deleted annotation') if $self->{annotation}; $self->{annotation} = ''; } +# may be used (but not yet) to load annotation from perl data file sub load_pod_annotation { my $self = shift; my $pod = shift; @@ -230,339 +234,6 @@ sub load_pod_annotation { } } -## Navigation - -# accept commands like -# item:b -> go down a node, create a new node if necessary -# - climbs up -# ! climbs up to the top - -# Now return an object and not a value ! - -sub grab { - my $self = shift; - my ( $steps, $mode, $autoadd, $type, $grab_non_available, $check ) = - ( undef, 'strict', 1, undef, 0, 'yes' ); - - my %args = @_ > 1 ? @_ : ( steps => $_[0] ); - - $steps = delete $args{steps} // delete $args{step}; - $mode = delete $args{mode} if defined $args{mode}; - $autoadd = delete $args{autoadd} if defined $args{autoadd}; - $grab_non_available = delete $args{grab_non_available} - if defined $args{grab_non_available}; - $type = delete $args{type}; # node, leaf or undef - $check = $self->_check_check( delete $args{check} ); - - if ( defined $args{strict} ) { - carp "grab: deprecated parameter 'strict'. Use mode"; - $mode = delete $args{strict} ? 'strict' : 'adaptative'; - } - - Config::Model::Exception::User->throw( - object => $self, - message => "grab: unexpected parameter: " . join( ' ', keys %args ) ) if %args; - - Config::Model::Exception::Internal->throw( - error => "grab: steps parameter must be a string " . "or an array ref" ) - unless ref $steps eq 'ARRAY' || not ref $steps; - - # accept commands, grep remove empty items left by spurious spaces - my $huge_string = ref $steps ? join( ' ', @$steps ) : $steps; - my @command = ( - $huge_string =~ m/ - ( # begin of *one* command - (?: # group parts of a command (e.g ...:... ) - [^\s"]+ # match anything but a space and a quote - (?: # begin quoted group - " # begin of a string - (?: # begin group - \\" # match an escaped quote - | # or - [^"] # anything but a quote - )* # lots of time - " # end of the string - ) # end of quoted group - ? # match if I got more than one group - )+ # can have several parts in one command - ) # end of *one* command - /gx - ); - - my @saved = @command; - - $logger->debug( - "grab: executing '", - join( "' '", @command ), - "' on object '", - $self->name, "'" - ); - - my @found = ($self); - -COMMAND: - while (@command) { - last if $mode eq 'step_by_step' and @saved > @command; - - my $cmd = shift @command; - - my $obj = $found[-1]; - $logger->debug( "grab: executing cmd '$cmd' on object '", $obj->name, "($obj)'" ); - - if ( $cmd eq '!' ) { - push @found, $obj->grab_root(); - next; - } - - if ( $cmd =~ /^!([\w:]*)/ ) { - my $ancestor = $obj->grab_ancestor($1); - if ( defined $ancestor ) { - push @found, $ancestor; - next; - } - else { - Config::Model::Exception::AncestorClass->throw( - object => $obj, - info => "grab called from '" - . $self->name - . "' with steps '@saved' looking for class $1" - ) if $mode eq 'strict'; - return; - } - } - - if ( $cmd =~ /^\?(\w[\w-]*)/ ) { - push @found, $obj->grab_ancestor_with_element_named($1); - $cmd =~ s/^\?//; #remove the go up part - unshift @command, $cmd; - next; - } - - if ( $cmd eq '-' ) { - if ( defined $obj->parent ) { - push @found, $obj->parent; - next; - } - else { - $logger->debug( "grab: ", $obj->name, " has no parent" ); - return $mode eq 'adaptative' ? $obj : undef; - } - } - - unless ( $obj->isa('Config::Model::Node') - or $obj->isa('Config::Model::WarpedNode') ) { - Config::Model::Exception::Model->throw( - object => $obj, - message => "Cannot apply command '$cmd' on leaf item" - . " (full command is '@saved')" - ); - } - - my ( $name, $action, $arg ) = - ( $cmd =~ /(\w[\-\w]*)(?:(:)((?:"[^\"]*")|(?:[\w:\/\.\-\+]+)))?/ ); - - if ( defined $arg and $arg =~ /^"/ and $arg =~ /"$/ ) { - $arg =~ s/^"//; # remove leading quote - $arg =~ s/"$//; # remove trailing quote - } - - { - no warnings "uninitialized"; - $logger->debug("grab: cmd '$cmd' -> name '$name', action '$action', arg '$arg'"); - } - - unless ( $obj->has_element($name) ) { - if ( $mode eq 'step_by_step' ) { - return wantarray ? ( undef, @command ) : undef; - } - elsif ( $mode eq 'loose' ) { - return; - } - elsif ( $mode eq 'adaptative' ) { - last; - } - else { - Config::Model::Exception::UnknownElement->throw( - object => $obj, - element => $name, - function => 'grab', - info => "grab called from '" . $self->name . "' with steps '@saved'" - ); - } - } - - unless ( - $grab_non_available - or $obj->is_element_available( - name => $name, - ) - ) { - if ( $mode eq 'step_by_step' ) { - return wantarray ? ( undef, @command ) : undef; - } - elsif ( $mode eq 'loose' ) { - return; - } - elsif ( $mode eq 'adaptative' ) { - last; - } - else { - Config::Model::Exception::UnavailableElement->throw( - object => $obj, - element => $name, - function => 'grab', - info => "grab called from '" . $self->name . "' with steps '@saved'" - ); - } - } - - my $next_obj = $obj->fetch_element( - name => $name, - check => $check, - accept_hidden => $grab_non_available - ); - - # create list or hash element only if autoadd is true - if ( defined $action - and $autoadd == 0 - and not $next_obj->exists($arg) ) { - return if $mode eq 'loose'; - Config::Model::Exception::UnknownId->throw( - object => $obj->fetch_element($name), - element => $name, - id => $arg, - function => 'grab' - ) unless $mode eq 'adaptative'; - last; - } - - if ( defined $action and not $next_obj->isa('Config::Model::AnyId') ) { - return if $mode eq 'loose'; - Config::Model::Exception::Model->throw( - object => $obj, - message => "Cannot apply command '$cmd' on non hash or non list item" - . " (full command is '@saved'). item is '" - . $next_obj->name . "'" - ); - last; - } - - # action can only be : - $next_obj = $next_obj->fetch_with_id(index => $arg, check => $check) if defined $action; - - push @found, $next_obj; - } - - # check element type - if ( defined $type ) { - my @allowed = ref $type ? @$type : ($type); - while ( @found and not grep {$found[-1]->get_type eq $_} @allowed ) { - Config::Model::Exception::WrongType->throw( - object => $found[-1], - function => 'grab', - got_type => $found[-1]->get_type, - expected_type => $type, - info => "requested with steps '$steps'" - ) if $mode ne 'adaptative'; - pop @found; - } - } - - my $return = $found[-1]; - $logger->debug( "grab: returning object '", $return->name, "($return)'" ); - return wantarray ? ( $return, @command ) : $return; -} - -sub grab_value { - my $self = shift; - my %args = scalar @_ == 1 ? ( steps => $_[0] ) : @_; - - my $obj = $self->grab(%args); - - # Pb: may return a node. add another option to grab ?? - # to get undef value when needed? - - return if ( $args{mode} and $args{mode} eq 'loose' and not defined $obj ); - - Config::Model::Exception::User->throw( - object => $self, - message => "grab_value: cannot get value of non-leaf or check_list " - . "item with '" - . join( "' '", @_ ) - . "'. item is $obj" - ) - unless ref $obj - and ( $obj->isa("Config::Model::Value") - or $obj->isa("Config::Model::CheckList") ); - - my $value = $obj->fetch; - if ( $logger->is_debug ) { - my $str = defined $value ? $value : '<undef>'; - $logger->debug( "grab_value: returning value $str of object '", $obj->name ); - } - return $value; -} - -sub grab_annotation { - my $self = shift; - my @args = scalar @_ == 1 ? ( steps => $_[0] ) : @_; - - my $obj = $self->grab(@args); - - return $obj->annotation; -} - -sub grab_root { - my $self = shift; - return defined $self->parent - ? $self->parent->grab_root - : $self; -} - -sub grab_ancestor { - my $self = shift; - my $class = shift || die "grab_ancestor: missing ancestor class"; - - return $self if $self->get_type eq 'node' and $self->config_class_name eq $class; - - return $self->{parent}->grab_ancestor($class) if defined $self->{parent}; - return; -} - -#internal. Used by grab with '?xxx' steps -sub grab_ancestor_with_element_named { - my ( $self, $search, $type ) = @_; - - my $obj = $self; - - while (1) { - $logger->debug( - "grab_ancestor_with_element_named: executing cmd '?$search' on object " . $obj->name ); - - my $obj_element_name = $obj->element_name; - - if ( $obj->isa('Config::Model::Node') - and $obj->has_element( name => $search, type => $type ) ) { - - # object contains the search element, we need to grab the - # searched object (i.e. the '?foo' part is done - return $obj; - } - elsif ( defined $obj->parent ) { - - # going up - $obj = $obj->parent; - } - else { - # there's no more up to go to... - Config::Model::Exception::Model->throw( - object => $self, - error => "Error: cannot grab '?$search'" . "from " . $self->name - ); - } - } -} - # fallback method for object that don't implement has_data sub has_data { my $self= shift; @@ -655,7 +326,7 @@ Config::Model::AnyThing - Base class for configuration tree item =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -696,12 +367,12 @@ Returns the root node of the configuration tree. =head2 location() Returns the node location in the configuration tree. This location -conforms with the syntax defined by L</grab()> method. +conforms with the syntax defined by L<grab|Config::Model::Role::Grab/grab> method. =head2 location_short() Returns the node location in the configuration tree. This location truncates long -indexes to be readable. It cannot be used by L</grab()> method. +indexes to be readable. It cannot be used by L<grab|Config::Model::Role::Grab/grab> method. =head2 composite_name @@ -758,110 +429,6 @@ Clear the annotation of an element =head1 Information management -=head2 grab(...) - -Grab an object from the configuration tree. - -Parameters are: - -=over - -=item C<steps> (or C<step>) - -A string indicating the steps to follow in the tree to find the -required item. (mandatory) - -=item C<mode> - -When set to C<strict>, C<grab> throws an exception if no object is found -using the passed string. When set to C<adaptative>, the object found last is -returned. For instance, for the steps C<good_step wrong_step>, only -the object held by C<good_step> is returned. When set to C<loose>, grab -returns undef in case of problem. (default is C<strict>) - -=item C<type> - -Either C<node>, C<leaf>, C<hash> or C<list> or an array ref containing these -values. Returns only an object of -requested type. Depending on C<strict> value, C<grab> either -throws an exception or returns the last object found with the requested type. -(optional, default to C<undef>, which means any type of object) - -Examples: - - $root->grep(steps => 'foo:2 bar', type => 'leaf') - $root->grep(steps => 'foo:2 bar', type => ['leaf','check_list']) - -=item C<autoadd> - -When set to 1, C<hash> or C<list> configuration element are created -when requested by the passed steps. (default is 1). - -=item grab_non_available - -When set to 1, grab returns an object even if this one is not -available. I.e. even if this element was warped out. (default is 0). - -=back - -The C<steps> parameters is made of the following items separated by -spaces: - -=over 8 - -=item - - -Go up one node - -=item ! - -Go to the root node. - -=item !Foo - -Go up the configuration tree until the C<Foo> configuration class is found. Raise an exception if -no C<Foo> class is found when root node is reached. - -=item xxx - -Go down using C<xxx> element. - -=item xxx:yy - -Go down using C<xxx> element and id C<yy> (valid for hash or list elements) - -=item ?xxx - -Go up the tree until a node containing element C<xxx> is found. Then go down -the tree like item C<xxx>. - -If C<?xxx:yy>, go up the tree the same way. But no check is done to -see if id C<yy> actually exists or not. Only the element C<xxx> is -considered when going up the tree. - -=back - -=head2 grab_value(...) - -Like L</grab(...)>, but returns the value of a leaf or check_list object, not -just the leaf object. - -C<grab_value> raises an exception if following the steps ends on anything but a -leaf or a check_list. - -=head2 grab_annotation(...) - -Like L</grab(...)>, but returns the annotation of an object. - -=head2 grab_root() - -Returns the root of the configuration tree. - -=head2 grab_ancestor( Foo ) - -Go up the configuration tree until the C<Foo> configuration class is found. Returns -the found node or undef. - =head2 notify_change(...) Notify the instance of semantic changes. Parameters are: diff --git a/lib/Config/Model/Backend/Any.pm b/lib/Config/Model/Backend/Any.pm index c43f5dd..eea9fc1 100644 --- a/lib/Config/Model/Backend/Any.pm +++ b/lib/Config/Model/Backend/Any.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Backend::Any; -$Config::Model::Backend::Any::VERSION = '2.094'; +$Config::Model::Backend::Any::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -185,7 +185,7 @@ Config::Model::Backend::Any - Virtual class for other backends =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Backend/Fstab.pm b/lib/Config/Model/Backend/Fstab.pm index 9ceb2ec..494dd6e 100644 --- a/lib/Config/Model/Backend/Fstab.pm +++ b/lib/Config/Model/Backend/Fstab.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Backend::Fstab; -$Config::Model::Backend::Fstab::VERSION = '2.094'; +$Config::Model::Backend::Fstab::VERSION = '2.095'; use Mouse; use Carp; use Log::Log4perl qw(get_logger :levels); @@ -164,7 +164,7 @@ Config::Model::Backend::Fstab - Read and write config from fstab file =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Backend/IniFile.pm b/lib/Config/Model/Backend/IniFile.pm index 431caa0..cb6b2f7 100644 --- a/lib/Config/Model/Backend/IniFile.pm +++ b/lib/Config/Model/Backend/IniFile.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Backend::IniFile; -$Config::Model::Backend::IniFile::VERSION = '2.094'; +$Config::Model::Backend::IniFile::VERSION = '2.095'; use Carp; use Mouse; use 5.10.0; @@ -408,7 +408,7 @@ Config::Model::Backend::IniFile - Read and write config as a INI file =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Backend/Json.pm b/lib/Config/Model/Backend/Json.pm index 535e285..cd8b421 100644 --- a/lib/Config/Model/Backend/Json.pm +++ b/lib/Config/Model/Backend/Json.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Backend::Json; -$Config::Model::Backend::Json::VERSION = '2.094'; +$Config::Model::Backend::Json::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -93,7 +93,7 @@ Config::Model::Backend::Json - Read and write config as a JSON data structure =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Backend/PlainFile.pm b/lib/Config/Model/Backend/PlainFile.pm index cc674ed..a6bb690 100644 --- a/lib/Config/Model/Backend/PlainFile.pm +++ b/lib/Config/Model/Backend/PlainFile.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Backend::PlainFile; -$Config::Model::Backend::PlainFile::VERSION = '2.094'; +$Config::Model::Backend::PlainFile::VERSION = '2.095'; use Carp; use Mouse; use Config::Model::Exception; @@ -189,7 +189,7 @@ Config::Model::Backend::PlainFile - Read and write config as plain file =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Backend/ShellVar.pm b/lib/Config/Model/Backend/ShellVar.pm index 47a1f75..5c74379 100644 --- a/lib/Config/Model/Backend/ShellVar.pm +++ b/lib/Config/Model/Backend/ShellVar.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Backend::ShellVar; -$Config::Model::Backend::ShellVar::VERSION = '2.094'; +$Config::Model::Backend::ShellVar::VERSION = '2.095'; use Carp; use Mouse; use Config::Model::Exception; @@ -114,7 +114,7 @@ Config::Model::Backend::ShellVar - Read and write config as a C<SHELLVAR> data s =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Backend/Yaml.pm b/lib/Config/Model/Backend/Yaml.pm index d719922..a71f20e 100644 --- a/lib/Config/Model/Backend/Yaml.pm +++ b/lib/Config/Model/Backend/Yaml.pm @@ -9,7 +9,7 @@ # package Config::Model::Backend::Yaml; -$Config::Model::Backend::Yaml::VERSION = '2.094'; +$Config::Model::Backend::Yaml::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -110,7 +110,7 @@ Config::Model::Backend::Yaml - Read and write config as a YAML data structure =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/BackendMgr.pm b/lib/Config/Model/BackendMgr.pm index fdb53cb..8a71490 100644 --- a/lib/Config/Model/BackendMgr.pm +++ b/lib/Config/Model/BackendMgr.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::BackendMgr; -$Config::Model::BackendMgr::VERSION = '2.094'; +$Config::Model::BackendMgr::VERSION = '2.095'; use Mouse; use Carp; @@ -813,7 +813,7 @@ Config::Model::BackendMgr - Load configuration node on demand =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -951,6 +951,24 @@ directory can be hardcoded in the custom class. C<config_dir> beginning with 'C<~>' is munged so C<~> is replaced by C<< File::HomeDir->my_data >>. See L<File::HomeDir> for details. +=item file + +Specify configuration file name (without the path). This parameter is +optional as the file name can be hardcoded in the custom class. + +The configuration file name can be specified with C<&index> keyword +when a backend is associated to a node contained in a hash. For instance, +with C<file> set to C<index.conf>: + + service # hash element + foo # hash index + nodeA # values of nodeA are stored in foo.conf + bar # hash index + nodeB # values of nodeB are stored in bar.conf + +Alternatively, C<file> can be set to C<->, in which case, the +configuration is read from STDIN. + =item os_config_dir Specify alternate location of a configuration directory depending on the OS diff --git a/lib/Config/Model/CheckList.pm b/lib/Config/Model/CheckList.pm index d3cf4e9..26548c7 100644 --- a/lib/Config/Model/CheckList.pm +++ b/lib/Config/Model/CheckList.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::CheckList; -$Config::Model::CheckList::VERSION = '2.094'; +$Config::Model::CheckList::VERSION = '2.095'; use Mouse; use 5.010; @@ -23,6 +23,8 @@ use Storable qw/dclone/; extends qw/Config::Model::AnyThing/; with "Config::Model::Role::WarpMaster"; +with "Config::Model::Role::Grab"; +with "Config::Model::Role::HelpAsText"; my $logger = get_logger("Tree::Element::CheckList"); @@ -744,7 +746,7 @@ Config::Model::CheckList - Handle check list element =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -931,7 +933,7 @@ checklist are available. This other hash or other checklist is indicated by the C<refer_to> or C<computed_refer_to> parameter. C<refer_to> uses the syntax of the -C<steps> parameter of L<grab(...)|Config::AnyThing/"grab(...)"> +C<steps> parameter of L<grab(...)|Config::Role::Grab/grab"> See L<refer_to parameter|Config::Model::IdElementReference/"refer_to parameter">. diff --git a/lib/Config/Model/Cookbook/CreateModelFromDoc.pod b/lib/Config/Model/Cookbook/CreateModelFromDoc.pod index bdcc24f..1b861db 100644 --- a/lib/Config/Model/Cookbook/CreateModelFromDoc.pod +++ b/lib/Config/Model/Cookbook/CreateModelFromDoc.pod @@ -13,7 +13,7 @@ Config::Model::Cookbook::CreateModelFromDoc - Create a configuration model from =head1 VERSION -version 2.094 +version 2.095 =head1 Introduction diff --git a/lib/Config/Model/Describe.pm b/lib/Config/Model/Describe.pm index a916744..acd84da 100644 --- a/lib/Config/Model/Describe.pm +++ b/lib/Config/Model/Describe.pm @@ -9,7 +9,7 @@ # package Config::Model::Describe; -$Config::Model::Describe::VERSION = '2.094'; +$Config::Model::Describe::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -182,7 +182,7 @@ Config::Model::Describe - Provide a description of a node element =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/DumpAsData.pm b/lib/Config/Model/DumpAsData.pm index 00c716f..0b500c3 100644 --- a/lib/Config/Model/DumpAsData.pm +++ b/lib/Config/Model/DumpAsData.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::DumpAsData; -$Config::Model::DumpAsData::VERSION = '2.094'; +$Config::Model::DumpAsData::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -252,7 +252,7 @@ Config::Model::DumpAsData - Dump configuration content as a perl data structure =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Dumper.pm b/lib/Config/Model/Dumper.pm index 6800ea0..fed73df 100644 --- a/lib/Config/Model/Dumper.pm +++ b/lib/Config/Model/Dumper.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Dumper; -$Config::Model::Dumper::VERSION = '2.094'; +$Config::Model::Dumper::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -259,7 +259,7 @@ Config::Model::Dumper - Serialize data of config tree =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Exception.pm b/lib/Config/Model/Exception.pm index 99f3144..1500d92 100644 --- a/lib/Config/Model/Exception.pm +++ b/lib/Config/Model/Exception.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Exception; -$Config::Model::Exception::VERSION = '2.094'; +$Config::Model::Exception::VERSION = '2.095'; use warnings; use strict; use Data::Dumper; @@ -93,19 +93,19 @@ sub full_message { } package Config::Model::Exception::Any; -$Config::Model::Exception::Any::VERSION = '2.094'; +$Config::Model::Exception::Any::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception'; package Config::Model::Exception::ModelDeclaration; -$Config::Model::Exception::ModelDeclaration::VERSION = '2.094'; +$Config::Model::Exception::ModelDeclaration::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::Fatal'; sub _desc {'configuration model declaration error' } package Config::Model::Exception::User ; -$Config::Model::Exception::User::VERSION = '2.094'; +$Config::Model::Exception::User::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::Any'; sub _desc {'user error' } @@ -113,7 +113,7 @@ sub _desc {'user error' } ## old classes below package Config::Model::Exception::Syntax; -$Config::Model::Exception::Syntax::VERSION = '2.094'; +$Config::Model::Exception::Syntax::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::Any'; @@ -134,7 +134,7 @@ sub full_message { } package Config::Model::Exception::LoadData; -$Config::Model::Exception::LoadData::VERSION = '2.094'; +$Config::Model::Exception::LoadData::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; @@ -158,7 +158,7 @@ sub full_message { } package Config::Model::Exception::Model; -$Config::Model::Exception::Model::VERSION = '2.094'; +$Config::Model::Exception::Model::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::Fatal'; @@ -191,7 +191,7 @@ sub full_message { } package Config::Model::Exception::Load; -$Config::Model::Exception::Load::VERSION = '2.094'; +$Config::Model::Exception::Load::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; @@ -220,7 +220,7 @@ sub full_message { } package Config::Model::Exception::UnavailableElement; -$Config::Model::Exception::UnavailableElement::VERSION = '2.094'; +$Config::Model::Exception::UnavailableElement::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; @@ -251,7 +251,7 @@ sub full_message { } package Config::Model::Exception::AncestorClass; -$Config::Model::Exception::AncestorClass::VERSION = '2.094'; +$Config::Model::Exception::AncestorClass::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; @@ -259,7 +259,7 @@ sub _desc { 'unknown ancestor class'} package Config::Model::Exception::ObsoleteElement; -$Config::Model::Exception::ObsoleteElement::VERSION = '2.094'; +$Config::Model::Exception::ObsoleteElement::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; @@ -275,7 +275,7 @@ sub full_message { my $msg = $self->description; my $location = $obj->name; - my $help = $obj->get_help($element) || ''; + my $help = $obj->get_help_as_text($element) || ''; $msg .= " '$element' in node '$location'.\n"; $msg .= "\t$help\n"; @@ -284,7 +284,7 @@ sub full_message { } package Config::Model::Exception::UnknownElement; -$Config::Model::Exception::UnknownElement::VERSION = '2.094'; +$Config::Model::Exception::UnknownElement::VERSION = '2.095'; use Carp; use Mouse; @@ -359,14 +359,14 @@ sub full_message { } package Config::Model::Exception::WarpError; -$Config::Model::Exception::WarpError::VERSION = '2.094'; +$Config::Model::Exception::WarpError::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; sub _desc { 'warp error'} package Config::Model::Exception::Fatal; -$Config::Model::Exception::Fatal::VERSION = '2.094'; +$Config::Model::Exception::Fatal::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::Any'; @@ -374,7 +374,7 @@ sub _desc { 'fatal error' } package Config::Model::Exception::UnknownId; -$Config::Model::Exception::UnknownId::VERSION = '2.094'; +$Config::Model::Exception::UnknownId::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; @@ -408,7 +408,7 @@ sub full_message { } package Config::Model::Exception::WrongValue; -$Config::Model::Exception::WrongValue::VERSION = '2.094'; +$Config::Model::Exception::WrongValue::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; @@ -416,7 +416,7 @@ sub _desc { 'wrong value'}; package Config::Model::Exception::WrongType; -$Config::Model::Exception::WrongType::VERSION = '2.094'; +$Config::Model::Exception::WrongType::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; @@ -447,14 +447,14 @@ sub full_message { } package Config::Model::Exception::ConfigFile; -$Config::Model::Exception::ConfigFile::VERSION = '2.094'; +$Config::Model::Exception::ConfigFile::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::User'; sub _desc { 'error in configuration file' } package Config::Model::Exception::ConfigFile::Missing; -$Config::Model::Exception::ConfigFile::Missing::VERSION = '2.094'; +$Config::Model::Exception::ConfigFile::Missing::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::ConfigFile'; @@ -471,14 +471,14 @@ sub full_message { } package Config::Model::Exception::Formula; -$Config::Model::Exception::Formula::VERSION = '2.094'; +$Config::Model::Exception::Formula::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::Model'; sub _desc { 'error in computation formula of the configuration model'} package Config::Model::Exception::Internal; -$Config::Model::Exception::Internal::VERSION = '2.094'; +$Config::Model::Exception::Internal::VERSION = '2.095'; use Mouse; extends 'Config::Model::Exception::Fatal'; @@ -500,7 +500,7 @@ Config::Model::Exception - Exception mechanism for configuration model =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/FuseUI.pm b/lib/Config/Model/FuseUI.pm index f8420f0..338fcd7 100644 --- a/lib/Config/Model/FuseUI.pm +++ b/lib/Config/Model/FuseUI.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::FuseUI; -$Config::Model::FuseUI::VERSION = '2.094'; +$Config::Model::FuseUI::VERSION = '2.095'; # there's no Singleton with Mouse use Mouse; @@ -327,7 +327,7 @@ Config::Model::FuseUI - Fuse virtual file interface for Config::Model =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/HashId.pm b/lib/Config/Model/HashId.pm index 4070011..2ad94f2 100644 --- a/lib/Config/Model/HashId.pm +++ b/lib/Config/Model/HashId.pm @@ -8,18 +8,26 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::HashId; -$Config::Model::HashId::VERSION = '2.094'; +$Config::Model::HashId::VERSION = '2.095'; use Mouse; +use 5.10.1; use Config::Model::Exception; use Carp; +use Mouse::Util::TypeConstraints; + +subtype 'HaskKeyArray' => as 'ArrayRef' ; +coerce 'HaskKeyArray' => from 'Str' => via { [$_] } ; + use Log::Log4perl qw(get_logger :levels); my $logger = get_logger("Tree::Element::Id::Hash"); extends qw/Config::Model::AnyId/; +with "Config::Model::Role::Grab"; + has data => ( is => 'rw', isa => 'HashRef', default => sub { {}; } ); has list => ( is => 'rw', @@ -31,8 +39,12 @@ has list => ( } ); -has [qw/default_keys auto_create_keys/] => - ( is => 'rw', isa => 'ArrayRef', default => sub { []; } ); +has [qw/default_keys auto_create_keys/] => ( + is => 'rw', + isa => 'HaskKeyArray', + coerce => 1, + default => sub { []; } +); has [qw/morph ordered/] => ( is => 'ro', isa => 'Bool' ); sub BUILD { @@ -217,6 +229,21 @@ sub sort { } } +sub insort { + my ($self, $id) = @_; + + if ($self->ordered) { + my $elt = $self->fetch_with_id($id); + $self->_sort; + return $elt; + } + else { + Config::Model::Exception::User->throw( + object => $self, + message => "cannot call insort on non ordered hash" + ); + } +} # hash only method sub firstkey { @@ -511,7 +538,7 @@ Config::Model::HashId - Handle hash element for configuration model =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -548,6 +575,16 @@ Returns the number of elements of the hash. Sort an ordered hash. Throws an error if called on a non ordered hash. +=head2 insort + +Parameters: key + +Create a new element in the ordered hash while keeping alphabetical order of the keys + +Returns the newly created element. + +Throws an error if called on a non ordered hash. + =head2 firstkey Returns the first key of the hash. Behaves like C<each> core perl diff --git a/lib/Config/Model/IdElementReference.pm b/lib/Config/Model/IdElementReference.pm index 122db56..fe30c32 100644 --- a/lib/Config/Model/IdElementReference.pm +++ b/lib/Config/Model/IdElementReference.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::IdElementReference; -$Config::Model::IdElementReference::VERSION = '2.094'; +$Config::Model::IdElementReference::VERSION = '2.095'; use Mouse; use Carp; @@ -190,7 +190,7 @@ Config::Model::IdElementReference - Refer to id element(s) and extract keys =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -317,7 +317,7 @@ Construction is handled by the calling object (L<Config::Model::Node>). C<refer_to> is used to specify a hash element that is used as a reference. C<refer_to> points to an array or hash element in the configuration tree using the path syntax (See -L<Config::Model::Node/grab> for details). +L<Config::Model::Role::Grab/grab> for details). =item computed_refer_to diff --git a/lib/Config/Model/Instance.pm b/lib/Config/Model/Instance.pm index b6b24a3..8561de7 100644 --- a/lib/Config/Model/Instance.pm +++ b/lib/Config/Model/Instance.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Instance; -$Config::Model::Instance::VERSION = '2.094'; +$Config::Model::Instance::VERSION = '2.095'; #use Scalar::Util qw(weaken) ; use strict; @@ -22,7 +22,6 @@ use Text::Diff; use File::Path; use Log::Log4perl qw(get_logger :levels); -use Config::Model::Annotation; use Config::Model::Exception; use Config::Model::Node; use Config::Model::Loader; @@ -554,7 +553,7 @@ Config::Model::Instance - Instance of configuration tree =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -726,6 +725,12 @@ In scalar context, returns a big string. Useful to print. Print all changes on STDOUT and return the list of changes. +=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. diff --git a/lib/Config/Model/Iterator.pm b/lib/Config/Model/Iterator.pm index 7d2bfb0..fff9411 100644 --- a/lib/Config/Model/Iterator.pm +++ b/lib/Config/Model/Iterator.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Iterator; -$Config::Model::Iterator::VERSION = '2.094'; +$Config::Model::Iterator::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -281,7 +281,7 @@ Config::Model::Iterator - Iterates forward or backward a configuration tree =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/ListId.pm b/lib/Config/Model/ListId.pm index 6f2665b..98acce9 100644 --- a/lib/Config/Model/ListId.pm +++ b/lib/Config/Model/ListId.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::ListId; -$Config::Model::ListId::VERSION = '2.094'; +$Config::Model::ListId::VERSION = '2.095'; use 5.10.1; use Mouse; @@ -18,6 +18,8 @@ use Log::Log4perl qw(get_logger :levels); use Carp; extends qw/Config::Model::AnyId/; +with "Config::Model::Role::Grab"; + my $logger = get_logger("Tree::Element::Id::List"); has data => ( @@ -502,7 +504,7 @@ Config::Model::ListId - Handle list element for configuration model =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Lister.pm b/lib/Config/Model/Lister.pm index 33abe41..7aaf7fd 100644 --- a/lib/Config/Model/Lister.pm +++ b/lib/Config/Model/Lister.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Lister; -$Config::Model::Lister::VERSION = '2.094'; +$Config::Model::Lister::VERSION = '2.095'; use strict; use warnings; use Exporter; @@ -85,7 +85,7 @@ Config::Model::Lister - List available models and applications =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Loader.pm b/lib/Config/Model/Loader.pm index 06d7a6c..7e7b425 100644 --- a/lib/Config/Model/Loader.pm +++ b/lib/Config/Model/Loader.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Loader; -$Config::Model::Loader::VERSION = '2.094'; +$Config::Model::Loader::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -409,22 +409,28 @@ sub _load_check_list { # function_args are the arguments passed to the load command my %dispatch_action = ( list_leaf => { - ':.sort' => sub { $_[1]->sort; }, - ':.push' => sub { $_[1]->push( @_[ 4 .. $#_ ] ); }, - ':.unshift' => sub { $_[1]->unshift( @_[ 4 .. $#_ ] ); }, - ':.insert_at' => sub { $_[1]->insert_at( @_[ 4 .. $#_ ] ); }, - ':.insort' => sub { $_[1]->insort( @_[ 4 .. $#_ ] ); }, + ':.sort' => sub { $_[1]->sort; return 'ok';}, + ':.push' => sub { $_[1]->push( @_[ 5 .. $#_ ] ); return 'ok'; }, + ':.unshift' => sub { $_[1]->unshift( @_[ 5 .. $#_ ] ); return 'ok'; }, + ':.insert_at' => sub { $_[1]->insert_at( @_[ 5 .. $#_ ] ); return 'ok'; }, + ':.insort' => sub { $_[1]->insort( @_[ 5 .. $#_ ] ); return 'ok'; }, ':.insert_before' => \&_insert_before, }, 'list_*' => { - ':.copy' => sub { $_[1]->copy( $_[4], $_[5] ); }, - ':.clear' => sub { $_[1]->clear;}, + ':.copy' => sub { $_[1]->copy( $_[5], $_[6] ); return 'ok'; }, + ':.clear' => sub { $_[1]->clear; return 'ok'; }, + }, + hash_leaf => { + ':.insort' => sub { $_[1]->insort($_[5])->store($_[6]); return 'ok'; }, + }, + hash_node => => { + ':.insort' => \&_insort_hash_of_node, }, 'hash_*' => { - ':.sort' => sub { $_[1]->sort; }, - ':@' => sub { $_[1]->sort; }, - ':.copy' => sub { $_[1]->copy( $_[4], $_[5] ); }, - ':.clear' => sub { $_[1]->clear;}, + ':.sort' => sub { $_[1]->sort; return 'ok'; }, + ':@' => sub { $_[1]->sort; return 'ok'; }, + ':.copy' => sub { $_[1]->copy( $_[5], $_[6] ); return 'ok'; }, + ':.clear' => sub { $_[1]->clear; return 'ok';}, }, leaf => { ':-=' => \&_remove_by_value, @@ -434,7 +440,8 @@ my %dispatch_action = ( fallback => { ':-' => \&_remove_by_id, '~' => \&_remove_by_id, - } ); + } +); my @equiv = qw/:@ :.sort :< :.push :> :.unshift/; while (@equiv) { @@ -443,20 +450,21 @@ while (@equiv) { } sub _insert_before { - my ( $self, $element, $check, $inst, $before_str, @values ) = @_; + my ( $self, $element, $check, $inst, $cmdref, $before_str, @values ) = @_; my $before = $before_str =~ m!^/! ? eval "qr$before_str" : $before_str; $element->insert_before( $before, @values ); + return 'ok'; } sub _remove_by_id { - my ( $self, $element, $check, $inst, $id ) = @_; - $logger->debug("_remove_by_id: removing id $id"); + my ( $self, $element, $check, $inst, $cmdref, $id ) = @_; + $logger->debug("_remove_by_id: removing id '$id'"); $element->remove($id); return 'ok'; } sub _remove_by_value { - my ( $self, $element, $check, $inst, $rm_val ) = @_; + my ( $self, $element, $check, $inst, $cmdref, $rm_val ) = @_; $logger->debug("_remove_by_value value $rm_val"); foreach my $idx ( $element->fetch_all_indexes ) { @@ -468,7 +476,7 @@ sub _remove_by_value { } sub _remove_matched_value { - my ( $self, $element, $check, $inst, $rm_val ) = @_; + my ( $self, $element, $check, $inst, $cmdref, $rm_val ) = @_; $logger->debug("_remove_matched_value $rm_val"); @@ -483,7 +491,7 @@ sub _remove_matched_value { } sub _substitute_value { - my ( $self, $element, $check, $inst, $s_val ) = @_; + my ( $self, $element, $check, $inst, $cmdref, $s_val ) = @_; $logger->debug("_substitute_value $s_val"); @@ -495,6 +503,13 @@ sub _substitute_value { return 'ok'; } +sub _insort_hash_of_node { + my ( $self, $element, $check, $inst, $cmdref, $id ) = @_; + my $node = $element->insort($_[5]); + $logger->debug("_insort_hash_of_node: calling _load on node id $id"); + return $self->_load( $node, $check, $cmdref ); +} + sub _load_list { my ( $self, $node, $check, $inst, $cmdref ) = @_; my ( $element_name, $action, $f_arg, $id, $subaction, $value, $note ) = @$inst; @@ -544,8 +559,7 @@ sub _load_list { || $dispatch_action{$cargo_type}{$action} || $dispatch_action{'fallback'}{$action}; if ($dispatch) { - $dispatch->( $self, $element, $check, $inst, @f_args ); - return 'ok'; + return $dispatch->( $self, $element, $check, $inst, $cmdref, @f_args ); } } @@ -660,11 +674,18 @@ sub _load_hash { || $dispatch_action{'fallback'}{$action}; if ($dispatch) { # todo missing arguments - $dispatch->( $self, $element, $check, $inst, @f_args ); - return 'ok'; + return $dispatch->( $self, $element, $check, $inst, $cmdref, @f_args ); } } + if (not defined $id) { + Config::Model::Exception::Load->throw( + object => $element, + command => join( '', @$inst ), + error => qq!Unexpected hash instruction: '$action' or missing id! + ); + } + my $obj = $element->fetch_with_id( index => $id, check => $check ); $self->_load_note( $obj, $note, $inst, $cmdref ); @@ -805,7 +826,7 @@ Config::Model::Loader - Load serialized data into config tree =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -1034,6 +1055,23 @@ Insert C<zz> value on C<xxx> list before B<value> matching C<yy>. Insert C<zz> value on C<xxx> list so that existing alphanumeric order is preserved. +=item xxx:.insort(zz) + +For hash element containing nodes: creates a new hash element with +C<zz> key on C<xxx> hash so that existing alphanumeric order of keys +is preserved. Note that all keys are sorted once this instruction is +called. Following instructions are applied on the created +element. I.e. putting key order aside, C<xxx:.insort(zz)> has the +same effect as C<xxx:zz> instruction. + +=item xxx:.insort(zz,vv) + +For hash element containing leaves: creates a new hash element with +C<zz> key and assing value C<vv> so that existing alphanumeric order of keys +is preserved. Note that all keys are sorted once this instruction is +called. Putting key order aside, C<xxx:.insort(zz,vv)> has the +same effect as C<xxx:zz=vv> instruction. + =item xxx:=z1,z2,z3 Set list element C<xxx> to list C<z1,z2,z3>. Use C<,,> for undef diff --git a/lib/Config/Model/Manual/ModelCreationAdvanced.pod b/lib/Config/Model/Manual/ModelCreationAdvanced.pod index 833f2fa..4babc1c 100644 --- a/lib/Config/Model/Manual/ModelCreationAdvanced.pod +++ b/lib/Config/Model/Manual/ModelCreationAdvanced.pod @@ -13,7 +13,7 @@ Config::Model::Manual::ModelCreationAdvanced - Creating a model with advanced fe =head1 VERSION -version 2.094 +version 2.095 =head1 Introduction diff --git a/lib/Config/Model/Manual/ModelCreationIntroduction.pod b/lib/Config/Model/Manual/ModelCreationIntroduction.pod index 7f92d7d..62303a8 100644 --- a/lib/Config/Model/Manual/ModelCreationIntroduction.pod +++ b/lib/Config/Model/Manual/ModelCreationIntroduction.pod @@ -13,7 +13,7 @@ Config::Model::Manual::ModelCreationIntroduction - Introduction to model creatio =head1 VERSION -version 2.094 +version 2.095 =head1 Introduction diff --git a/lib/Config/Model/Node.pm b/lib/Config/Model/Node.pm index 239b139..c899167 100644 --- a/lib/Config/Model/Node.pm +++ b/lib/Config/Model/Node.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Node; -$Config::Model::Node::VERSION = '2.094'; +$Config::Model::Node::VERSION = '2.095'; use Mouse; with "Config::Model::Role::NodeLoader"; @@ -29,6 +29,9 @@ use List::MoreUtils qw(insert_after_string); extends qw/Config::Model::AnyThing/; +with "Config::Model::Role::Grab"; +with "Config::Model::Role::HelpAsText"; + use vars qw(@status @level %default_property); *status = *Config::Model::status; @@ -312,6 +315,7 @@ sub check_properties { sub init { my $self = shift; + my %args = @_; return if $self->{initialized}; $self->{initialized} = 1; # avoid recursions @@ -332,7 +336,7 @@ sub init { ); if ( defined $model->{read_config} ) { - $self->read_config_data( check => $self->check ); + $self->read_config_data( check => $args{check} // $self->check ); } # use read_config data if write_config is missing @@ -650,7 +654,7 @@ sub fetch_element { my $check = $self->_check_check( $args{check} ); my $accept_hidden = $args{accept_hidden} || 0; - $self->init(); + $self->init(check => $check); my $model = $self->{model}; @@ -1080,6 +1084,8 @@ sub copy_from { $self->load( step => $dump, check => $check ); } +# TODO: need Pod::Text attribute -> move that to a role ? +# to translate Pod description to plain text when help is displayed sub get_help { my $self = shift; @@ -1203,7 +1209,7 @@ Config::Model::Node - Class for configuration tree node =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -1751,17 +1757,17 @@ Returns 1 if the element is known in the model. Returns 1 if the element is defined. -=head2 grab(...) +=head2 grab -See L<Config::Model::AnyThing/"grab(...)">. +See L<Config::Model::Role::Grab/grab">. -=head2 grab_value(...) +=head2 grab_value -See L<Config::Model::AnyThing/"grab_value(...)">. +See L<Config::Model::Role::Grab/grab_value">. -=head2 grab_root() +=head2 grab_root -See L<Config::Model::AnyThing/"grab_root()">. +See L<Config::Model::Role::Grab/"grab_root">. =head2 get( path => ..., mode => ... , check => ... , get_obj => 1|0, autoadd => 1|0) diff --git a/lib/Config/Model/ObjTreeScanner.pm b/lib/Config/Model/ObjTreeScanner.pm index 6594da7..7f85e32 100644 --- a/lib/Config/Model/ObjTreeScanner.pm +++ b/lib/Config/Model/ObjTreeScanner.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::ObjTreeScanner; -$Config::Model::ObjTreeScanner::VERSION = '2.094'; +$Config::Model::ObjTreeScanner::VERSION = '2.095'; use strict; use Config::Model::Exception; use Scalar::Util qw/blessed/; @@ -285,7 +285,7 @@ Config::Model::ObjTreeScanner - Scan config tree and perform call-backs for each =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Report.pm b/lib/Config/Model/Report.pm index d39b33f..d4d7a7e 100644 --- a/lib/Config/Model/Report.pm +++ b/lib/Config/Model/Report.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Report; -$Config::Model::Report::VERSION = '2.094'; +$Config::Model::Report::VERSION = '2.095'; use Carp; use strict; use warnings; @@ -44,11 +44,11 @@ sub report { if ( defined $value ) { my $name = defined $index ? " $element:$index" : $element; push @$data_r, $obj->location . " $name = $value"; - my $desc = $obj->get_help($element); + my $desc = $obj->get_help_as_text($element); if ( defined $desc and $desc ) { push @$data_r, wrap( "\t", "\t\t", "DESCRIPTION: $desc" ); } - my $effect = $value_obj->get_help($value); + my $effect = $value_obj->get_help_as_text($value); if ( defined $effect and $effect ) { push @$data_r, wrap( "\t", "\t\t", "SELECTED: $effect" ); } @@ -90,7 +90,7 @@ Config::Model::Report - Reports data from config tree =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/AnyThing.pm b/lib/Config/Model/Role/Grab.pm similarity index 53% copy from lib/Config/Model/AnyThing.pm copy to lib/Config/Model/Role/Grab.pm index 1678eb5..3ab81cb 100644 --- a/lib/Config/Model/AnyThing.pm +++ b/lib/Config/Model/Role/Grab.pm @@ -7,228 +7,19 @@ # # The GNU Lesser General Public License, Version 2.1, February 1999 # -package Config::Model::AnyThing; -$Config::Model::AnyThing::VERSION = '2.094'; -use Mouse; +package Config::Model::Role::Grab; +$Config::Model::Role::Grab::VERSION = '2.095'; +# ABSTRACT: Role to grab data from elsewhere in the tree -# FIXME: must cleanup warp mechanism to implement this -# use MouseX::StrictConstructor; - -use Pod::POM; +use Mouse::Role; +use strict; +use warnings; use Carp; -use Log::Log4perl qw(get_logger :levels); -use 5.10.1; - -my $logger = get_logger("Anything"); -my $change_logger = get_logger("ChangeTracker"); - -has element_name => ( is => 'ro', isa => 'Str' ); -has parent => ( is => 'ro', isa => 'Config::Model::Node', weak_ref => 1 ); - -has instance => ( - is => 'ro', - isa => 'Config::Model::Instance', - weak_ref => 1, - handles => [qw/show_message/] -); - -# needs_check defaults to 1 to trap undef mandatory values -has needs_check => ( is => 'rw', isa => 'Bool', default => 1 ); - -# index_value can be written to when move method is called. But let's -# not advertise this feature. -has index_value => ( - is => 'rw', - isa => 'Str', - trigger => sub { my $self = shift; $self->{location} = $self->_location; }, -); - -has container => ( is => 'ro', isa => 'Ref', required => 1, weak_ref => 1 ); - -has container_type => ( is => 'ro', isa => 'Str', builder => '_container_type', lazy => 1 ); - -sub _container_type { - my $self = shift; - my $p = $self->parent; - return defined $p - ? $p->element_type( $self->element_name ) - : 'node'; # root node - -} - -has root => ( - is => 'ro', - isa => 'Config::Model::Node', - weak_ref => 1, - builder => '_root', - lazy => 1 -); - -sub _root { - my $self = shift; - - return $self->parent || $self; -} - -has location => ( is => 'ro', isa => 'Str', builder => '_location', lazy => 1 ); -has location_short => ( is => 'ro', isa => 'Str', builder => '_location_short', lazy => 1 ); - -has backend_support_annotation => ( - is => 'ro', - isa => 'Bool', - builder => '_backend_support_annotation', - lazy => 1 -); - -sub _backend_support_annotation { - my $self = shift; - # this method is overridden in Config::Model::Node - return $self->parent->backend_support_annotation; -}; - -sub notify_change { - my $self = shift; - my %args = @_; - - return if $self->instance->initial_load and not $args{really}; - - $change_logger->debug( "called for ", $self->name, " from ", join( ' ', caller ), - " with ", join( ' ', %args ) ) - if $change_logger->is_debug; - - # needs_save may be overridden by caller - $args{needs_save} //= 1; - $args{path} //= $self->location; - $args{name} //= $self->element_name if $self->element_name; - $args{index} //= $self->index_value if $self->index_value; - - # better use %args instead of @_ to forward arguments. %args eliminates duplicated keys - $self->container->notify_change(%args); -} - -sub _location { - my $self = shift; - - my $str = ''; - $str .= $self->parent->location if defined $self->parent; - - $str .= ' ' if $str; - - $str .= $self->composite_name; - - return $str; -} - -sub _location_short { - my $self = shift; - - my $str = ''; - $str .= $self->parent->location_short if defined $self->parent; - - $str .= ' ' if $str; - - $str .= $self->composite_name_short; - - return $str; -} - -#has composite_name => (is => 'ro', isa => 'Str' , builder => '_composite_name', lazy => 1); - -sub composite_name { - my $self = shift; - my $element = $self->element_name; - $element = '' unless defined $element; - - my $idx = $self->index_value; - return $element unless defined $idx; - $idx = '"' . $idx . '"' if $idx =~ /\W/; - - return "$element:$idx"; -} - -sub composite_name_short { - my $self = shift; - - my $element = $self->element_name; - $element = '' unless defined $element; - - - my $idx = $self->shorten_idx($self->index_value); - return $element unless length $idx; - $idx = '"' . $idx . '"' if $idx =~ /\W/; - return "$element:$idx"; -} - -sub shorten_idx { - my $self = shift; - my $long_index = shift ; - - my @idx = split /\n/, $long_index // '' ; - my $idx = shift @idx; - $idx .= '[...]' if @idx; - - return $idx // ''; # may be undef on freebsd with perl 5.10.1 ... -} - - -## Fixme: not yet tested -sub xpath { - my $self = shift; - - $logger->debug("xpath called on $self"); - - my $element = $self->element_name; - $element = '' unless defined $element; - - my $idx = $self->index_value; - - my $str = ''; - $str .= $self->cim_parent->parent->xpath - if $self->can('cim_parent') - and defined $self->cim_parent; - - $str .= '/' . $element . ( defined $idx ? "[\@id=$idx]" : '' ) if $element; - - return $str; -} - -sub annotation { - my $self = shift; - $self->{annotation} = join( "\n", grep ( defined $_, @_ ) ) - if @_ - and not $self->instance->preset - and not $self->instance->layered; - return $self->{annotation} || ''; -} - -sub clear_annotation { - my $self = shift; - $self->{annotation} = ''; -} +use Mouse::Util; +use Log::Log4perl qw(get_logger :levels); -sub load_pod_annotation { - my $self = shift; - my $pod = shift; - - my $parser = Pod::POM->new(); - my $pom = $parser->parse_text($pod) - || croak $parser->error(); - my $sections = $pom->head1(); - - foreach my $s (@$sections) { - next unless $s->title eq 'Annotations'; - - foreach my $item ( $s->over->[0]->item ) { - my $path = $item->title . ''; # force string representation. Not understood why... - $path =~ s/^[\s\*]+//; - my $note = $item->text . ''; - $note =~ s/\s+$//; - $logger->debug("load_pod_annotation: '$path' -> '$note'"); - $self->grab( steps => $path )->annotation($note); - } - } -} +my $logger = get_logger("Grab"); ## Navigation @@ -563,86 +354,6 @@ sub grab_ancestor_with_element_named { } } -# fallback method for object that don't implement has_data -sub has_data { - my $self= shift; - $logger->debug("called fall-back has_data for element", $self->name) if $logger->is_debug; - return 1; -} - -sub model_searcher { - my $self = shift; - my %args = @_; - - my $model = $self->instance->config_model; - return Config::Model::SearchElement->new( model => $model, node => $self, %args ); -} - -sub searcher { - carp "Config::Model::AnyThing searcher is deprecated"; - goto &model_searcher; -} - -sub dump_as_data { - my $self = shift; - my $dumper = Config::Model::DumpAsData->new; - $dumper->dump_as_data( node => $self, @_ ); -} - -# hum, check if the check information is valid -sub _check_check { - my $self = shift; - my $p = shift; - - return 'yes' if not defined $p or $p eq '1' or $p eq 'yes'; - return 'no' if $p eq '0' or $p eq 'no'; - return $p if $p eq 'skip'; - - croak "Internal error: Unvalid check value: $p"; -} - -sub has_fixes { - my $self = shift; - $logger->debug( "dummy has_fixes called on " . $self->name ); - return 0; -} - -sub has_warning { - my $self = shift; - $logger->debug( "dummy has_warning called on " . $self->name ); - return 0; -} - -sub warp_error { - my $self = shift; - return '' unless defined $self->{warper}; - return $self->{warper}->warp_error; -} - -# used by Value and AnyId -sub set_convert { - my ( $self, $arg_ref ) = @_; - - my $convert = delete $arg_ref->{convert}; - - # convert_sub keeps a subroutine reference - $self->{convert_sub} = - $convert eq 'uc' ? sub { uc(shift) } - : $convert eq 'lc' ? sub { lc(shift) } - : undef; - - Config::Model::Exception::Model->throw( - object => $self, - error => "Unexpected convert value: $convert, " . "expected lc or uc" - ) unless defined $self->{convert_sub}; -} - -__PACKAGE__->meta->make_immutable; - -1; - -# ABSTRACT: Base class for configuration tree item - __END__ =pod @@ -651,114 +362,25 @@ __END__ =head1 NAME -Config::Model::AnyThing - Base class for configuration tree item +Config::Model::Role::Grab - Role to grab data from elsewhere in the tree =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS - # internal class + $self->load_node( config_class_name => "...", %other_args); =head1 DESCRIPTION -This class must be inherited by all nodes or leaves of the -configuration tree. - -AnyThing provides some methods and no constructor. - -=head1 Introspection methods - -=head2 element_name() - -Returns the element name that contain this object. - -=head2 index_value() - -For object stored in an array or hash element, returns the index (or key) -containing this object. - -=head2 parent() - -Returns the node containing this object. May return undef if C<parent()> -is called on the root of the tree. - -=head2 container_type() - -Returns the type (e.g. C<list> or C<hash> or C<leaf> or C<node> or -C<warped_node>) of the element containing this object. - -=head2 root() - -Returns the root node of the configuration tree. - -=head2 location() - -Returns the node location in the configuration tree. This location -conforms with the syntax defined by L</grab()> method. - -=head2 location_short() - -Returns the node location in the configuration tree. This location truncates long -indexes to be readable. It cannot be used by L</grab()> method. - -=head2 composite_name - -Return the element name with its index (if any). I.e. returns C<foo:bar> or -C<foo>. - -=head2 composite_name_short - -Return the element name with its index (if any). Too long indexes are -truncated to be readable. - -=head1 Annotation - -Annotation is a way to store miscellaneous information associated to -each node. (Yeah... comments). Reading and writing annotation makes -sense only if they can be read from and written to the configuration -file, hence the need for the following method: - -=head2 backend_support_annotation +Role used to let a tree item (i.e. node, hash, list or leaf) to grab +another item or value from the configuration tree using a path (a bit +like an xpath path with a different syntax). -Returns 1 if at least one of the backends attached to a parent node -support to read and write annotations (aka comments) in the -configuration file. +=head1 METHODS -=head2 support_annotation - -Returns 1 if at least one of the backends support to read and write annotations -(aka comments) in the configuration file. - -=head2 annotation( [ note1, [ note2 , ... ] ] ) - -Without argument, return a string containing the object's annotation (or -an empty string). - -With several arguments, join the arguments with "\n", store the annotations -and return the resulting string. - -=head2 load_pod_annotation ( pod_string ) - -Load annotations in configuration tree from a pod document. The pod must -be in the form: - - =over - - =item path - - Annotation text - - =back - -=head2 clear_annotation - -Clear the annotation of an element - -=head1 Information management - -=head2 grab(...) +=head2 grab Grab an object from the configuration tree. @@ -841,104 +463,33 @@ considered when going up the tree. =back -=head2 grab_value(...) +=head2 grab_value -Like L</grab(...)>, but returns the value of a leaf or check_list object, not +Like L</grab>, but returns the value of a leaf or check_list object, not just the leaf object. C<grab_value> raises an exception if following the steps ends on anything but a leaf or a check_list. -=head2 grab_annotation(...) +=head2 grab_annotation -Like L</grab(...)>, but returns the annotation of an object. +Like L</grab>, but returns the annotation of an object. -=head2 grab_root() +=head2 grab_root Returns the root of the configuration tree. -=head2 grab_ancestor( Foo ) - -Go up the configuration tree until the C<Foo> configuration class is found. Returns -the found node or undef. - -=head2 notify_change(...) - -Notify the instance of semantic changes. Parameters are: - -=over 8 - -=item old - -old value. (optional) - -=item new - -new value (optional) - -=item path - -Location of the changed parameter starting from root node. Default to C<$self->location>. - -=item name +=head2 grab_ancestor -element name. Default to C<$self->element_name> - -=item index - -If the changed parameter is part of a hash or an array, C<index> -contains the key or the index to get the changed parameter. - -=item note - -information about the change. Mandatory of neither old or new value are defined. - -=item really - -When set to 1, force recording of change even if in initial load phase. - -=item needs_save - -internal parameter. - -=back - -=head2 show_message( string ) - -Forwarded to L<Config::Model::Instance/"show_message( string )">. - -=head2 model_searcher () - -Returns an object dedicated to search an element in the configuration -model (respecting privilege level). - -This method returns a L<Config::Model::SearchElement> object. See -L<Config::Model::Searcher> for details on how to handle a search. - -=head2 dump_as_data ( ) - -Dumps the configuration data of the node and its siblings into a perl -data structure. - -Returns a hash ref containing the data. See -L<Config::Model::DumpAsData> for details. - -=head2 warp_error - -Returns a string describing any issue with L<Config::Model::Warper> object. -Returns '' if invoked on a tree object without warp specification. - -=head1 AUTHOR +Parameter: a configuration class name -Dominique Dumont, (ddumont at cpan dot org) +Go up the configuration tree until a node using the configuration +class is found. Returns the found node or undef. -=head1 SEE ALSO +Example: -L<Config::Model>, -L<Config::Model::Instance>, -L<Config::Model::Node>, -L<Config::Model::Loader>, -L<Config::Model::Dumper> + # returns a Config::Model::Node object for a Systemd::Service config class + $self->grab('Systemd::Service'); =head1 AUTHOR diff --git a/lib/Config/Model/Role/HelpAsText.pm b/lib/Config/Model/Role/HelpAsText.pm new file mode 100644 index 0000000..706b8b6 --- /dev/null +++ b/lib/Config/Model/Role/HelpAsText.pm @@ -0,0 +1,97 @@ +# +# This file is part of Config-Model +# +# This software is Copyright (c) 2005-2016 by Dominique Dumont. +# +# This is free software, licensed under: +# +# The GNU Lesser General Public License, Version 2.1, February 1999 +# +package Config::Model::Role::HelpAsText; +$Config::Model::Role::HelpAsText::VERSION = '2.095'; +# ABSTRACT: Transalet element help from pod to text + +use Mouse::Role; +use strict; +use warnings; +use Pod::Text; +use Pod::Simple 3.23; +use 5.10.1; + +requires('get_help'); + +sub get_help_as_text { + my $self = shift; + + my $pod = $self->get_help(@_) ; + return undef unless defined $pod; + + my $parser = Pod::Text->new( + indent => 0, + nourls => 1, + ); + + # require Pod::Simple 3.23 + $parser->parse_characters('utf8'); + + my $output = ''; + $parser->output_string(\$output); + + $parser->parse_string_document("=pod\n\n" . $pod); + $output =~ s/[\n\s]+$//; + + return $output; +} + +1; + +__END__ + +=pod + +=encoding UTF-8 + +=head1 NAME + +Config::Model::Role::HelpAsText - Transalet element help from pod to text + +=head1 VERSION + +version 2.095 + +=head1 SYNOPSIS + + $self->get_help_as_text( ... ); + +=head1 DESCRIPTION + +Role used to translate Config::Model help text or description from pod +to text. The provided method should be used when the help text should +be displayed on STDOUT. + +This functionality is provided as a role because the interface to +L<Pod::Text> is not so easy. + +=head1 METHODS + +=head2 get_help_as_text + +Calls C<get_help> and tranlate the output to text. + +=head2 SEE ALSO + +L<Pod::Text>, L<Pod::Simple> + +=head1 AUTHOR + +Dominique Dumont + +=head1 COPYRIGHT AND LICENSE + +This software is Copyright (c) 2005-2016 by Dominique Dumont. + +This is free software, licensed under: + + The GNU Lesser General Public License, Version 2.1, February 1999 + +=cut diff --git a/lib/Config/Model/Role/NodeLoader.pm b/lib/Config/Model/Role/NodeLoader.pm index 8a5395e..1947592 100644 --- a/lib/Config/Model/Role/NodeLoader.pm +++ b/lib/Config/Model/Role/NodeLoader.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Role::NodeLoader; -$Config::Model::Role::NodeLoader::VERSION = '2.094'; +$Config::Model::Role::NodeLoader::VERSION = '2.095'; # ABSTRACT: Load Node element in configuration tree use Mouse::Role; @@ -44,7 +44,7 @@ Config::Model::Role::NodeLoader - Load Node element in configuration tree =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Role/WarpMaster.pm b/lib/Config/Model/Role/WarpMaster.pm index e501dc7..8a99d37 100644 --- a/lib/Config/Model/Role/WarpMaster.pm +++ b/lib/Config/Model/Role/WarpMaster.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Role::WarpMaster; -$Config::Model::Role::WarpMaster::VERSION = '2.094'; +$Config::Model::Role::WarpMaster::VERSION = '2.095'; # ABSTRACT: register and trigger a warped element use Mouse::Role; @@ -106,7 +106,7 @@ Config::Model::Role::WarpMaster - register and trigger a warped element =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/SearchElement.pm b/lib/Config/Model/SearchElement.pm index a601043..146acee 100644 --- a/lib/Config/Model/SearchElement.pm +++ b/lib/Config/Model/SearchElement.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::SearchElement; -$Config::Model::SearchElement::VERSION = '2.094'; +$Config::Model::SearchElement::VERSION = '2.095'; use Log::Log4perl qw(get_logger :levels); use Carp; use strict; @@ -333,7 +333,7 @@ Config::Model::SearchElement - Search an element in a configuration model =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/SimpleUI.pm b/lib/Config/Model/SimpleUI.pm index 465c1c9..d397181 100644 --- a/lib/Config/Model/SimpleUI.pm +++ b/lib/Config/Model/SimpleUI.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::SimpleUI; -$Config::Model::SimpleUI::VERSION = '2.094'; +$Config::Model::SimpleUI::VERSION = '2.095'; use Carp; use 5.010; use strict; @@ -52,10 +52,14 @@ my $desc_sub = sub { if (@_) { my $item; while ( $item = shift ) { - if ( $obj->isa('Config::Model::Node') ) { + if ( $obj->get_type() eq 'node' ) { my $type = $obj->element_type($item); my $elt = $obj->fetch_element($item); - $res .= "element $item (type $type): " . $obj->get_help($item) . "\n"; + my $help = $obj->get_help_as_text($item); + $res .= "element $item (type $type)"; + $res .= ": " if $help; + $res .= "\n" if $help =~ /\n/ or length($help) > 40 ; + $res .= $help . "\n" if $help; if ( $type eq 'leaf' and $elt->value_type eq 'enum' ) { $res .= " possible values: " . join( ', ', $elt->get_choice ) . "\n"; } @@ -63,7 +67,7 @@ my $desc_sub = sub { } } else { - $res = $obj->get_help(); + $res = $obj->get_help_as_text(); } return $res; }; @@ -330,7 +334,7 @@ Config::Model::SimpleUI - Simple interface for Config::Model =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/TermUI.pm b/lib/Config/Model/TermUI.pm index a9bcf50..4a47e11 100644 --- a/lib/Config/Model/TermUI.pm +++ b/lib/Config/Model/TermUI.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::TermUI; -$Config::Model::TermUI::VERSION = '2.094'; +$Config::Model::TermUI::VERSION = '2.095'; use Carp; use utf8; # so literals and identifiers can be in UTF-8 use v5.12; # or later to get "unicode_strings" feature @@ -228,7 +228,7 @@ Config::Model::TermUI - Interactive command line interface for cme =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/TreeSearcher.pm b/lib/Config/Model/TreeSearcher.pm index 5ac6629..8bdc931 100644 --- a/lib/Config/Model/TreeSearcher.pm +++ b/lib/Config/Model/TreeSearcher.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::TreeSearcher; -$Config::Model::TreeSearcher::VERSION = '2.094'; +$Config::Model::TreeSearcher::VERSION = '2.095'; use Mouse; use Mouse::Util::TypeConstraints; @@ -147,7 +147,7 @@ Config::Model::TreeSearcher - Search tree for match in value, description... =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -225,7 +225,7 @@ Search in all the items above Search the keyword or pattern in the tree. The search is done in a case insensitive manner. Returns a list of path pointing -to the matching tree element. See L<Config::Model::AnyThing/grab(...)> for details +to the matching tree element. See L<Config::Model::Role::Grab/grab> for details on the path syntax. =head1 BUGS diff --git a/lib/Config/Model/Utils/GenClassPod.pm b/lib/Config/Model/Utils/GenClassPod.pm index 81e98d5..3301b98 100644 --- a/lib/Config/Model/Utils/GenClassPod.pm +++ b/lib/Config/Model/Utils/GenClassPod.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Utils::GenClassPod; -$Config::Model::Utils::GenClassPod::VERSION = '2.094'; +$Config::Model::Utils::GenClassPod::VERSION = '2.095'; # ABSTRACT: generate pod documentation from configuration models use strict; @@ -55,7 +55,7 @@ Config::Model::Utils::GenClassPod - generate pod documentation from configuratio =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Value.pm b/lib/Config/Model/Value.pm index b2991bf..734590f 100644 --- a/lib/Config/Model/Value.pm +++ b/lib/Config/Model/Value.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Value; -$Config::Model::Value::VERSION = '2.094'; +$Config::Model::Value::VERSION = '2.095'; use 5.10.1; use Mouse; @@ -32,6 +32,8 @@ use List::MoreUtils qw(any) ; extends qw/Config::Model::AnyThing/; with "Config::Model::Role::WarpMaster"; +with "Config::Model::Role::Grab"; +with "Config::Model::Role::HelpAsText"; my $logger = get_logger("Tree::Element::Value"); my $change_logger = get_logger("Anything::Change"); @@ -1805,7 +1807,7 @@ Config::Model::Value - Strongly typed configuration value =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/Value/LayeredInclude.pm b/lib/Config/Model/Value/LayeredInclude.pm index 24411a2..8552b09 100644 --- a/lib/Config/Model/Value/LayeredInclude.pm +++ b/lib/Config/Model/Value/LayeredInclude.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Value::LayeredInclude; -$Config::Model::Value::LayeredInclude::VERSION = '2.094'; +$Config::Model::Value::LayeredInclude::VERSION = '2.095'; use 5.010; use strict; use warnings; @@ -108,7 +108,7 @@ Config::Model::Value::LayeredInclude - Include a sub layer configuration =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS diff --git a/lib/Config/Model/ValueComputer.pm b/lib/Config/Model/ValueComputer.pm index 82c90ce..6b0dd96 100644 --- a/lib/Config/Model/ValueComputer.pm +++ b/lib/Config/Model/ValueComputer.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::ValueComputer; -$Config::Model::ValueComputer::VERSION = '2.094'; +$Config::Model::ValueComputer::VERSION = '2.095'; use Mouse; use MouseX::StrictConstructor; @@ -581,7 +581,7 @@ Config::Model::ValueComputer - Provides configuration value computation =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -643,7 +643,7 @@ A string formula that use variables and replace function. A set of variable and their relative location in the tree (using the notation explained in -L<grab() method|Config::Model::AnyThing/"grab(...)"> +L<grab() method|Config::Model::Role::Grab/grab"> =item * @@ -658,7 +658,7 @@ An optional parameter to force a Perl eval of a string. B<Note>: A variable must point to a valid location in the configuration tree. Even when C<&index()> or C<$replace{}> is used. After substitution of these functions, the string is used as a path (See -L<grab()|Config::Model::AnyThing/"grab(...)">) starting from the +L<grab()|Config::Model::Role::Grab/grab">) starting from the computed value. Hence the path must begin with C<!> to go back to root node, or C<-> to go up a level. diff --git a/lib/Config/Model/WarpedNode.pm b/lib/Config/Model/WarpedNode.pm index cbfe274..c83fbf4 100644 --- a/lib/Config/Model/WarpedNode.pm +++ b/lib/Config/Model/WarpedNode.pm @@ -8,9 +8,8 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::WarpedNode; -$Config::Model::WarpedNode::VERSION = '2.094'; +$Config::Model::WarpedNode::VERSION = '2.095'; use Mouse; -with "Config::Model::Role::NodeLoader"; use Carp qw(cluck croak); @@ -23,6 +22,9 @@ use Scalar::Util qw/weaken/; extends qw/Config::Model::AnyThing/; +with "Config::Model::Role::NodeLoader"; +with "Config::Model::Role::Grab"; + my $logger = get_logger("Tree::Node::Warped"); # don't authorize to warp 'morph' parameter as it may lead to @@ -80,7 +82,7 @@ foreach my $method ( qw/fetch_element config_class_name copy_from get_element_name has_element is_element_available element_type load fetch_element_value get_type get_cargo_type dump_tree - describe get_help children get set accept_regexp/ + describe get_help get_help_as_text children get set accept_regexp/ ) { # to register new methods in package no strict "refs"; @@ -311,7 +313,7 @@ Config::Model::WarpedNode - Node that change config class properties =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -405,7 +407,7 @@ Always set to C<warped_node>. =item follow -L<Grab string|Config::Model::AnyThing/"grab(...)"> leading to the +L<Grab string|Config::Model::Role::Grab/grab"> leading to the C<Config::Model::Value> warp master. See L<Config::Model::Warper/"Warp follow argument"> for details. diff --git a/lib/Config/Model/Warper.pm b/lib/Config/Model/Warper.pm index c5cb31b..ab68c90 100644 --- a/lib/Config/Model/Warper.pm +++ b/lib/Config/Model/Warper.pm @@ -8,7 +8,7 @@ # The GNU Lesser General Public License, Version 2.1, February 1999 # package Config::Model::Warper; -$Config::Model::Warper::VERSION = '2.094'; +$Config::Model::Warper::VERSION = '2.095'; use Mouse; use Log::Log4perl qw(get_logger :levels); @@ -620,7 +620,7 @@ Config::Model::Warper - Warp tree properties =head1 VERSION -version 2.094 +version 2.095 =head1 SYNOPSIS @@ -654,13 +654,13 @@ and C<rules>: =head2 Warp follow argument -L<Grab string|Config::Model::AnyThing/"grab(...)"> leading to the +L<Grab string|Config::Model::Role::Grab/grab> leading to the C<Config::Model::Value> or L<Config::Model::CheckList> warp master. E.g.: follow => '! tree_macro' In case of several warp master, C<follow> is set to an array ref -of several L<grab string|Config::Model::AnyThing/"grab(...)">: +of several L<grab string|Config::Model::Role::Grab/grab>: follow => [ '! macro1', '- macro2' ] @@ -822,7 +822,7 @@ warp master is an enumerated type) When a warped object is created, the constructor registers to the warp masters. The warp master are found by using the special string passed to the C<follow> parameter. As explained in -L<grab method|Config::Model::AnyThing/"grab(...)">, +L<grab method|Config::Model::Role::Grab/grab>, the string provides the location of the warp master in the configuration tree using a symbolic form. diff --git a/t/annotation.t b/t/annotation.t index b708005..9b3c61b 100644 --- a/t/annotation.t +++ b/t/annotation.t @@ -1,12 +1,13 @@ # -*- cperl -*- use ExtUtils::testlib; -use Test::More tests => 19; +use Test::More ; use Test::Memory::Cycle; use Config::Model; use Config::Model::Annotation; use File::Path; use Data::Dumper; +use 5.10.0; use warnings; no warnings qw(once); @@ -52,9 +53,14 @@ my $step = . '! hash_a:X2=x hash_a:Y2=xy hash_a:toto#"index comment" hash_b:X3=xy my_check_list=X2,X3'; ok( $root->load( step => $step ), "set up data in tree with '$step'" ); - -my @annotate = map { [ $_ => "$_ annotation" ] } - ( 'std_id', 'std_id:bc X', 'my_check_list', 'olist:0', 'olist:2' ); +$inst->clear_changes; + +my @annotate = map + { [ $_ => "$_ annotation" ] } + ( + 'std_id', 'std_id', # test that 2 saves of same value is tracked once + 'std_id:bc X', 'my_check_list', 'olist:0', 'olist:2' + ); my %expect = ( 'hash_a:toto' => "index comment", 'olist:1' => 'olist1_comment' ); foreach (@annotate) { @@ -64,10 +70,18 @@ foreach (@annotate) { ok( 1, "set annotation of $l" ); } +say "pending changes:\n".$inst->list_changes if $trace; +is( $inst->needs_save, 5, "verify instance needs_save status after storing only annotations" ); +$inst->clear_changes; + is( $root->grab("std_id:ab X")->annotation('to delete'), 'to delete', "test clear annotation" ); is( $root->grab("std_id:ab X")->clear_annotation, '', "test clear annotation" ); +say "pending changes:\n".$inst->list_changes if $trace; +is( $inst->needs_save, 2, "verify instance needs_save status after store/delete annotations" ); +$inst->clear_changes; + my $annotate_saver = Config::Model::Annotation->new( config_class_name => 'Master', instance => $inst, @@ -132,4 +146,6 @@ my $h3_ref = $saver2->get_annotation_hash(); print Dumper ($h3_ref) if $trace; is_deeply( $h3_ref, \%expect3, "check loaded annotation data with non-empty tree" ); -memory_cycle_ok($model); +memory_cycle_ok($model, "memory cycles"); + +done_testing; diff --git a/t/backend_mgr.t b/t/backend_mgr.t index 52f9aa0..fc4200f 100644 --- a/t/backend_mgr.t +++ b/t/backend_mgr.t @@ -4,15 +4,14 @@ use ExtUtils::testlib; use Test::More; use Test::Memory::Cycle; use Config::Model; -use File::Path; -use File::Copy; +use Path::Tiny 0.070; use Test::Warn; use Test::Exception; use Test::File::Contents; use warnings; no warnings qw(once); - +use 5.10.1; use strict; use vars qw/$model/; @@ -40,15 +39,15 @@ else { ok( 1, "compiled" ); # pseudo root for config files -my $wr_root = 'wr_root'; -my $root1 = "$wr_root/test1/"; -my $root2 = "$wr_root/test2/"; -my $root3 = "$wr_root/test3/"; +my $wr_root = path('wr_root'); +my $root1 = $wr_root->child('test1'); +my $root2 = $wr_root->child('test2'); +my $root3 = $wr_root->child('test3'); my $conf_dir = '/etc/test/'; # cleanup before tests -rmtree($wr_root); +$wr_root->remove_tree; # model declaration $model->create_config_class( @@ -57,7 +56,10 @@ $model->create_config_class( [qw/X Y Z/] => { type => 'leaf', value_type => 'enum', - choice => [qw/Av Bv Cv/] } ] ); + choice => [qw/Av Bv Cv/] + } + ] +); $model->create_config_class( name => 'Level1', @@ -86,7 +88,9 @@ $model->create_config_class( bar => { type => 'node', config_class_name => 'Level2', - } ] ); + } + ] +); $model->create_config_class( name => 'SameReadWriteSpec', @@ -106,7 +110,10 @@ $model->create_config_class( bar => { type => 'node', config_class_name => 'Level2', - } ] ); + }, + int_with_max => {qw/type leaf value_type integer max 10/}, + ] +); $model->create_config_class( name => 'Master', @@ -144,7 +151,8 @@ $model->create_config_class( type => 'node', config_class_name => 'SameReadWriteSpec', }, - ] ); + ] +); $model->create_config_class( name => 'FromScratch', @@ -158,7 +166,8 @@ $model->create_config_class( element => [ aa => { type => 'leaf', value_type => 'string' }, - ] ); + ] +); $model->create_config_class( name => 'CdsWithFile', @@ -172,7 +181,8 @@ $model->create_config_class( element => [ aa => { type => 'leaf', value_type => 'string' }, - ] ); + ] +); $model->create_config_class( name => 'CdsWithNoFile', @@ -181,7 +191,8 @@ $model->create_config_class( element => [ aa => { type => 'leaf', value_type => 'string' }, - ] ); + ] +); $model->create_config_class( name => 'SimpleRW', @@ -196,7 +207,8 @@ $model->create_config_class( element => [ aa => { type => 'leaf', value_type => 'string' }, - ] ); + ] +); #global variable to snoop on read config action my %result; @@ -264,7 +276,7 @@ package main; my $i_fail = $model->instance( instance_name => 'failed_inst', root_class_name => 'Master', - root_dir => $root1, + root_dir => $root1->stringify, backend => 'perl_file', ); throws_ok { @@ -278,7 +290,7 @@ is( $result{master_read}, undef, "Master read conf dir" ); my $i_zero = $model->instance( instance_name => 'zero_inst', root_class_name => 'Master', - root_dir => $root1, + root_dir => $root1->stringify, ); ok( $i_zero, "Created instance (from scratch)" ); @@ -320,15 +332,15 @@ $i_zero->write_back( backend => 'all', force => 1 ); # check written files foreach my $suffix (qw/cds ini/) { map { - my $f = "$root1$conf_dir/$_.$suffix"; - ok( -e $f, "check written file $f" ); + my $f = $root1->child("$conf_dir/$_.$suffix"); + ok( $f->is_file, "check written file $f" ); } ( 'zero_inst', 'zero_inst/level1', 'zero_inst/samerw' ); } foreach my $suffix (qw/pl/) { map { - my $f = "$root1$conf_dir/$_.$suffix"; - ok( -e "$f", "check written file $f" ); + my $f = $root1->child("$conf_dir/$_.$suffix"); + ok( $f->is_file, "check written file $f" ); } ( 'zero_inst', 'zero_inst/level1' ); } @@ -342,12 +354,15 @@ $i_zero->write_back( backend => 'all', config_dir => $override, force => 1 ); # check written files foreach my $suffix (qw/cds ini/) { - map { ok( -e "$root1$override$_.$suffix", "check written file $root1$override$_.$suffix" ); } - ( 'zero_inst', 'zero_inst/level1', 'zero_inst/samerw' ); + map { + ok( $root1->child("$override$_.$suffix")->is_file, + "check written file ".$root1->child("$override$_.$suffix") ); + } ( 'zero_inst', 'zero_inst/level1', 'zero_inst/samerw' ); } foreach my $suffix (qw/pl/) { - map { ok( -e "$root1$override$_.$suffix", "check written file $root1$override$_.$suffix" ); } - ( 'zero_inst', 'zero_inst/level1' ); + map { ok( $root1->child("$override$_.$suffix")->is_file, + "check written file ".$root1->child("$override$_.$suffix") ); + } ( 'zero_inst', 'zero_inst/level1' ); } is( $result{wr_stuff}, $override, 'check custom overridden write dir' ); @@ -368,24 +383,22 @@ my %cds = ( "$inst2/level1" => 'bar X=Av Y=Bv - ' ); -my $dir2 = "$root2/etc/test/"; -mkpath( $dir2 . $inst2, 0, 0755 ) || die "Can't mkpath $dir2.$inst2:$!"; +my $dir2 = $root2->child("etc/test/"); +$dir2->child($inst2)->mkpath(); # write input config files foreach my $f ( keys %cds ) { - my $fout = "$dir2/$f.cds"; + my $fout = $dir2->child($f.'.cds'); next if -r $fout; - open( FOUT, ">$fout" ) or die "can't open $fout:$!"; - print FOUT $cds{$f}; - close FOUT; + $fout->spew($cds{$f}); } # create another instance my $test2_inst = $model->instance( root_class_name => 'Master', instance_name => $inst2, - root_dir => $root2, + root_dir => $root2->stringify, ); ok( $test2_inst, "created second instance" ); @@ -412,13 +425,6 @@ samerw '; is( $dump2, $expect2, "$inst2: check dump" ); -# test loading with ini files -map { - my $o = $_; - s!$root1/zero!ini!; - copy( $o, "$root2/$_" ) or die "can't copy $o $_:$!" -} glob("$root1/*.ini"); - # create another instance to load ini files my $ini_inst = $model->instance( root_class_name => 'Master', @@ -440,15 +446,6 @@ $dump = $ini_inst->config_root->dump_tree; is( $dump, $expect_custom, "ini_test: check dump" ); -unlink( glob("$root2/*.ini") ); - -# test loading with pl files -map { - my $o = $_; - s!$root1/zero!pl!; - copy( $o, "$root2/$_" ) or die "can't copy $o $_:$!" -} glob("$root1/*.pl"); - # create another instance to load pl files my $pl_inst = $model->instance( root_class_name => 'Master', @@ -464,7 +461,7 @@ is( $dump, $expect_custom, "pl_test: check dump" ); my $scratch_i = $model->instance( root_class_name => 'FromScratch', instance_name => 'scratch_inst', - root_dir => $root3, + root_dir => $root3->stringify, ); ok( $scratch_i, "Created instance from scratch to load cds files" ); @@ -478,7 +475,7 @@ ok( -e "$root3/$conf_dir/scratch_inst.cds", "wrote cds config file" ); my $cdswf = $model->instance( root_class_name => 'CdsWithFile', instance_name => 'cds_with_file_inst', - root_dir => $root3, + root_dir => $root3->stringify, ); ok( $cdswf, "Created instance to load custom cds file" ); @@ -489,14 +486,13 @@ is( $cdswf->config_root->dump_tree, $expect, "check dump" ); $cdswf->write_back; -my $toto_conf = "$root3/$conf_dir/toto.conf"; -copy( "$root3/$conf_dir/scratch_inst.cds", $toto_conf ) +my $toto_conf = $root3->child("$conf_dir/scratch_inst.cds")->copy( $root3->child("$conf_dir/toto.conf")) or die "can't copy scratch_inst.cds to toto.conf:$!"; my $ctoto = $model->instance( root_class_name => 'SimpleRW', instance_name => 'custom_toto', - root_dir => $root3, + root_dir => $root3->stringify, ); ok( $ctoto, "Created instance to load custom custom toto file" ); @@ -520,7 +516,7 @@ my $scratch_conf = 'etc/test/scratch_inst.cds'; my $cdswnf = $model->instance( root_class_name => 'CdsWithNoFile', instance_name => 'cds_with_no_file_inst', - root_dir => $root3, + root_dir => $root3->stringify, config_file => $scratch_conf, ); ok( $cdswnf, "Created instance to load overridden cds config file" ); diff --git a/t/dump_load_model.pm b/t/dump_load_model.pm index 0b26d99..a872d41 100644 --- a/t/dump_load_model.pm +++ b/t/dump_load_model.pm @@ -127,6 +127,13 @@ cargo_type => 'leaf', cargo_args => { value_type => 'string' }, }, + ordered_hash_of_node => { + type => 'hash', + index_type => 'string', + ordered => 1, + cargo_type => 'node', + config_class_name => 'SubSlave2', + }, olist => { type => 'list', cargo_type => 'node', diff --git a/t/hash_id_of_values.t b/t/hash_id_of_values.t index 6d692c0..ebc5dd8 100644 --- a/t/hash_id_of_values.t +++ b/t/hash_id_of_values.t @@ -368,8 +368,18 @@ $oh->move_after( 'd', 'e' ); eq_or_diff( [ $oh->fetch_all_indexes ], [qw/a z x e d/], "check index order of ordered_hash after move_after(d e)" ); +$oh->sort; +eq_or_diff( [ $oh->fetch_all_indexes ], + [qw/a d e x z/], "check index order of ordered_hash after sort" ); + +$oh->insort('v')->store('v val'); +eq_or_diff( [ $oh->fetch_all_indexes ], + [qw/a d e v x z/], "check index order of ordered_hash after insort" ); +is($oh->fetch_with_id('v')->fetch,'v val',"check value entered with insort"); + $inst->clear_changes; $oh->clear; + is( $inst->needs_save, 1, "verify instance needs_save status after clear" ); eq_or_diff([$inst->list_changes],['ordered_hash: cleared all entries'],"check change message after clear"); eq_or_diff( [ $oh->fetch_all_indexes ], [], "check index order of ordered_hash after clear" ); diff --git a/t/load.t b/t/load.t index 5877070..ad1ae3f 100644 --- a/t/load.t +++ b/t/load.t @@ -400,6 +400,15 @@ eq_or_diff( [$oh->fetch_all_indexes()],[qw/b a/], "check unsorted keys") ; $root->load('ordered_hash:.sort') ; eq_or_diff( [$oh->fetch_all_indexes()],[qw/a b/], "check sorted keys") ; +# test insort on ordered hash +$root->load('ordered_hash:.insort(d,"dv")') ; +eq_or_diff( [$oh->fetch_all_indexes()],[qw/a b d/], "check sorted keys after insort") ; + +# test insort on ordered hash of node +my $ohon = $root->fetch_element('ordered_hash_of_node'); +$root->load('ordered_hash_of_node:g aa2="g aa2 val" - ordered_hash_of_node:.insort(d) aa2="d aa2 val"'); +eq_or_diff( [$ohon->fetch_all_indexes()],[qw/d g/], "check sorted keys") ; + # test combination of annotation plus load and some utf8 $step = 'std_id#std_id_note ! std_id:ab#std_id_ab_note X=Bv X#X_note - std_id:bc X=Av X#X2_note ' diff --git a/t/model_tests.d/multi-ini-examples/max-overflow/etc/bar.conf b/t/model_tests.d/multi-ini-examples/max-overflow/etc/bar.conf new file mode 100644 index 0000000..d6cf8ff --- /dev/null +++ b/t/model_tests.d/multi-ini-examples/max-overflow/etc/bar.conf @@ -0,0 +1 @@ +int_with_max=100 \ No newline at end of file diff --git a/t/model_tests.d/multi-ini-test-conf.pl b/t/model_tests.d/multi-ini-test-conf.pl new file mode 100644 index 0000000..5da431b --- /dev/null +++ b/t/model_tests.d/multi-ini-test-conf.pl @@ -0,0 +1,71 @@ +# +# This file is part of Config-Model +# +# This software is Copyright (c) 2005-2016 by Dominique Dumont. +# +# This is free software, licensed under: +# +# The GNU Lesser General Public License, Version 2.1, February 1999 +# + +# test inifile backend with multiple ini files + +# specify the name of the class to test +$model_to_test = "MultiMiniIni"; + +# create minimal model to test ini file backend. + +# this class is used by MultiMiniIni class below +$model->create_config_class( + name => 'MultiIniTest::Class', + element => [ + int_with_max => {qw/type leaf value_type integer max 10/}, + ], + read_config => [{ + backend => 'IniFile', + config_dir => '/etc/', + file => '&index.conf', + auto_create => 1, + }], +); + +$model->create_config_class( + name => 'MultiMiniIni', + element => [ + service => { + type => 'hash', + index_type => 'string', + # require to trigger load of bar.conf + default_keys => 'bar', + cargo => { + type => 'node', + config_class_name => 'MultiIniTest::Class' + } + }, + ], + read_config => [{ + backend => 'Yaml', + config_dir => '/etc/', + file => 'service.yml', + auto_create => 1, + }], +); + + +# the test suite +@tests = ( + { + name => 'max-overflow', + # work only with Config::Model > 2.094 because of an obscure + # initialisation bug occuring while loading a bad value in + # a sub-node (thanks systemd) + load => 'service:bar int_with_max=9', + file_check_sub => sub { + my $list_ref = shift ; + # file added because of default bar key + push @$list_ref, "/etc/service.yml" ; + }, + }, +); + +1; -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libconfig-model-perl.git _______________________________________________ Pkg-perl-cvs-commits mailing list Pkg-perl-cvs-commits@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-perl-cvs-commits