| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # | 
| 2 |  |  |  |  |  |  | # This file is part of Config-Model | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # This software is Copyright (c) 2005-2022 by Dominique Dumont. | 
| 5 |  |  |  |  |  |  | # | 
| 6 |  |  |  |  |  |  | # This is free software, licensed under: | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | #   The GNU Lesser General Public License, Version 2.1, February 1999 | 
| 9 |  |  |  |  |  |  | # | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | # ABSTRACT: Role to grab data from elsewhere in the tree | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | use Mouse::Role; | 
| 14 | 59 |  |  | 59 |  | 29192 | use strict; | 
|  | 59 |  |  |  |  | 137 |  | 
|  | 59 |  |  |  |  | 359 |  | 
| 15 | 59 |  |  | 59 |  | 16098 | use warnings; | 
|  | 59 |  |  |  |  | 121 |  | 
|  | 59 |  |  |  |  | 1166 |  | 
| 16 | 59 |  |  | 59 |  | 326 | use Carp; | 
|  | 59 |  |  |  |  | 131 |  | 
|  | 59 |  |  |  |  | 1527 |  | 
| 17 | 59 |  |  | 59 |  | 360 | use 5.20.0; | 
|  | 59 |  |  |  |  | 134 |  | 
|  | 59 |  |  |  |  | 3287 |  | 
| 18 | 59 |  |  | 59 |  | 743 |  | 
|  | 59 |  |  |  |  | 239 |  | 
| 19 |  |  |  |  |  |  | use List::MoreUtils qw/any/; | 
| 20 | 59 |  |  | 59 |  | 379 | use Mouse::Util; | 
|  | 59 |  |  |  |  | 118 |  | 
|  | 59 |  |  |  |  | 589 |  | 
| 21 | 59 |  |  | 59 |  | 35702 | use Log::Log4perl qw(get_logger :levels); | 
|  | 59 |  |  |  |  | 123 |  | 
|  | 59 |  |  |  |  | 1787 |  | 
| 22 | 59 |  |  | 59 |  | 4270 |  | 
|  | 59 |  |  |  |  | 121 |  | 
|  | 59 |  |  |  |  | 451 |  | 
| 23 |  |  |  |  |  |  | with "Config::Model::Role::Utils"; | 
| 24 |  |  |  |  |  |  | use feature qw/signatures postderef/; | 
| 25 | 59 |  |  | 59 |  | 7248 | no warnings qw/experimental::signatures experimental::postderef/; | 
|  | 59 |  |  |  |  | 121 |  | 
|  | 59 |  |  |  |  | 5943 |  | 
| 26 | 59 |  |  | 59 |  | 363 |  | 
|  | 59 |  |  |  |  | 156 |  | 
|  | 59 |  |  |  |  | 58179 |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | my $logger = get_logger("Grab"); | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | ## Navigation | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | # accept commands like | 
| 33 |  |  |  |  |  |  | # item:b -> go down a node, create a new node if necessary | 
| 34 |  |  |  |  |  |  | # - climbs up | 
| 35 |  |  |  |  |  |  | # ! climbs up to the top | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | # Now return an object and not a value ! | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | my %args = _resolve_arg_shortcut(\@args, 'steps'); | 
| 40 | 3295 |  |  | 3295 | 1 | 99948 | my ( $steps, $mode, $autoadd, $type, $grab_non_available, $check ) = | 
|  | 3295 |  |  |  |  | 4527 |  | 
|  | 3295 |  |  |  |  | 5967 |  | 
|  | 3295 |  |  |  |  | 3822 |  | 
| 41 | 3295 |  |  |  |  | 8814 | ( undef, 'strict', 1, undef, 0, 'yes' ); | 
| 42 | 3295 |  |  |  |  | 8369 |  | 
| 43 |  |  |  |  |  |  | $steps              = delete $args{steps} // delete $args{step}; | 
| 44 |  |  |  |  |  |  | $mode               = delete $args{mode} if defined $args{mode}; | 
| 45 | 3295 |  | 100 |  |  | 11136 | $autoadd            = delete $args{autoadd} if defined $args{autoadd}; | 
| 46 | 3295 | 100 |  |  |  | 8068 | $grab_non_available = delete $args{grab_non_available} | 
| 47 | 3295 | 100 |  |  |  | 6699 | if defined $args{grab_non_available}; | 
| 48 |  |  |  |  |  |  | $type  = delete $args{type};                           # node, leaf or undef | 
| 49 | 3295 | 100 |  |  |  | 6313 | $check = $self->_check_check( delete $args{check} ); | 
| 50 | 3295 |  |  |  |  | 4871 |  | 
| 51 | 3295 |  |  |  |  | 10375 | if ( defined $args{strict} ) { | 
| 52 |  |  |  |  |  |  | carp "grab: deprecated parameter 'strict'. Use mode"; | 
| 53 | 3295 | 50 |  |  |  | 7307 | $mode = delete $args{strict} ? 'strict' : 'adaptative'; | 
| 54 | 0 |  |  |  |  | 0 | } | 
| 55 | 0 | 0 |  |  |  | 0 |  | 
| 56 |  |  |  |  |  |  | Config::Model::Exception::User->throw( | 
| 57 |  |  |  |  |  |  | object  => $self, | 
| 58 |  |  |  |  |  |  | message => "grab: unexpected parameter: " . join( ' ', keys %args ) ) if %args; | 
| 59 | 3295 | 50 |  |  |  | 6279 |  | 
| 60 |  |  |  |  |  |  | Config::Model::Exception::Internal->throw( | 
| 61 |  |  |  |  |  |  | error => "grab: steps parameter must be a string " . "or an array ref" ) | 
| 62 | 3295 | 100 | 100 |  |  | 10314 | unless ref $steps eq 'ARRAY' || ! ref $steps; | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | # accept commands, grep remove empty items left by spurious spaces | 
| 65 |  |  |  |  |  |  | my $huge_string = ref $steps ? join( ' ', @$steps ) : $steps; | 
| 66 |  |  |  |  |  |  | return $self unless $huge_string; | 
| 67 | 3294 | 100 |  |  |  | 7564 |  | 
| 68 | 3294 | 100 |  |  |  | 5955 | my @command = ( | 
| 69 |  |  |  |  |  |  | $huge_string =~ m/ | 
| 70 | 3243 |  |  |  |  | 20738 | (         # begin of *one* command | 
| 71 |  |  |  |  |  |  | (?:        # group parts of a command (e.g ...:... ) | 
| 72 |  |  |  |  |  |  | [^\s"]+  # match anything but a space and a quote | 
| 73 |  |  |  |  |  |  | (?:        # begin quoted group | 
| 74 |  |  |  |  |  |  | "         # begin of a string | 
| 75 |  |  |  |  |  |  | (?:        # begin group | 
| 76 |  |  |  |  |  |  | \\"       # match an escaped quote | 
| 77 |  |  |  |  |  |  | |         # or | 
| 78 |  |  |  |  |  |  | [^"]      # anything but a quote | 
| 79 |  |  |  |  |  |  | )*         # lots of time | 
| 80 |  |  |  |  |  |  | "         # end of the string | 
| 81 |  |  |  |  |  |  | )          # end of quoted group | 
| 82 |  |  |  |  |  |  | ?          # match if I got more than one group | 
| 83 |  |  |  |  |  |  | )+      # can have several parts in one command | 
| 84 |  |  |  |  |  |  | )        # end of *one* command | 
| 85 |  |  |  |  |  |  | /gx | 
| 86 |  |  |  |  |  |  | ); | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | my @saved = @command; | 
| 89 |  |  |  |  |  |  |  | 
| 90 | 3243 |  |  |  |  | 7148 | $logger->trace( | 
| 91 |  |  |  |  |  |  | "grab: executing '", | 
| 92 | 3243 |  |  |  |  | 11389 | join( "' '", @command ), | 
| 93 |  |  |  |  |  |  | "' on object '", | 
| 94 |  |  |  |  |  |  | $self->name, "'" | 
| 95 |  |  |  |  |  |  | ); | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | my @found = ($self); | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 3243 |  |  |  |  | 24038 | COMMAND: | 
| 100 |  |  |  |  |  |  | while (@command) { | 
| 101 |  |  |  |  |  |  | last if $mode eq 'step_by_step' and @saved > @command; | 
| 102 | 3243 |  |  |  |  | 7359 |  | 
| 103 | 5585 | 100 | 100 |  |  | 13809 | my $cmd = shift @command; | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 4879 |  |  |  |  | 7247 | my $obj = $found[-1]; | 
| 106 |  |  |  |  |  |  | $logger->trace( "grab: executing cmd '$cmd' on object '", $obj->name, "($obj)'" ); | 
| 107 | 4879 |  |  |  |  | 6451 |  | 
| 108 | 4879 |  |  |  |  | 12015 | if ( $cmd eq '!' ) { | 
| 109 |  |  |  |  |  |  | push @found, $obj->grab_root(); | 
| 110 | 4879 | 100 |  |  |  | 32077 | next; | 
| 111 | 449 |  |  |  |  | 1283 | } | 
| 112 | 449 |  |  |  |  | 1159 |  | 
| 113 |  |  |  |  |  |  | if ( $cmd =~ /^!([\w:]*)/ ) { | 
| 114 |  |  |  |  |  |  | my $ancestor = $obj->grab_ancestor($1); | 
| 115 | 4430 | 100 |  |  |  | 9348 | if ( defined $ancestor ) { | 
| 116 | 3 |  |  |  |  | 12 | push @found, $ancestor; | 
| 117 | 3 | 50 |  |  |  | 8 | next; | 
| 118 | 3 |  |  |  |  | 9 | } | 
| 119 | 3 |  |  |  |  | 9 | else { | 
| 120 |  |  |  |  |  |  | Config::Model::Exception::AncestorClass->throw( | 
| 121 |  |  |  |  |  |  | object => $obj, | 
| 122 | 0 | 0 |  |  |  | 0 | info   => "grab called from '" | 
| 123 |  |  |  |  |  |  | . $self->name | 
| 124 |  |  |  |  |  |  | . "' with steps '@saved' looking for class $1" | 
| 125 |  |  |  |  |  |  | ) if $mode eq 'strict'; | 
| 126 |  |  |  |  |  |  | return; | 
| 127 |  |  |  |  |  |  | } | 
| 128 | 0 |  |  |  |  | 0 | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | if ( $cmd =~ /^\?(\w[\w-]*)/ ) { | 
| 131 |  |  |  |  |  |  | push @found, $obj->grab_ancestor_with_element_named($1); | 
| 132 | 4427 | 100 |  |  |  | 8233 | $cmd =~ s/^\?//;    #remove the go up part | 
| 133 | 5 |  |  |  |  | 29 | unshift @command, $cmd; | 
| 134 | 4 |  |  |  |  | 24 | next; | 
| 135 | 4 |  |  |  |  | 12 | } | 
| 136 | 4 |  |  |  |  | 16 |  | 
| 137 |  |  |  |  |  |  | if ( $cmd eq '-' ) { | 
| 138 |  |  |  |  |  |  | if ( defined $obj->parent ) { | 
| 139 | 4422 | 100 |  |  |  | 7927 | push @found, $obj->parent; | 
| 140 | 1570 | 50 |  |  |  | 4505 | next; | 
| 141 | 1570 |  |  |  |  | 3361 | } | 
| 142 | 1570 |  |  |  |  | 3796 | else { | 
| 143 |  |  |  |  |  |  | $logger->debug( "grab: ", $obj->name, " has no parent" ); | 
| 144 |  |  |  |  |  |  | return $mode eq 'adaptative' ? $obj : undef; | 
| 145 | 0 |  |  |  |  | 0 | } | 
| 146 | 0 | 0 |  |  |  | 0 | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | unless ( $obj->isa('Config::Model::Node') | 
| 149 |  |  |  |  |  |  | or $obj->isa('Config::Model::WarpedNode') ) { | 
| 150 | 2852 | 100 | 100 |  |  | 10555 | Config::Model::Exception::Model->throw( | 
| 151 |  |  |  |  |  |  | object  => $obj, | 
| 152 | 3 |  |  |  |  | 24 | message => "Cannot apply command '$cmd' on leaf item" | 
| 153 |  |  |  |  |  |  | . " (full command is '@saved')" | 
| 154 |  |  |  |  |  |  | ); | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | my ( $name, $action, $arg ) = | 
| 158 |  |  |  |  |  |  | ( $cmd =~ /(\w[\-\w]*)(?:(:)((?:"[^\"]*")|(?:[\w:\/\.\-\+]+)))?/ ); | 
| 159 | 2849 |  |  |  |  | 13344 |  | 
| 160 |  |  |  |  |  |  | if ( defined $arg and $arg =~ /^"/ and $arg =~ /"$/ ) { | 
| 161 |  |  |  |  |  |  | $arg =~ s/^"//;    # remove leading quote | 
| 162 | 2849 | 100 | 100 |  |  | 8126 | $arg =~ s/"$//;    # remove trailing quote | 
|  |  |  | 66 |  |  |  |  | 
| 163 | 10 |  |  |  |  | 39 | } | 
| 164 | 10 |  |  |  |  | 32 |  | 
| 165 |  |  |  |  |  |  | { | 
| 166 |  |  |  |  |  |  | no warnings "uninitialized"; ## no critic (TestingAndDebugging::ProhibitNoWarnings) | 
| 167 |  |  |  |  |  |  | $logger->debug("grab: cmd '$cmd' -> name '$name', action '$action', arg '$arg'"); | 
| 168 | 59 |  |  | 59 |  | 447 | } | 
|  | 59 |  |  |  |  | 120 |  | 
|  | 59 |  |  |  |  | 70152 |  | 
|  | 2849 |  |  |  |  | 3798 |  | 
| 169 | 2849 |  |  |  |  | 10273 |  | 
| 170 |  |  |  |  |  |  | unless ( $obj->has_element(name => $name, autoadd => $autoadd) ) { | 
| 171 |  |  |  |  |  |  | if ( $mode eq 'step_by_step' ) { | 
| 172 | 2849 | 100 |  |  |  | 22087 | return wantarray ? ( undef, @command ) : undef; | 
| 173 | 206 | 100 |  |  |  | 587 | } | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 174 | 87 | 50 |  |  |  | 495 | elsif ( $mode eq 'loose' ) { | 
| 175 |  |  |  |  |  |  | return; | 
| 176 |  |  |  |  |  |  | } | 
| 177 | 84 |  |  |  |  | 376 | elsif ( $mode eq 'adaptative' ) { | 
| 178 |  |  |  |  |  |  | last; | 
| 179 |  |  |  |  |  |  | } | 
| 180 | 1 |  |  |  |  | 3 | else { | 
| 181 |  |  |  |  |  |  | Config::Model::Exception::UnknownElement->throw( | 
| 182 |  |  |  |  |  |  | object   => $obj, | 
| 183 | 34 |  |  |  |  | 86 | element  => $name, | 
| 184 |  |  |  |  |  |  | function => 'grab', | 
| 185 |  |  |  |  |  |  | info     => "grab called from '" . $self->name . "' with steps '@saved'" | 
| 186 |  |  |  |  |  |  | ); | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | unless ( | 
| 191 |  |  |  |  |  |  | $grab_non_available | 
| 192 | 2643 | 100 | 100 |  |  | 8963 | or $obj->is_element_available( | 
| 193 |  |  |  |  |  |  | name       => $name, | 
| 194 |  |  |  |  |  |  | ) | 
| 195 |  |  |  |  |  |  | ) { | 
| 196 |  |  |  |  |  |  | if ( $mode eq 'step_by_step' ) { | 
| 197 |  |  |  |  |  |  | return wantarray ? ( undef, @command ) : undef; | 
| 198 | 3 | 50 |  |  |  | 29 | } | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 199 | 0 | 0 |  |  |  | 0 | elsif ( $mode eq 'loose' ) { | 
| 200 |  |  |  |  |  |  | return; | 
| 201 |  |  |  |  |  |  | } | 
| 202 | 3 |  |  |  |  | 14 | elsif ( $mode eq 'adaptative' ) { | 
| 203 |  |  |  |  |  |  | last; | 
| 204 |  |  |  |  |  |  | } | 
| 205 | 0 |  |  |  |  | 0 | else { | 
| 206 |  |  |  |  |  |  | Config::Model::Exception::UnavailableElement->throw( | 
| 207 |  |  |  |  |  |  | object   => $obj, | 
| 208 | 0 |  |  |  |  | 0 | element  => $name, | 
| 209 |  |  |  |  |  |  | function => 'grab', | 
| 210 |  |  |  |  |  |  | info     => "grab called from '" . $self->name . "' with steps '@saved'" | 
| 211 |  |  |  |  |  |  | ); | 
| 212 |  |  |  |  |  |  | } | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | my $next_obj = $obj->fetch_element( | 
| 216 |  |  |  |  |  |  | name          => $name, | 
| 217 | 2640 |  |  |  |  | 7157 | check         => $check, | 
| 218 |  |  |  |  |  |  | autoadd       => $autoadd, | 
| 219 |  |  |  |  |  |  | accept_hidden => $grab_non_available | 
| 220 |  |  |  |  |  |  | ); | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | # create list or hash element only if autoadd is true | 
| 223 |  |  |  |  |  |  | if (    defined $action | 
| 224 |  |  |  |  |  |  | and $autoadd == 0 | 
| 225 | 2640 | 100 | 100 |  |  | 7279 | and not $next_obj->exists($arg) ) { | 
|  |  |  | 100 |  |  |  |  | 
| 226 |  |  |  |  |  |  | return if $mode eq 'loose'; | 
| 227 |  |  |  |  |  |  | Config::Model::Exception::UnknownId->throw( | 
| 228 | 11 | 100 |  |  |  | 42 | object   => $obj->fetch_element($name), | 
| 229 | 8 | 50 |  |  |  | 38 | element  => $name, | 
| 230 |  |  |  |  |  |  | id       => $arg, | 
| 231 |  |  |  |  |  |  | function => 'grab' | 
| 232 |  |  |  |  |  |  | ) unless $mode eq 'adaptative'; | 
| 233 |  |  |  |  |  |  | last; | 
| 234 |  |  |  |  |  |  | } | 
| 235 | 0 |  |  |  |  | 0 |  | 
| 236 |  |  |  |  |  |  | if ( defined $action and not $next_obj->isa('Config::Model::AnyId') ) { | 
| 237 |  |  |  |  |  |  | return if $mode eq 'loose'; | 
| 238 | 2629 | 100 | 100 |  |  | 7101 | Config::Model::Exception::Model->throw( | 
| 239 | 7 | 50 |  |  |  | 63 | object  => $obj, | 
| 240 | 0 |  |  |  |  | 0 | message => "Cannot apply command '$cmd' on non hash or non list item" | 
| 241 |  |  |  |  |  |  | . " (full command is '@saved'). item is '" | 
| 242 |  |  |  |  |  |  | . $next_obj->name . "'" | 
| 243 |  |  |  |  |  |  | ); | 
| 244 |  |  |  |  |  |  | last; | 
| 245 |  |  |  |  |  |  | } | 
| 246 | 0 |  |  |  |  | 0 |  | 
| 247 |  |  |  |  |  |  | # action can only be : | 
| 248 |  |  |  |  |  |  | $next_obj = $next_obj->fetch_with_id(index => $arg, check => $check) if defined $action; | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 2622 | 100 |  |  |  | 5762 | push @found, $next_obj; | 
| 251 |  |  |  |  |  |  | } | 
| 252 | 2622 |  |  |  |  | 8577 |  | 
| 253 |  |  |  |  |  |  | # check element type | 
| 254 |  |  |  |  |  |  | if ( defined $type ) { | 
| 255 |  |  |  |  |  |  | my @allowed = ref $type ? @$type : ($type); | 
| 256 | 3013 | 100 |  |  |  | 6348 | while ( @found and not any {$found[-1]->get_type eq $_} @allowed ) { | 
| 257 | 68 | 100 |  |  |  | 231 | Config::Model::Exception::WrongType->throw( | 
| 258 | 68 |  | 66 | 72 |  | 559 | object        => $found[-1], | 
|  | 72 |  |  |  |  | 272 |  | 
| 259 | 12 | 100 |  |  |  | 66 | function      => 'grab', | 
| 260 |  |  |  |  |  |  | got_type      => $found[-1]->get_type, | 
| 261 |  |  |  |  |  |  | expected_type => $type, | 
| 262 |  |  |  |  |  |  | info          => "requested with steps '$steps'" | 
| 263 |  |  |  |  |  |  | ) if $mode ne 'adaptative'; | 
| 264 |  |  |  |  |  |  | pop @found; | 
| 265 |  |  |  |  |  |  | } | 
| 266 | 1 |  |  |  |  | 5 | } | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | my $return = $found[-1]; | 
| 269 |  |  |  |  |  |  | $logger->debug( "grab: returning object '", $return->name, "($return)'" ); | 
| 270 | 3002 |  |  |  |  | 4455 | return wantarray ? ( $return, @command ) : $return; | 
| 271 | 3002 |  |  |  |  | 9359 | } | 
| 272 | 3002 | 100 |  |  |  | 29942 |  | 
| 273 |  |  |  |  |  |  | my %args = _resolve_arg_shortcut(\@args, 'steps'); | 
| 274 |  |  |  |  |  |  |  | 
| 275 | 375 |  |  | 375 | 1 | 30331 | my $obj = $self->grab(%args); | 
|  | 375 |  |  |  |  | 599 |  | 
|  | 375 |  |  |  |  | 710 |  | 
|  | 375 |  |  |  |  | 517 |  | 
| 276 | 375 |  |  |  |  | 1213 |  | 
| 277 |  |  |  |  |  |  | # Pb: may return a node. add another option to grab ?? | 
| 278 | 375 |  |  |  |  | 1203 | # to get undef value when needed? | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | return if ( $args{mode} and $args{mode} eq 'loose' and not defined $obj ); | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | if (not $obj->isa("Config::Model::Value") | 
| 283 | 375 | 100 | 66 |  |  | 1148 | and not $obj->isa("Config::Model::CheckList") | 
|  |  |  | 100 |  |  |  |  | 
| 284 |  |  |  |  |  |  | ) { | 
| 285 | 373 | 100 | 100 |  |  | 1410 | Config::Model::Exception::User->throw( | 
| 286 |  |  |  |  |  |  | object  => $self, | 
| 287 |  |  |  |  |  |  | message => "Cannot get a value from '". $obj->location . "'. ", | 
| 288 | 1 |  |  |  |  | 16 | info    => "grab arguments are '".join( "' '", @args ) . "'." | 
| 289 |  |  |  |  |  |  | ); | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | my $value = $obj->fetch; | 
| 293 |  |  |  |  |  |  | if ( $logger->is_debug ) { | 
| 294 |  |  |  |  |  |  | my $str = defined $value ? $value : '<undef>'; | 
| 295 | 372 |  |  |  |  | 1315 | $logger->debug( "grab_value: returning value $str of object '", $obj->name ); | 
| 296 | 372 | 100 |  |  |  | 873 | } | 
| 297 | 32 | 100 |  |  |  | 132 | return $value; | 
| 298 | 32 |  |  |  |  | 104 | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 | 372 |  |  |  |  | 3316 | return $self->grab(@args)->annotation; | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  |  | 
| 303 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 304 | 0 |  |  |  |  | 0 | return defined $self->parent | 
| 305 |  |  |  |  |  |  | ? $self->parent->grab_root | 
| 306 |  |  |  |  |  |  | : $self; | 
| 307 |  |  |  |  |  |  | } | 
| 308 | 1162 |  |  | 1162 | 1 | 1561 |  | 
| 309 | 1162 | 100 |  |  |  | 4000 | my $self = shift; | 
| 310 |  |  |  |  |  |  | my $class = shift || die "grab_ancestor: missing ancestor class"; | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | return $self if $self->get_type eq 'node' and $self->config_class_name eq $class; | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | return $self->{parent}->grab_ancestor($class) if defined $self->{parent}; | 
| 315 | 9 |  |  | 9 | 1 | 12 | return; | 
| 316 | 9 |  | 50 |  |  | 24 | } | 
| 317 |  |  |  |  |  |  |  | 
| 318 | 9 | 100 | 100 |  |  | 22 | #internal. Used by grab with '?xxx' steps | 
| 319 |  |  |  |  |  |  | my ( $self, $search, $type ) = @_; | 
| 320 | 6 | 50 |  |  |  | 25 |  | 
| 321 | 0 |  |  |  |  | 0 | my $obj = $self; | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | while (1) { | 
| 324 |  |  |  |  |  |  | $logger->debug( | 
| 325 |  |  |  |  |  |  | "grab_ancestor_with_element_named: executing cmd '?$search' on object " . $obj->name ); | 
| 326 | 5 |  |  | 5 | 0 | 28 |  | 
| 327 |  |  |  |  |  |  | my $obj_element_name = $obj->element_name; | 
| 328 | 5 |  |  |  |  | 12 |  | 
| 329 |  |  |  |  |  |  | if (    $obj->isa('Config::Model::Node') | 
| 330 | 5 |  |  |  |  | 10 | and $obj->has_element( name => $search, type => $type ) ) { | 
| 331 | 18 |  |  |  |  | 50 |  | 
| 332 |  |  |  |  |  |  | # object contains the search element, we need to grab the | 
| 333 |  |  |  |  |  |  | # searched object (i.e. the '?foo' part is done | 
| 334 | 18 |  |  |  |  | 149 | return $obj; | 
| 335 |  |  |  |  |  |  | } | 
| 336 | 18 | 100 | 100 |  |  | 120 | elsif ( defined $obj->parent ) { | 
|  |  | 100 |  |  |  |  |  | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | # going up | 
| 339 |  |  |  |  |  |  | $obj = $obj->parent; | 
| 340 |  |  |  |  |  |  | } | 
| 341 | 4 |  |  |  |  | 26 | else { | 
| 342 |  |  |  |  |  |  | # there's no more up to go to... | 
| 343 |  |  |  |  |  |  | Config::Model::Exception::Model->throw( | 
| 344 |  |  |  |  |  |  | object => $self, | 
| 345 |  |  |  |  |  |  | error  => "Error: cannot grab '?$search'" . "from " . $self->name | 
| 346 | 13 |  |  |  |  | 29 | ); | 
| 347 |  |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  | return; # should never be reached... | 
| 350 | 1 |  |  |  |  | 9 | } | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | 1; | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | =pod | 
| 356 | 0 |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | =encoding UTF-8 | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | =head1 NAME | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | Config::Model::Role::Grab - Role to grab data from elsewhere in the tree | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =head1 VERSION | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | version 2.151 | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | $root->grab('foo:2 bar'); | 
| 370 |  |  |  |  |  |  | $root->grab(steps => 'foo:2 bar'); | 
| 371 |  |  |  |  |  |  | $root->grab(steps => 'foo:2 bar', type => 'leaf'); | 
| 372 |  |  |  |  |  |  | $root->grab_value(steps => 'foo:2 bar'); | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | Role used to let a tree item (i.e. node, hash, list or leaf) to grab | 
| 377 |  |  |  |  |  |  | another item or value from the configuration tree using a path (a bit | 
| 378 |  |  |  |  |  |  | like an xpath path with a different syntax). | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | =head1 METHODS | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | =head2 grab | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | Grab an object from the configuration tree. | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | Parameters are: | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | =over | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | =item C<steps> (or C<step>) | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | A string indicating the steps to follow in the tree to find the | 
| 393 |  |  |  |  |  |  | required item. (mandatory) | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | =item C<mode> | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | When set to C<strict>, C<grab> throws an exception if no object is found | 
| 398 |  |  |  |  |  |  | using the passed string. When set to C<adaptative>, the object found last is | 
| 399 |  |  |  |  |  |  | returned. For instance, for the steps C<good_step wrong_step>, only | 
| 400 |  |  |  |  |  |  | the object held by C<good_step> is returned. When set to C<loose>, grab | 
| 401 |  |  |  |  |  |  | returns undef in case of problem. (default is C<strict>) | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | =item C<type> | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | Either C<node>, C<leaf>, C<hash> or C<list> or an array ref containing these | 
| 406 |  |  |  |  |  |  | values. Returns only an object of | 
| 407 |  |  |  |  |  |  | requested type. Depending on C<strict> value, C<grab> either | 
| 408 |  |  |  |  |  |  | throws an exception or returns the last object found with the requested type. | 
| 409 |  |  |  |  |  |  | (optional, default to C<undef>, which means any type of object) | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | Examples: | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | $root->grab(steps => 'foo:2 bar', type => 'leaf') | 
| 414 |  |  |  |  |  |  | $root->grab(steps => 'foo:2 bar', type => ['leaf','check_list']) | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | =item C<autoadd> | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | When set to 1, C<hash> or C<list> configuration element are created | 
| 419 |  |  |  |  |  |  | when requested by the passed steps. (default is 1). | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | =item grab_non_available | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | When set to 1, grab returns an object even if this one is not | 
| 424 |  |  |  |  |  |  | available. I.e. even if this element was warped out. (default is 0). | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =back | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | The C<steps> parameters is made of the following items separated by | 
| 429 |  |  |  |  |  |  | spaces: | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | =over 8 | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | =item - | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | Go up one node | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | =item ! | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | Go to the root node. | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | =item !Foo | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | Go up the configuration tree until the C<Foo> configuration class is found. Raise an exception if | 
| 444 |  |  |  |  |  |  | no C<Foo> class is found when root node is reached. | 
| 445 |  |  |  |  |  |  |  | 
| 446 |  |  |  |  |  |  | =item xxx | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | Go down using C<xxx> element. | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | =item xxx:yy | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | Go down using C<xxx> element and id C<yy> (valid for hash or list elements) | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | =item ?xxx | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | Go up the tree until a node containing element C<xxx> is found. Then go down | 
| 457 |  |  |  |  |  |  | the tree like item C<xxx>. | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | C<?xxx:yy> goes up the tree the same way. But no check is done to see | 
| 460 |  |  |  |  |  |  | if id C<yy> key actually exists or not. Only the element C<xxx> is | 
| 461 |  |  |  |  |  |  | considered when going up the tree. | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | =back | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | =head2 grab_value | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | Like L</grab>, but returns the value of a leaf or check_list object, not | 
| 468 |  |  |  |  |  |  | just the leaf object. | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | C<grab_value> raises an exception if following the steps ends on anything but a | 
| 471 |  |  |  |  |  |  | leaf or a check_list. | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | =head2 grab_annotation | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | Like L</grab>, but returns the annotation of an object. | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | =head2 grab_root | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | Returns the root of the configuration tree. | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | =head2 grab_ancestor | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | Parameter: a configuration class name | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | Go up the configuration tree until a node using the configuration | 
| 486 |  |  |  |  |  |  | class is found. Returns the found node or undef. | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | Example: | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | # returns a Config::Model::Node object for a Systemd::Service config class | 
| 491 |  |  |  |  |  |  | $self->grab('Systemd::Service'); | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | =head1 AUTHOR | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | Dominique Dumont | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | This software is Copyright (c) 2005-2022 by Dominique Dumont. | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | This is free software, licensed under: | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | The GNU Lesser General Public License, Version 2.1, February 1999 | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | =cut |