| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # | 
| 2 |  |  |  |  |  |  | # This file is part of Config-Model | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # This software is Copyright (c) 2005-2022 by Dominique Dumont. | 
| 5 |  |  |  |  |  |  | # | 
| 6 |  |  |  |  |  |  | # This is free software, licensed under: | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | #   The GNU Lesser General Public License, Version 2.1, February 1999 | 
| 9 |  |  |  |  |  |  | # | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | use Mouse; | 
| 12 | 22 |  |  | 1411 |  | 876 |  | 
|  | 22 |  |  |  |  | 48 |  | 
|  | 22 |  |  |  |  | 182 |  | 
| 13 |  |  |  |  |  |  | use Carp qw(cluck croak); | 
| 14 | 22 |  |  | 22 |  | 9815 |  | 
|  | 22 |  |  |  |  | 54 |  | 
|  | 22 |  |  |  |  | 1247 |  | 
| 15 |  |  |  |  |  |  | use Config::Model::Exception; | 
| 16 | 22 |  |  | 22 |  | 124 | use Config::Model::Warper; | 
|  | 22 |  |  |  |  | 41 |  | 
|  | 22 |  |  |  |  | 498 |  | 
| 17 | 22 |  |  | 22 |  | 115 | use Data::Dumper (); | 
|  | 22 |  |  |  |  | 40 |  | 
|  | 22 |  |  |  |  | 464 |  | 
| 18 | 22 |  |  | 22 |  | 114 | use Log::Log4perl qw(get_logger :levels); | 
|  | 22 |  |  |  |  | 46 |  | 
|  | 22 |  |  |  |  | 416 |  | 
| 19 | 22 |  |  | 22 |  | 118 | use Storable qw/dclone/; | 
|  | 22 |  |  |  |  | 41 |  | 
|  | 22 |  |  |  |  | 195 |  | 
| 20 | 22 |  |  | 22 |  | 3013 | use Scalar::Util qw/weaken/; | 
|  | 22 |  |  |  |  | 42 |  | 
|  | 22 |  |  |  |  | 1167 |  | 
| 21 | 22 |  |  | 22 |  | 165 |  | 
|  | 22 |  |  |  |  | 49 |  | 
|  | 22 |  |  |  |  | 8993 |  | 
| 22 |  |  |  |  |  |  | extends qw/Config::Model::AnyThing/; | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | with "Config::Model::Role::NodeLoader"; | 
| 25 |  |  |  |  |  |  | with "Config::Model::Role::Grab"; | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | my $logger = get_logger("Tree::Node::Warped"); | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | # don't authorize to warp 'morph' parameter as it may lead to | 
| 30 |  |  |  |  |  |  | # difficult maintenance | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | # status is not warpable either as an obsolete parameter must stay | 
| 33 |  |  |  |  |  |  | # obsolete | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | my @allowed_warp_params = qw/config_class_name level gist/; | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | has 'backup' => ( is => 'rw', isa => 'HashRef', default => sub { {}; } ); | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | has 'warp'  => ( is => 'rw', isa => 'HashRef', default => sub { {}; }); | 
| 40 |  |  |  |  |  |  | has 'morph' => ( is => 'ro', isa => 'Bool', default => 0 ); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | has warper => ( is => 'rw', isa => 'Config::Model::Warper' ); | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | my @backup_list = @allowed_warp_params; | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | around BUILDARGS => sub { | 
| 47 |  |  |  |  |  |  | my $orig  = shift; | 
| 48 |  |  |  |  |  |  | my $class = shift; | 
| 49 |  |  |  |  |  |  | my %args  = @_; | 
| 50 |  |  |  |  |  |  | my %h     = map { ( $_ => $args{$_} ); } grep { defined $args{$_} } @backup_list; | 
| 51 |  |  |  |  |  |  | return $class->$orig( backup => dclone( \%h ), @_ ); | 
| 52 |  |  |  |  |  |  | }; | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | my $self = shift; | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 129 |  |  | 129 | 1 | 456 | # WarpedNode registers this object in a Value object (the | 
| 57 |  |  |  |  |  |  | # warper).  When the warper gets a new value, it modifies the | 
| 58 |  |  |  |  |  |  | # WarpedNode according to the data passed by the user. | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | my $warp_info = $self->warp; | 
| 61 |  |  |  |  |  |  | $warp_info->{follow} //= {}; | 
| 62 | 129 |  |  |  |  | 552 | $warp_info->{rules}  //= []; | 
| 63 | 129 |  | 50 |  |  | 510 | my $w = Config::Model::Warper->new( | 
| 64 | 129 |  | 50 |  |  | 373 | warped_object => $self, | 
| 65 | 129 |  |  |  |  | 2140 | %$warp_info, | 
| 66 |  |  |  |  |  |  | allowed       => \@allowed_warp_params | 
| 67 |  |  |  |  |  |  | ); | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | $self->warper($w); | 
| 70 |  |  |  |  |  |  | return $self; | 
| 71 | 129 |  |  |  |  | 1219 | } | 
| 72 | 129 |  |  |  |  | 1313 |  | 
| 73 |  |  |  |  |  |  | my $self = shift; | 
| 74 |  |  |  |  |  |  | return $self->parent->config_model; | 
| 75 |  |  |  |  |  |  | } | 
| 76 | 340 |  |  | 340 | 0 | 679 |  | 
| 77 | 340 |  |  |  |  | 2082 | # Forward selected methods (See man perltootc) | 
| 78 |  |  |  |  |  |  | foreach my $method ( | 
| 79 |  |  |  |  |  |  | qw/fetch_element config_class_name copy_from get_element_name | 
| 80 |  |  |  |  |  |  | get_info fetch_gist has_element is_element_available element_type load | 
| 81 |  |  |  |  |  |  | fetch_element_value get_type get_cargo_type dump_tree needs_save | 
| 82 |  |  |  |  |  |  | describe get_help get_help_as_text children get set accept_regexp/ | 
| 83 |  |  |  |  |  |  | ) { | 
| 84 |  |  |  |  |  |  | # to register new methods in package | 
| 85 |  |  |  |  |  |  | no strict "refs"; ## no critic TestingAndDebugging::ProhibitNoStrict | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | *$method = sub { | 
| 88 | 22 |  |  | 22 |  | 151 | my $self = shift; | 
|  | 22 |  |  |  |  | 53 |  | 
|  | 22 |  |  |  |  | 24521 |  | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | if ($self->check) { | 
| 91 | 1423 |  |  | 1423 |  | 2144 | return $self->{data}->$method(@_); | 
| 92 |  |  |  |  |  |  | } | 
| 93 | 1423 | 100 |  |  |  | 2727 |  | 
| 94 | 1422 |  |  |  |  | 5149 | # return undef if no class was warped in | 
| 95 |  |  |  |  |  |  | return ; | 
| 96 |  |  |  |  |  |  | }; | 
| 97 |  |  |  |  |  |  | } | 
| 98 | 1 |  |  |  |  | 3 |  | 
| 99 |  |  |  |  |  |  | my $self = shift; | 
| 100 |  |  |  |  |  |  | return $self->location; | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 2391 |  |  | 2391 | 1 | 3295 | my $self = shift; | 
| 104 | 2391 |  |  |  |  | 11392 | return defined $self->{data} ? 1 : 0; | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | my $self = shift; | 
| 108 | 2 |  |  | 2 | 1 | 5 | $self->check; | 
| 109 | 2 | 50 |  |  |  | 11 | return $self->{data};    # might be undef | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | my $self = shift; | 
| 113 | 261 |  |  | 261 | 1 | 499 | my $check = shift || 'yes '; | 
| 114 | 261 |  |  |  |  | 666 |  | 
| 115 | 261 |  |  |  |  | 594 | # must croak if element is not available | 
| 116 |  |  |  |  |  |  | if ( not defined $self->{data} ) { | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | # a node can be retrieved either for a store operation or for | 
| 119 | 1684 |  |  | 1684 | 0 | 2190 | # a fetch. | 
| 120 | 1684 |  | 50 |  |  | 4584 | if ( $check eq 'yes' ) { | 
| 121 |  |  |  |  |  |  | Config::Model::Exception::User->throw( | 
| 122 |  |  |  |  |  |  | object  => $self, | 
| 123 | 1684 | 100 |  |  |  | 3584 | message => "Object '$self->{element_name}' is not accessible.\n\t" | 
| 124 |  |  |  |  |  |  | . $self->warp_error | 
| 125 |  |  |  |  |  |  | ); | 
| 126 |  |  |  |  |  |  | } | 
| 127 | 1 | 50 |  |  |  | 2 | else { | 
| 128 | 0 |  |  |  |  | 0 | return 0; | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  | return 1; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | my $self = shift; | 
| 135 | 1 |  |  |  |  | 5 |  | 
| 136 |  |  |  |  |  |  | my %args = ( %{ $self->backup }, @_ ); | 
| 137 |  |  |  |  |  |  |  | 
| 138 | 1683 |  |  |  |  | 3215 | # mega cleanup | 
| 139 |  |  |  |  |  |  | for (@allowed_warp_params) { delete $self->{$_} } | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | $logger->trace( $self->name . " set_properties called with ", | 
| 142 | 174 |  |  | 174 | 0 | 393 | Data::Dumper->Dump( [ \%args ], ['set_properties_args'] ) ); | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 174 |  |  |  |  | 298 | my $config_class_name = delete $args{config_class_name}; | 
|  | 174 |  |  |  |  | 938 |  | 
| 145 |  |  |  |  |  |  | my $node_class = delete $args{class} || 'Config::Model::Node'; | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 174 |  |  |  |  | 504 | my @prop_args = ( qw/property level element/, $self->element_name ); | 
|  | 522 |  |  |  |  | 986 |  | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 174 |  |  |  |  | 627 | my $original_level = $self->config_model->get_element_property( | 
| 150 |  |  |  |  |  |  | class => $self->parent->config_class_name, | 
| 151 |  |  |  |  |  |  | @prop_args, | 
| 152 | 174 |  |  |  |  | 7453 | ); | 
| 153 | 174 |  | 50 |  |  | 754 |  | 
| 154 |  |  |  |  |  |  | my $next_level = | 
| 155 | 174 |  |  |  |  | 715 | defined $args{level}       ? $args{level} | 
| 156 |  |  |  |  |  |  | : defined $config_class_name ? $original_level | 
| 157 | 174 |  |  |  |  | 638 | :                              'hidden'; | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | $self->parent->set_element_property( @prop_args, value => $next_level ) | 
| 160 |  |  |  |  |  |  | unless defined $self->index_value; | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | unless ( defined $config_class_name ) { | 
| 163 |  |  |  |  |  |  | $self->clear; | 
| 164 | 174 | 100 |  |  |  | 2581 | return; | 
|  |  | 100 |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  |  | 
| 167 | 174 | 100 |  |  |  | 1268 | my @args; | 
| 168 |  |  |  |  |  |  | ( $config_class_name, @args ) = @$config_class_name | 
| 169 |  |  |  |  |  |  | if ref $config_class_name; | 
| 170 | 174 | 100 |  |  |  | 455 |  | 
| 171 | 8 |  |  |  |  | 23 | # check if some action is needed (ie. create or morph node) | 
| 172 | 8 |  |  |  |  | 27 | return | 
| 173 |  |  |  |  |  |  | if defined $self->{config_class_name} | 
| 174 |  |  |  |  |  |  | and $self->{config_class_name} eq $config_class_name; | 
| 175 | 166 |  |  |  |  | 279 |  | 
| 176 | 166 | 100 |  |  |  | 425 | my $old_object            = $self->{data}; | 
| 177 |  |  |  |  |  |  | my $old_config_class_name = $self->{config_class_name}; | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | # create a new object from scratch | 
| 180 |  |  |  |  |  |  | my $new_object = $self->create_node( $config_class_name, @args ); | 
| 181 |  |  |  |  |  |  |  | 
| 182 | 166 | 50 | 33 |  |  | 510 | $self->{config_class_name} = $config_class_name; | 
| 183 |  |  |  |  |  |  | $self->{data}              = $new_object; | 
| 184 | 166 |  |  |  |  | 300 |  | 
| 185 | 166 |  |  |  |  | 268 | if ( defined $old_object and $self->{morph} ) { | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | # there an old object that we need to translate | 
| 188 | 166 |  |  |  |  | 535 | $logger->debug( "WarpedNode: morphing ", $old_object->name, " to ", $new_object->name ) | 
| 189 |  |  |  |  |  |  | if $logger->is_debug; | 
| 190 | 166 |  |  |  |  | 447 |  | 
| 191 | 166 |  |  |  |  | 363 | $new_object->copy_from( from => $old_object, check => 'skip' ); | 
| 192 |  |  |  |  |  |  | } | 
| 193 | 166 | 100 | 100 |  |  | 558 |  | 
| 194 |  |  |  |  |  |  | # bringing a new object does not really modify the content of the config tree. | 
| 195 |  |  |  |  |  |  | # only changes underneath changes the tree. And these changes below triggers | 
| 196 | 24 | 50 |  |  |  | 99 | # their own change notif. So there's no need to call notify_change when transitioning | 
| 197 |  |  |  |  |  |  | # from an undef object into a real object. On the other hand, warping out an object does | 
| 198 |  |  |  |  |  |  | # NOT trigger notify_changes from below. So notify_change must be called | 
| 199 | 24 |  |  |  |  | 238 | if ( defined $old_object and $old_config_class_name) { | 
| 200 |  |  |  |  |  |  | my $from = $old_config_class_name ; | 
| 201 |  |  |  |  |  |  | my $to   = $config_class_name     // '<undef>'; | 
| 202 |  |  |  |  |  |  | $self->notify_change( note => "warped node from $from to $to" ); | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | # need to call trigger on all registered objects only after all is setup | 
| 206 |  |  |  |  |  |  | $self->trigger_warp; | 
| 207 | 166 | 50 | 66 |  |  | 618 | } | 
| 208 | 0 |  |  |  |  | 0 |  | 
| 209 | 0 |  | 0 |  |  | 0 | my $self              = shift; | 
| 210 | 0 |  |  |  |  | 0 | my $config_class_name = shift; | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | my @args = ( | 
| 213 |  |  |  |  |  |  | config_class_name => $config_class_name, | 
| 214 | 166 |  |  |  |  | 2152 | instance          => $self->{instance}, | 
| 215 |  |  |  |  |  |  | element_name      => $self->{element_name}, | 
| 216 |  |  |  |  |  |  | parent            => $self->parent, | 
| 217 |  |  |  |  |  |  | container         => $self->container, | 
| 218 | 166 |  |  | 166 | 0 | 304 | ); | 
| 219 | 166 |  |  |  |  | 259 |  | 
| 220 |  |  |  |  |  |  | push @args, index_value => $self->index_value if defined $self->index_value; | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | return $self->load_node(@args); | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 166 |  |  |  |  | 1428 | my $self = shift; | 
| 226 |  |  |  |  |  |  | delete $self->{data}; | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 166 | 100 |  |  |  | 674 | my $self  = shift; | 
| 230 |  |  |  |  |  |  | my %args  = @_ > 1 ? @_ : ( data => shift ); | 
| 231 | 166 |  |  |  |  | 701 | my $data  = $args{data}; | 
| 232 |  |  |  |  |  |  | my $check = $self->_check_check( $args{check} ); | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | if ( ref($data) ne 'HASH' ) { | 
| 235 | 8 |  |  | 8 | 0 | 27 | Config::Model::Exception::LoadData->throw( | 
| 236 | 8 |  |  |  |  | 84 | object     => $self, | 
| 237 |  |  |  |  |  |  | message    => "load_data called with non hash ref arg", | 
| 238 |  |  |  |  |  |  | wrong_data => $data, | 
| 239 |  |  |  |  |  |  | ); | 
| 240 | 4 |  |  | 4 | 1 | 8 | } | 
| 241 | 4 | 50 |  |  |  | 15 |  | 
| 242 | 4 |  |  |  |  | 12 | $self->get_actual_node->load_data(%args); | 
| 243 | 4 |  |  |  |  | 15 |  | 
| 244 |  |  |  |  |  |  | } | 
| 245 | 4 | 50 |  |  |  | 15 |  | 
| 246 | 0 |  |  |  |  | 0 | my $self = shift; | 
| 247 |  |  |  |  |  |  | $self->get_actual_node->is_auto_write_for_type(@_); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | # register warper that goes through this path when looking for warp master value | 
| 251 |  |  |  |  |  |  | my ( $self, $warped, $w_idx ) = @_; | 
| 252 |  |  |  |  |  |  |  | 
| 253 | 4 |  |  |  |  | 11 | $logger->debug( "WarpedNode: " . $self->name, " registered " . $warped->name ); | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | # weaken only applies to the passed reference, and there's no way | 
| 256 |  |  |  |  |  |  | # to duplicate a weak ref. Only a strong ref is created. See | 
| 257 |  |  |  |  |  |  | #  qw(weaken) module for weaken() | 
| 258 | 2 |  |  | 2 | 0 | 5 | my @tmp = ( $warped, $w_idx ); | 
| 259 | 2 |  |  |  |  | 9 | weaken( $tmp[0] ); | 
| 260 |  |  |  |  |  |  | push @{ $self->{warp_these_objects} }, \@tmp; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | my $self = shift; | 
| 264 | 124 |  |  | 124 | 0 | 322 |  | 
| 265 |  |  |  |  |  |  | # warp_these_objects is modified by the calls below, so this copy | 
| 266 | 124 |  |  |  |  | 271 | # must be done before the loop | 
| 267 |  |  |  |  |  |  | my @list = @{ $self->{warp_these_objects} || [] }; | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | foreach my $ref (@list) { | 
| 270 |  |  |  |  |  |  | my ( $warped, $warp_index ) = @$ref; | 
| 271 | 124 |  |  |  |  | 958 | next unless defined $warped;    # $warped is a weak ref and may vanish | 
| 272 | 124 |  |  |  |  | 572 |  | 
| 273 | 124 |  |  |  |  | 199 | # pure warp of object | 
|  | 124 |  |  |  |  | 616 |  | 
| 274 |  |  |  |  |  |  | $logger->debug( "node trigger_warp: from '", | 
| 275 |  |  |  |  |  |  | $self->name, "' warping '", $warped->name, "'" ); | 
| 276 |  |  |  |  |  |  |  | 
| 277 | 166 |  |  | 166 | 0 | 289 | # FIXME: this does not trigger new registration (or removal thereof)... | 
| 278 |  |  |  |  |  |  | $warped->refresh_affected_registrations( $self->location ); | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | #$warped->refresh_values_from_master ; | 
| 281 | 166 | 100 |  |  |  | 259 | $warped->do_warp; | 
|  | 166 |  |  |  |  | 778 |  | 
| 282 |  |  |  |  |  |  | $logger->debug( "node trigger_warp: from '", | 
| 283 | 166 |  |  |  |  | 2399 | $self->name, "' warping '", $warped->name, "' done" ); | 
| 284 | 7 |  |  |  |  | 36 | } | 
| 285 | 7 | 50 |  |  |  | 10 | } | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | # FIXME: should we un-register ??? | 
| 288 | 7 |  |  |  |  | 14 |  | 
| 289 |  |  |  |  |  |  | 1; | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | # ABSTRACT: Node that change config class properties | 
| 292 | 7 |  |  |  |  | 54 |  | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | =pod | 
| 295 | 7 |  |  |  |  | 24 |  | 
| 296 | 7 |  |  |  |  | 20 | =encoding UTF-8 | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | =head1 NAME | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | Config::Model::WarpedNode - Node that change config class properties | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | =head1 VERSION | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | version 2.152 | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | use Config::Model; | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | my $model = Config::Model->new; | 
| 311 |  |  |  |  |  |  | foreach (qw/X Y/) { | 
| 312 |  |  |  |  |  |  | $model->create_config_class( | 
| 313 |  |  |  |  |  |  | name    => "Class$_", | 
| 314 |  |  |  |  |  |  | element => [ foo => {qw/type leaf value_type string/} ] | 
| 315 |  |  |  |  |  |  | ); | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  | $model->create_config_class( | 
| 318 |  |  |  |  |  |  | name => "MyClass", | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | element => [ | 
| 321 |  |  |  |  |  |  | master_switch => { | 
| 322 |  |  |  |  |  |  | type       => 'leaf', | 
| 323 |  |  |  |  |  |  | value_type => 'enum', | 
| 324 |  |  |  |  |  |  | choice     => [qw/cX cY/] | 
| 325 |  |  |  |  |  |  | }, | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | 'a_warped_node' => { | 
| 328 |  |  |  |  |  |  | type   => 'warped_node', | 
| 329 |  |  |  |  |  |  | warp => } | 
| 330 |  |  |  |  |  |  | follow => { ms => '! master_switch' }, | 
| 331 |  |  |  |  |  |  | rules  => [ | 
| 332 |  |  |  |  |  |  | '$ms eq "cX"' => { config_class_name => 'ClassX' }, | 
| 333 |  |  |  |  |  |  | '$ms eq "cY"' => { config_class_name => 'ClassY' }, | 
| 334 |  |  |  |  |  |  | ] | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  | }, | 
| 337 |  |  |  |  |  |  | ], | 
| 338 |  |  |  |  |  |  | ); | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | my $inst = $model->instance(root_class_name => 'MyClass' ); | 
| 341 |  |  |  |  |  |  | my $root = $inst->config_root ; | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | print "Visible elements: ",join(' ',$root->get_element_name),"\n" ; | 
| 344 |  |  |  |  |  |  | # Visible elements: master_switch | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | $root->load( steps => 'master_switch=cX' ); | 
| 347 |  |  |  |  |  |  | print "Visible elements: ",join(' ',$root->get_element_name),"\n" ; | 
| 348 |  |  |  |  |  |  | # Visible elements: master_switch a_warped_node | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | my $node = $root->grab('a_warped_node') ; | 
| 351 |  |  |  |  |  |  | print "a_warped_node class: ",$node->config_class_name,"\n" ; | 
| 352 |  |  |  |  |  |  | # a_warped_node class: ClassX | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | $root->load( steps => 'master_switch=cY' ); | 
| 355 |  |  |  |  |  |  | print "a_warped_node class: ",$node->config_class_name,"\n" ; | 
| 356 |  |  |  |  |  |  | # a_warped_node class: ClassY | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | This class provides a way to change dynamically the configuration | 
| 361 |  |  |  |  |  |  | class (or some other properties) of a node. The changes are done | 
| 362 |  |  |  |  |  |  | according to the model declaration. | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | This declaration specifies one (or several) leaf in the | 
| 365 |  |  |  |  |  |  | configuration tree that triggers the actual property change of the | 
| 366 |  |  |  |  |  |  | warped node. This leaf is also referred as I<warp master>. | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | When the warp master(s) value(s) changes, C<WarpedNode> creates an instance | 
| 369 |  |  |  |  |  |  | of the new class required by the warp master. | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | If the morph parameter is set, the values held by the old object are | 
| 372 |  |  |  |  |  |  | (if possible) copied to the new instance of the object using | 
| 373 |  |  |  |  |  |  | L<copy_from|Config::Model::Node/"copy_from ( another_node_object )"> | 
| 374 |  |  |  |  |  |  | method. | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | Warped node can alter the following properties: | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | config_class_name | 
| 379 |  |  |  |  |  |  | level | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | =head1 Constructor | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | C<WarpedNode> should not be created directly. | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | =head1 Warped node model declaration | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | =head2 Parameter overview | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | A warped node must be declared with the following parameters: | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | =over | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | =item type | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | Always set to C<warped_node>. | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | =item follow | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | L<Grab string|Config::Model::Role::Grab/grab"> leading to the | 
| 400 |  |  |  |  |  |  | C<Config::Model::Value> warp master. | 
| 401 |  |  |  |  |  |  | See L<Config::Model::Warper/"Warp follow argument"> for details. | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | =item morph | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | boolean. If 1, C<WarpedNode> tries to recursively copy the value from | 
| 406 |  |  |  |  |  |  | the old object to the new object using | 
| 407 |  |  |  |  |  |  | L<copy_from method|Config::Model::Node/"copy_from ( another_node_object )">. | 
| 408 |  |  |  |  |  |  | When a copy is not possible, undef values | 
| 409 |  |  |  |  |  |  | are assigned to object elements. | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | =item rules | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | Hash or array ref that specify the property change rules according to the | 
| 414 |  |  |  |  |  |  | warp master(s) value(s). | 
| 415 |  |  |  |  |  |  | See L<Config::Model::Warper/"Warp rules argument"> for details | 
| 416 |  |  |  |  |  |  | on how to specify the warp master values (or combination of values). | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | =back | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | =head2 Effect declaration | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | For a warped node, the effects are declared with these parameters: | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | =over 8 | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =item B<config_class_name> | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | When requested by the warp master,the C<WarpedNode> creates a new | 
| 429 |  |  |  |  |  |  | object of the type specified by this parameter: | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | XZ => { config_class_name => 'SlaveZ' } | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | Instead of a string, you can an array ref which contains the class | 
| 434 |  |  |  |  |  |  | name and constructor arguments : | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | XY  => { config_class_name => ['SlaveY', foo => 'bar' ], }, | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | =item B<class> | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | Specify a Perl class to implement the above config class. This Perl Class B<must> inherit | 
| 441 |  |  |  |  |  |  | L<Config::Model::Node>. | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | =back | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | =head1 Forwarded methods | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | The following methods are forwarded to contained node: | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | fetch_element config_class_name get_element_name has_element | 
| 450 |  |  |  |  |  |  | is_element_available element_type load fetch_element_value get_type | 
| 451 |  |  |  |  |  |  | get_cargo_type describe | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | =head1 Methods | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | =head2 name | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | Return the name of the node (even if warped out). | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | =head2 is_accessible | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | Returns true if the node hidden behind this warped node is accessible, | 
| 462 |  |  |  |  |  |  | i.e. the warp master have values so a node was warped in. | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | =head2 get_actual_node | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | Returns the node object hidden behind the warped node. Croaks if the | 
| 467 |  |  |  |  |  |  | node is not accessible. | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | =head2 load_data | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | Parameters: C<< ( hash_ref ) >> | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | Load configuration data with a hash ref. The hash ref key must match | 
| 474 |  |  |  |  |  |  | the available elements of the node carried by the warped node. | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | =head1 EXAMPLE | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | $model ->create_config_class | 
| 479 |  |  |  |  |  |  | ( | 
| 480 |  |  |  |  |  |  | element => | 
| 481 |  |  |  |  |  |  | [ | 
| 482 |  |  |  |  |  |  | tree_macro => { type => 'leaf', | 
| 483 |  |  |  |  |  |  | value_type => 'enum', | 
| 484 |  |  |  |  |  |  | choice     => [qw/XX XY XZ ZZ/] | 
| 485 |  |  |  |  |  |  | }, | 
| 486 |  |  |  |  |  |  | bar =>  { | 
| 487 |  |  |  |  |  |  | type => 'warped_node', | 
| 488 |  |  |  |  |  |  | follow => '! tree_macro', | 
| 489 |  |  |  |  |  |  | morph => 1, | 
| 490 |  |  |  |  |  |  | rules => [ | 
| 491 |  |  |  |  |  |  | XX => { config_class_name | 
| 492 |  |  |  |  |  |  | => [ 'ClassX', 'foo' ,'bar' ]} | 
| 493 |  |  |  |  |  |  | XY => { config_class_name => 'ClassY'}, | 
| 494 |  |  |  |  |  |  | XZ => { config_class_name => 'ClassZ'} | 
| 495 |  |  |  |  |  |  | ] | 
| 496 |  |  |  |  |  |  | } | 
| 497 |  |  |  |  |  |  | ] | 
| 498 |  |  |  |  |  |  | ); | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | In the example above we see that: | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | =over | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | =item * | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | The 'bar' slot can refer to a C<ClassX>, C<ClassZ> or C<ClassY> object. | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | =item * | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | The warper object is the C<tree_macro> attribute of the root of the | 
| 511 |  |  |  |  |  |  | object tree. | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | =item * | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | When C<tree_macro> is set to C<ZZ>, C<bar> is not available. Trying to | 
| 516 |  |  |  |  |  |  | access C<bar> raises an exception. | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | =item * | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | When C<tree_macro> is changed from C<ZZ> to C<XX>, | 
| 521 |  |  |  |  |  |  | C<bar> refers to a brand new C<ClassX> | 
| 522 |  |  |  |  |  |  | object constructed with C<< ClassX->new(foo => 'bar') >> | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | =item * | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | Then, if C<tree_macro> is changed from C<XX> to C<XY>, C<bar> | 
| 527 |  |  |  |  |  |  | refers to a brand new C<ClassY> object. But in this case, the object is | 
| 528 |  |  |  |  |  |  | initialized with most if not all the attributes of C<ClassX>. This copy | 
| 529 |  |  |  |  |  |  | is done whenever C<tree_macro> is changed. | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | =back | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | =head1 AUTHOR | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | Dominique Dumont, (ddumont at cpan dot org) | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | L<Config::Model::Instance>, | 
| 540 |  |  |  |  |  |  | L<Config::Model>, | 
| 541 |  |  |  |  |  |  | L<Config::Model::HashId>, | 
| 542 |  |  |  |  |  |  | L<Config::Model::ListId>, | 
| 543 |  |  |  |  |  |  | L<Config::Model::AnyThing>, | 
| 544 |  |  |  |  |  |  | L<Config::Model::Warper>, | 
| 545 |  |  |  |  |  |  | L<Config::Model::WarpedNode>, | 
| 546 |  |  |  |  |  |  | L<Config::Model::Value> | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | =head1 AUTHOR | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | Dominique Dumont | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | This software is Copyright (c) 2005-2022 by Dominique Dumont. | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | This is free software, licensed under: | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | The GNU Lesser General Public License, Version 2.1, February 1999 | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | =cut |