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