| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Games::Object::Manager; |
|
2
|
|
|
|
|
|
|
|
|
3
|
9
|
|
|
9
|
|
40575
|
use strict; |
|
|
9
|
|
|
|
|
17
|
|
|
|
9
|
|
|
|
|
301
|
|
|
4
|
9
|
|
|
9
|
|
46
|
use Exporter; |
|
|
9
|
|
|
|
|
18
|
|
|
|
9
|
|
|
|
|
527
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
9
|
|
|
9
|
|
47
|
use Carp qw(carp croak confess); |
|
|
9
|
|
|
|
|
14
|
|
|
|
9
|
|
|
|
|
511
|
|
|
7
|
9
|
|
|
9
|
|
1959
|
use IO::File; |
|
|
9
|
|
|
|
|
31418
|
|
|
|
9
|
|
|
|
|
1724
|
|
|
8
|
9
|
|
|
9
|
|
2128
|
use Games::Object::Common qw(FetchParams LoadData SaveData ANAME_MANAGER); |
|
|
9
|
|
|
|
|
19
|
|
|
|
9
|
|
|
|
|
715
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
9
|
|
|
9
|
|
64
|
use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS @ISA); |
|
|
9
|
|
|
|
|
16
|
|
|
|
9
|
|
|
|
|
1103
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
$VERSION = "0.11"; |
|
13
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
|
14
|
|
|
|
|
|
|
@EXPORT_OK = qw($CompareFunction REL_NO_CIRCLE); |
|
15
|
|
|
|
|
|
|
%EXPORT_TAGS = ( |
|
16
|
|
|
|
|
|
|
flags => [ qw(REL_NO_CIRCLE) ], |
|
17
|
|
|
|
|
|
|
variables => [ qw($CompareFunction) ], |
|
18
|
|
|
|
|
|
|
); |
|
19
|
|
|
|
|
|
|
|
|
20
|
9
|
|
|
9
|
|
49
|
use vars qw($CompareFunction); |
|
|
9
|
|
|
|
|
14
|
|
|
|
9
|
|
|
|
|
351
|
|
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Define flags. |
|
23
|
9
|
|
|
9
|
|
46
|
use constant REL_NO_CIRCLE => 0x00000001; # Don't allow cir. relates |
|
|
9
|
|
|
|
|
16
|
|
|
|
9
|
|
|
|
|
2148
|
|
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# Define the comparison function to use for processing order. |
|
26
|
|
|
|
|
|
|
$CompareFunction = '_CompareDefault'; |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# Define the default process info. |
|
29
|
|
|
|
|
|
|
my @ProcessList = ( |
|
30
|
|
|
|
|
|
|
'process_queue', |
|
31
|
|
|
|
|
|
|
'process_pmod', |
|
32
|
|
|
|
|
|
|
'process_tend_to', |
|
33
|
|
|
|
|
|
|
); |
|
34
|
|
|
|
|
|
|
my $ProcessLimit = 100; |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
#### |
|
37
|
|
|
|
|
|
|
## INTERNAL FUNCTIONS |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# Default comparison function when determining the order of processing of |
|
40
|
|
|
|
|
|
|
# two objects. |
|
41
|
|
|
|
|
|
|
|
|
42
|
27
|
|
|
27
|
|
88
|
sub _CompareDefault { $b->priority() <=> $a->priority() } |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Comparison function when using the creation order option |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub _CompareAddOrder { |
|
47
|
0
|
|
|
0
|
|
0
|
my $cmp = $b->priority() <=> $a->priority(); |
|
48
|
0
|
0
|
|
|
|
0
|
$cmp == 0 ? $a->order() <=> $b->order() : $cmp; |
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# Create a relation methods |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub _CreateRelators |
|
54
|
|
|
|
|
|
|
{ |
|
55
|
15
|
|
|
15
|
|
75
|
my %args = @_; |
|
56
|
15
|
|
|
|
|
34
|
my $realname = $args{name}; |
|
57
|
15
|
|
|
|
|
28
|
my $name = $args{relate_method}; |
|
58
|
15
|
|
|
|
|
27
|
my $uname = $args{unrelate_method}; |
|
59
|
15
|
|
|
|
|
25
|
my $rname = $args{related_method}; |
|
60
|
15
|
|
|
|
|
30
|
my $iname = $args{is_related_method}; |
|
61
|
15
|
|
|
|
|
35
|
my $lname = $args{related_list_method}; |
|
62
|
|
|
|
|
|
|
|
|
63
|
9
|
|
|
9
|
|
54
|
no strict 'refs'; |
|
|
9
|
|
|
|
|
14
|
|
|
|
9
|
|
|
|
|
43463
|
|
|
64
|
|
|
|
|
|
|
*$name = sub { |
|
65
|
27
|
|
|
27
|
|
1393
|
my $man = shift; |
|
66
|
27
|
50
|
|
|
|
106
|
my $args = ( ref($_[$#_]) eq 'HASH' ? pop @_ : {} ); |
|
67
|
27
|
|
|
|
|
158
|
$man->relate(how => $realname, |
|
68
|
|
|
|
|
|
|
self => $_[0], |
|
69
|
|
|
|
|
|
|
object => $_[1], |
|
70
|
|
|
|
|
|
|
other => $_[2], |
|
71
|
|
|
|
|
|
|
args => $args); |
|
72
|
15
|
100
|
|
|
|
222
|
} if (!defined(&$name)); |
|
73
|
|
|
|
|
|
|
*$uname = sub { |
|
74
|
8
|
|
|
8
|
|
16
|
my $man = shift; |
|
75
|
8
|
50
|
|
|
|
35
|
my $args = ( ref($_[$#_]) eq 'HASH' ? pop @_ : {} ); |
|
76
|
8
|
|
|
|
|
48
|
$man->unrelate(how => $realname, |
|
77
|
|
|
|
|
|
|
object => $_[0], |
|
78
|
|
|
|
|
|
|
other => $_[1], |
|
79
|
|
|
|
|
|
|
args => $args); |
|
80
|
15
|
100
|
|
|
|
8718
|
} if (!defined(&$uname)); |
|
81
|
|
|
|
|
|
|
*$rname = sub { |
|
82
|
161
|
|
|
161
|
|
861
|
my $man = shift; |
|
83
|
161
|
|
|
|
|
470
|
$man->related(how => $realname, object => $_[0]); |
|
84
|
15
|
100
|
|
|
|
124
|
} if (!defined(&$rname)); |
|
85
|
|
|
|
|
|
|
*$iname = sub { |
|
86
|
8
|
|
|
8
|
|
44
|
my $man = shift; |
|
87
|
8
|
|
|
|
|
25
|
$man->is_related(how => $realname, self => $_[0], object => $_[1]); |
|
88
|
15
|
100
|
|
|
|
131
|
} if (!defined(&$iname)); |
|
89
|
|
|
|
|
|
|
*$lname = sub { |
|
90
|
4
|
|
|
4
|
|
10
|
my $man = shift; |
|
91
|
4
|
|
|
|
|
18
|
$man->related_list(how => $realname, self => $_[0]); |
|
92
|
15
|
100
|
|
|
|
183
|
} if (!defined(&$lname)); |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
#### |
|
96
|
|
|
|
|
|
|
## CONSTRUCTOR |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# Basic constructor |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub new |
|
101
|
|
|
|
|
|
|
{ |
|
102
|
8
|
|
|
8
|
0
|
1043
|
my $proto = shift; |
|
103
|
8
|
|
33
|
|
|
78
|
my $class = ref($proto) || $proto; |
|
104
|
8
|
|
|
|
|
22
|
my $man = {}; |
|
105
|
8
|
|
|
|
|
25
|
my %args = (); |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# Fetch parameters. |
|
108
|
8
|
|
|
|
|
115
|
FetchParams(\@_, \%args, [ |
|
109
|
|
|
|
|
|
|
[ 'opt', 'base_id', 0, 'int' ], |
|
110
|
|
|
|
|
|
|
[ 'opt', 'process_list', \@ProcessList, 'arrayref' ], |
|
111
|
|
|
|
|
|
|
[ 'opt', 'process_limit', $ProcessLimit, 'int' ], |
|
112
|
|
|
|
|
|
|
] ); |
|
113
|
8
|
|
|
|
|
38
|
bless $man, $class; |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# Define storage for created objects. Note that this means that objects |
|
116
|
|
|
|
|
|
|
# will be persistent. They can go out of scope and still exist, since |
|
117
|
|
|
|
|
|
|
# each is identified by a unique ID. |
|
118
|
8
|
|
|
|
|
95
|
$man->{index} = {}; |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Define tables that handle object relationships |
|
121
|
8
|
|
|
|
|
23
|
$man->{relation_def} = {}; |
|
122
|
8
|
|
|
|
|
66
|
$man->{relate_to} = {}; |
|
123
|
8
|
|
|
|
|
22
|
$man->{relate_from} = {}; |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# Define a counter for creating objects when the user wants us to |
|
126
|
|
|
|
|
|
|
# assume that every new object is unique. The starting number can be |
|
127
|
|
|
|
|
|
|
# changed with base_id() but only if no objects have been created yet. |
|
128
|
8
|
|
|
|
|
30
|
$man->{next} = $args{base_id}; |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# Define a counter that will be used to track the order in which objects |
|
131
|
|
|
|
|
|
|
# are created. This is to support a new feature in v0.05 |
|
132
|
8
|
|
|
|
|
21
|
$man->{order} = 0; |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# And if we are doing this, we want to try and use space efficiently by |
|
135
|
|
|
|
|
|
|
# reclaiming unused IDs. Thus we track the lowest available opening. |
|
136
|
|
|
|
|
|
|
# [ NOT YET IMPLEMENTED ] |
|
137
|
8
|
|
|
|
|
20
|
$man->{reclaim} = 1; |
|
138
|
8
|
|
|
|
|
36
|
$man->{avail} = 0; |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# Track the highest priority object. |
|
141
|
8
|
|
|
|
|
18
|
$man->{highest_pri} = 0; |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# Define a table that shows what order process() is supposed to do |
|
144
|
|
|
|
|
|
|
# things. |
|
145
|
8
|
|
|
|
|
19
|
$man->{process_list} = $args{process_list}; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# Define a limit to how many times the same item can be processed in a |
|
148
|
|
|
|
|
|
|
# queue (see process_queue() for details) |
|
149
|
8
|
|
|
|
|
32
|
$man->{process_limit} = $args{process_limit}; |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# Set the default inherit_from relationship. |
|
152
|
8
|
|
|
|
|
67
|
$man->define_relation( |
|
153
|
|
|
|
|
|
|
name => 'inherit', |
|
154
|
|
|
|
|
|
|
relate_method => 'inherit', |
|
155
|
|
|
|
|
|
|
unrelate_method => 'disinherit', |
|
156
|
|
|
|
|
|
|
related_method => 'inheriting_from', |
|
157
|
|
|
|
|
|
|
related_list_method => 'has_inherting', |
|
158
|
|
|
|
|
|
|
is_related_method => 'is_inheriting_from', |
|
159
|
|
|
|
|
|
|
flags => REL_NO_CIRCLE, |
|
160
|
|
|
|
|
|
|
); |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# Done. |
|
163
|
8
|
|
|
|
|
33
|
$man; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Constructor for loading entire container from a file. |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub load |
|
169
|
|
|
|
|
|
|
{ |
|
170
|
1
|
|
|
1
|
0
|
3
|
my $proto = shift; |
|
171
|
1
|
|
33
|
|
|
8
|
my $class = ref($proto) || $proto; |
|
172
|
1
|
|
|
|
|
2
|
my $file = shift; |
|
173
|
1
|
|
|
|
|
2
|
my $filename; |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# If we got a filename instead of a file object, open the file. |
|
176
|
1
|
50
|
|
|
|
5
|
if (!ref($file)) { |
|
177
|
1
|
|
|
|
|
2
|
$filename = $file; |
|
178
|
1
|
|
|
|
|
10
|
$file = IO::File->new(); |
|
179
|
1
|
50
|
|
|
|
67
|
$file->open("<$filename") or |
|
180
|
|
|
|
|
|
|
croak "Unable to open manager file '$filename'"; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# Initialize the object. |
|
184
|
1
|
|
|
|
|
54
|
my $man; |
|
185
|
1
|
50
|
|
|
|
5
|
if (ref($proto)) { |
|
186
|
|
|
|
|
|
|
# This is a "load in place", meaning we're reloading to an |
|
187
|
|
|
|
|
|
|
# existing object, so clear out the old stuff. |
|
188
|
0
|
|
|
|
|
0
|
$man = $proto; |
|
189
|
0
|
|
|
|
|
0
|
foreach my $key (keys %$man) { delete $man->{$key}; } |
|
|
0
|
|
|
|
|
0
|
|
|
190
|
|
|
|
|
|
|
} else { |
|
191
|
|
|
|
|
|
|
# Totally new object originating from the file. |
|
192
|
1
|
|
|
|
|
4
|
$man = {}; |
|
193
|
1
|
|
|
|
|
4
|
bless $man, $class; |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Check the header to make sure this is manager data. |
|
197
|
1
|
|
|
|
|
43
|
my $line = <$file>; chomp $line; |
|
|
1
|
|
|
|
|
4
|
|
|
198
|
1
|
50
|
|
|
|
6
|
croak "Did not find manager header data in file" |
|
199
|
|
|
|
|
|
|
if ($line ne 'OBJ:__MANAGER__'); |
|
200
|
1
|
|
|
|
|
2
|
$line = <$file>; chomp $line; |
|
|
1
|
|
|
|
|
2
|
|
|
201
|
1
|
50
|
|
|
|
7
|
croak "Second line of manager data header bad" |
|
202
|
|
|
|
|
|
|
if ($line !~ /^CL:(.+)$/); |
|
203
|
1
|
|
|
|
|
3
|
my $mclass = $1; |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Load. |
|
206
|
1
|
|
|
|
|
6
|
LoadData($file, $man); |
|
207
|
1
|
50
|
|
|
|
19
|
$file->close() if defined($filename); |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# Restore manager attributes to all objects. |
|
210
|
1
|
|
|
|
|
29
|
foreach my $obj (values %{$man->{index}}) { $obj->manager($man); } |
|
|
1
|
|
|
|
|
6
|
|
|
|
8
|
|
|
|
|
26
|
|
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# Restore relators. |
|
213
|
1
|
|
|
|
|
3
|
foreach my $rel (values %{$man->{relation_def}}) { |
|
|
1
|
|
|
|
|
6
|
|
|
214
|
4
|
|
|
|
|
28
|
_CreateRelators(%$rel); |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# Done. |
|
218
|
1
|
|
|
|
|
6
|
bless $man, $mclass; |
|
219
|
1
|
|
|
|
|
10
|
$man; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
#### |
|
223
|
|
|
|
|
|
|
## MANAGER DATA METHODS |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# Save the manager and its contents to a file. |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub save |
|
228
|
|
|
|
|
|
|
{ |
|
229
|
1
|
|
|
1
|
0
|
2
|
my $man = shift; |
|
230
|
1
|
|
|
|
|
2
|
my $file = shift; |
|
231
|
1
|
|
|
|
|
2
|
my $filename; |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# If we got a filename instead of a file object, open the file. |
|
234
|
1
|
50
|
|
|
|
5
|
if (!ref($file)) { |
|
235
|
1
|
|
|
|
|
2
|
$filename = $file; |
|
236
|
1
|
|
|
|
|
15
|
$file = IO::File->new(); |
|
237
|
1
|
50
|
|
|
|
55
|
$file->open(">$filename") or |
|
238
|
|
|
|
|
|
|
croak "Unable to open manager file '$filename'"; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# Save header. This indicates that this is indeed manager object |
|
242
|
|
|
|
|
|
|
# data and preserves the class. |
|
243
|
1
|
|
|
|
|
174
|
print $file "OBJ:__MANAGER__\n" . |
|
244
|
|
|
|
|
|
|
"CL:" . ref($man) . "\n"; |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# Save data. See the comments on the save() routine in Games::Object |
|
247
|
|
|
|
|
|
|
# for why we copy the ref to an ordinary hash first. |
|
248
|
1
|
|
|
|
|
24
|
my %hash = %$man; |
|
249
|
1
|
|
|
|
|
6
|
SaveData($file, \%hash); |
|
250
|
1
|
50
|
|
|
|
17
|
$file->close() if (defined($filename)); |
|
251
|
1
|
|
|
|
|
117
|
1; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# "Find" an object (i.e. look up its ID). If given something that is |
|
255
|
|
|
|
|
|
|
# already an object, validates that the object is still valid. If the |
|
256
|
|
|
|
|
|
|
# assertion flag is passed, an invalid object will result in a fatal error. |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub find |
|
259
|
|
|
|
|
|
|
{ |
|
260
|
412
|
|
|
412
|
0
|
1395
|
my ($man, $id, $assert) = @_; |
|
261
|
|
|
|
|
|
|
|
|
262
|
412
|
100
|
|
|
|
853
|
if (!defined($id)) { |
|
263
|
26
|
50
|
|
|
|
45
|
if ($assert) { |
|
264
|
0
|
|
|
|
|
0
|
confess "Assertion failed: ID is undefined"; |
|
265
|
|
|
|
|
|
|
} else { |
|
266
|
26
|
|
|
|
|
163
|
return undef; |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
} |
|
269
|
386
|
50
|
33
|
|
|
910
|
$id = $id->id() if (ref($id) && UNIVERSAL::isa($id, 'Games::Object')); |
|
270
|
386
|
100
|
|
|
|
893
|
if (defined($man->{index}{$id})) { |
|
|
|
100
|
|
|
|
|
|
|
271
|
381
|
|
|
|
|
1433
|
$man->{index}{$id}; |
|
272
|
|
|
|
|
|
|
} elsif ($assert) { |
|
273
|
|
|
|
|
|
|
# Report with confess() so user can see where the assertion was made |
|
274
|
1
|
|
|
|
|
135
|
confess "Assertion failed: '$id' is not a valid/managed object ID"; |
|
275
|
|
|
|
|
|
|
} else { |
|
276
|
4
|
|
|
|
|
21
|
undef; |
|
277
|
|
|
|
|
|
|
} |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Return the number of objects in the manager. |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub total_objects { |
|
283
|
0
|
|
|
0
|
0
|
0
|
my $man = shift; |
|
284
|
0
|
|
|
|
|
0
|
scalar keys %{$man->{index}}; |
|
|
0
|
|
|
|
|
0
|
|
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# Returns the ID of an object, with the side effect that it validates that |
|
288
|
|
|
|
|
|
|
# this object is really a Games::Object derivative and is being managed by |
|
289
|
|
|
|
|
|
|
# this manager. The user specifies either the ID or the object ref. If valid, |
|
290
|
|
|
|
|
|
|
# the ID is always returned (thus it can be used to guarantee the return of |
|
291
|
|
|
|
|
|
|
# an ID when you're not sure if you were passed an object or the ID). |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
sub id |
|
294
|
|
|
|
|
|
|
{ |
|
295
|
359
|
|
|
359
|
0
|
791
|
my ($man, $obj, $assert) = @_; |
|
296
|
|
|
|
|
|
|
|
|
297
|
359
|
100
|
66
|
|
|
1965
|
if (ref($obj) && UNIVERSAL::isa($obj, 'Games::Object')) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
298
|
325
|
|
|
|
|
960
|
my $id = $obj->id(); |
|
299
|
325
|
50
|
|
|
|
1203
|
defined($man->{index}{$id}) ? $id : undef; |
|
300
|
|
|
|
|
|
|
} elsif (defined($man->{index}{$obj})) { |
|
301
|
32
|
|
|
|
|
75
|
$obj; |
|
302
|
|
|
|
|
|
|
} elsif ($assert) { |
|
303
|
|
|
|
|
|
|
# Report with confess() so user can see where the assertion was made |
|
304
|
1
|
|
|
|
|
216
|
confess "Assertion failed: '$obj' is not a valid/managed object"; |
|
305
|
|
|
|
|
|
|
} else { |
|
306
|
1
|
|
|
|
|
4
|
undef; |
|
307
|
|
|
|
|
|
|
} |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
#### |
|
311
|
|
|
|
|
|
|
## OBJECT MANAGEMENT METHODS |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# Add a new object to the manager. The user may either specify an ID (which |
|
314
|
|
|
|
|
|
|
# must not already exist), or allow it to take a predefined ID from the object |
|
315
|
|
|
|
|
|
|
# (if defined), or pick one on its own (if previous two undefined) |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sub add |
|
318
|
|
|
|
|
|
|
{ |
|
319
|
33
|
|
|
33
|
0
|
1605
|
my ($man, $obj, $id) = @_; |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# Pick new ID if needed. |
|
322
|
33
|
100
|
|
|
|
180
|
$id = $obj->id() if (!defined($id)); |
|
323
|
33
|
100
|
|
|
|
227
|
$id = $man->{next}++ if (!defined($id)); |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# Make sure it does not exist. |
|
326
|
33
|
100
|
|
|
|
341
|
croak "Attempt to add duplicate object ID '$id'" |
|
327
|
|
|
|
|
|
|
if (defined($man->{index}{$id})); |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# Add it. Do this before adding the manager link so we don't get |
|
330
|
|
|
|
|
|
|
# a call back to us. |
|
331
|
32
|
|
|
|
|
90
|
$man->{index}{$id} = $obj; |
|
332
|
32
|
|
|
|
|
103
|
$obj->id($id); |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# Add the manager attribute |
|
335
|
32
|
|
|
|
|
89
|
$obj->manager($man); |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
# Done. |
|
338
|
32
|
|
|
|
|
101
|
$id; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# Similar to add, but allows an object to already exist under this ID, in |
|
342
|
|
|
|
|
|
|
# which case the old on is removed. Returns the same values as add(). The |
|
343
|
|
|
|
|
|
|
# ID to replace is always taken from the existing object. The ID parameter |
|
344
|
|
|
|
|
|
|
# is applied to the new object (thus it must not already exist). |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
sub replace |
|
347
|
|
|
|
|
|
|
{ |
|
348
|
0
|
|
|
0
|
0
|
0
|
my ($man, $obj, $id) = @_; |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# Get rid of the old object. Don't worry if the object does not |
|
351
|
|
|
|
|
|
|
# already exist. |
|
352
|
0
|
|
|
|
|
0
|
$man->remove($id); |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# Add new one. |
|
355
|
0
|
|
|
|
|
0
|
$man->add($obj, $id); |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# Remove an object. Returns the object if the object was found and removed, |
|
359
|
|
|
|
|
|
|
# undef if not. The on_removed action is invoked on the object (but before |
|
360
|
|
|
|
|
|
|
# the object is actually removed so it can still access the manager linkage). |
|
361
|
|
|
|
|
|
|
# User may specify additional args to be passed to the action() call. |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub remove |
|
364
|
|
|
|
|
|
|
{ |
|
365
|
1
|
|
|
1
|
0
|
3
|
my $man = shift; |
|
366
|
1
|
|
|
|
|
3
|
my $self = shift; |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# If the last arg is a hash, this is additional args to any callback |
|
369
|
|
|
|
|
|
|
# that might get invoked. |
|
370
|
1
|
50
|
33
|
|
|
7
|
my $aargs = ( @_ && ref($_[$#_]) eq 'HASH' ? pop @_ : {} ); |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
# Any remaining arg is other. |
|
373
|
1
|
50
|
|
|
|
3
|
my $other = ( @_ ? shift : $self ); |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# If object does not exist, no need to go any further. |
|
376
|
1
|
|
|
|
|
5
|
my $id = $man->id($self); |
|
377
|
1
|
50
|
|
|
|
6
|
return undef if (!defined($man->{index}{$id})); |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# Fetch the object and invoke action. |
|
380
|
1
|
|
|
|
|
4
|
$self = $man->find($id); |
|
381
|
1
|
|
|
|
|
5
|
$self->action(other => $other, |
|
382
|
|
|
|
|
|
|
action => "object:remove", |
|
383
|
|
|
|
|
|
|
args => $aargs); |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# Break relationships TO this object. These are all done with the |
|
386
|
|
|
|
|
|
|
# force option. This means that no tests will be done for each |
|
387
|
|
|
|
|
|
|
# unrelate(), but post-unrelate() actions WILL occur. |
|
388
|
1
|
|
|
|
|
2
|
my @hows = (); |
|
389
|
1
|
50
|
|
|
|
6
|
@hows = keys %{$man->{relate_from}{$id}} |
|
|
1
|
|
|
|
|
5
|
|
|
390
|
|
|
|
|
|
|
if (defined($man->{relate_from}{$id})); |
|
391
|
1
|
|
|
|
|
2
|
foreach my $how (@hows) { |
|
392
|
2
|
|
|
|
|
3
|
my @fobjs = @{$man->{relate_from}{$id}{$how}}; |
|
|
2
|
|
|
|
|
9
|
|
|
393
|
2
|
|
|
|
|
5
|
foreach my $fobj (@fobjs) { |
|
394
|
4
|
|
|
|
|
21
|
$man->unrelate( |
|
395
|
|
|
|
|
|
|
how => $how, |
|
396
|
|
|
|
|
|
|
object => $fobj, |
|
397
|
|
|
|
|
|
|
other => $other, |
|
398
|
|
|
|
|
|
|
force => 1, |
|
399
|
|
|
|
|
|
|
args => { source => 'remove:to', %$aargs }, |
|
400
|
|
|
|
|
|
|
); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
} |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# Break all relationships FROM this object to others. |
|
405
|
1
|
|
|
|
|
3
|
@hows = (); |
|
406
|
1
|
50
|
|
|
|
5
|
@hows = keys %{$man->{relate_to}{$id}} |
|
|
1
|
|
|
|
|
5
|
|
|
407
|
|
|
|
|
|
|
if (defined($man->{relate_to}{$id})); |
|
408
|
1
|
|
|
|
|
4
|
foreach my $how (@hows) { |
|
409
|
1
|
|
|
|
|
2
|
my @objs = map { $man->find($_) } @{$man->{relate_from}{$id}{$how}}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
1
|
|
|
|
|
5
|
|
|
410
|
1
|
|
|
|
|
5
|
foreach my $obj (@objs) { |
|
411
|
0
|
|
|
|
|
0
|
$man->unrelate( |
|
412
|
|
|
|
|
|
|
how => $how, |
|
413
|
|
|
|
|
|
|
object => $obj, |
|
414
|
|
|
|
|
|
|
other => $other, |
|
415
|
|
|
|
|
|
|
force => 1, |
|
416
|
|
|
|
|
|
|
args => { source => 'remove:from', %$aargs } |
|
417
|
|
|
|
|
|
|
); |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# Delete from internal tables, which should remove all references to |
|
422
|
|
|
|
|
|
|
# it save the one we have. |
|
423
|
1
|
|
|
|
|
3
|
delete $man->{index}{$id}; |
|
424
|
1
|
|
|
|
|
3
|
delete $man->{relate_to}{$id}; |
|
425
|
1
|
|
|
|
|
5
|
delete $man->{relate_from}{$id}; |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# Remove the manager attribute. |
|
428
|
1
|
|
|
|
|
4
|
$self->manager(undef); |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# Done. |
|
431
|
1
|
|
|
|
|
4
|
$self; |
|
432
|
|
|
|
|
|
|
} |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
# Go down the complete list of objects and perform a method call on each. If |
|
435
|
|
|
|
|
|
|
# no args are given, 'process' is assumed. This will call them in order of |
|
436
|
|
|
|
|
|
|
# priority. |
|
437
|
|
|
|
|
|
|
# |
|
438
|
|
|
|
|
|
|
# The caller may choose to filter the list by providing a CODE ref as the |
|
439
|
|
|
|
|
|
|
# first argument. Only the objects for which the CODE ref returns true are |
|
440
|
|
|
|
|
|
|
# considered (new in v0.10). |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub process |
|
443
|
|
|
|
|
|
|
{ |
|
444
|
13
|
|
|
13
|
0
|
1152
|
my $man = shift; |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# Note that we grab the actual objects and not the ids in the sort. |
|
447
|
|
|
|
|
|
|
# This is more efficient, as each object is simply a reference (a |
|
448
|
|
|
|
|
|
|
# scalar with a fixed size) as opposed to a string (a scalar with |
|
449
|
|
|
|
|
|
|
# a variable size). |
|
450
|
13
|
|
|
|
|
20
|
my $method = shift; |
|
451
|
13
|
100
|
|
|
|
36
|
my $code = ( ref($method) eq 'CODE' ? $method : undef ); |
|
452
|
13
|
100
|
|
|
|
40
|
$method = shift if ($code); |
|
453
|
13
|
|
|
|
|
24
|
my @args = @_; |
|
454
|
13
|
100
|
|
|
|
33
|
$method = 'process' if (!defined($method)); |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
# Derive the object list. |
|
457
|
3
|
|
|
|
|
50
|
my @objs = ( |
|
458
|
|
|
|
|
|
|
$code ? |
|
459
|
3
|
|
|
|
|
10
|
grep { &$code($_, @args) } |
|
460
|
1
|
|
|
|
|
7
|
grep { UNIVERSAL::can($_, $method) } |
|
461
|
30
|
|
|
|
|
112
|
sort $CompareFunction values %{$man->{index}} |
|
462
|
|
|
|
|
|
|
: |
|
463
|
12
|
|
|
|
|
92
|
grep { UNIVERSAL::can($_, $method) } |
|
464
|
13
|
100
|
|
|
|
31
|
sort $CompareFunction values %{$man->{index}} |
|
465
|
|
|
|
|
|
|
); |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
# Process. |
|
468
|
13
|
100
|
|
|
|
47
|
unshift @args, $man->{process_list} if ($method eq 'process'); |
|
469
|
13
|
|
|
|
|
23
|
foreach my $obj (@objs) { |
|
470
|
29
|
50
|
|
|
|
151
|
$obj->$method(@args) if (UNIVERSAL::can($obj, $method)); |
|
471
|
|
|
|
|
|
|
} |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
# Return the number of objects processed. |
|
474
|
13
|
|
|
|
|
59
|
scalar(@objs); |
|
475
|
|
|
|
|
|
|
} |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
# Set/fetch the process list for the process() function. Note that the user is |
|
478
|
|
|
|
|
|
|
# not limited to the methods found here. The methods can be in the subclass |
|
479
|
|
|
|
|
|
|
# if desired. Note that we have no way to validate the method names here, |
|
480
|
|
|
|
|
|
|
# so we take it on good faith that they exist. |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub process_list { |
|
483
|
0
|
|
|
0
|
0
|
0
|
my $man = shift; |
|
484
|
0
|
0
|
|
|
|
0
|
if (@_) { @{$man->{process_list}} = @_ } else { @{$man->{process_list}} } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
#### |
|
488
|
|
|
|
|
|
|
## OBJECT RELATIONSHIP METHODS |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
# Check to see if a relationship is valid. If assertion flag present, this |
|
491
|
|
|
|
|
|
|
# will bomb the program if the relationship is not present. |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
sub has_relation |
|
494
|
|
|
|
|
|
|
{ |
|
495
|
684
|
|
|
684
|
0
|
989
|
my ($man, $how, $assert) = @_; |
|
496
|
|
|
|
|
|
|
|
|
497
|
684
|
0
|
|
|
|
4266
|
defined($man->{relation_def}{$how}) ? 1 : |
|
|
|
50
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
$assert ? croak "'$how' is an invalid relationship type" |
|
499
|
|
|
|
|
|
|
: 0; |
|
500
|
|
|
|
|
|
|
} |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
# Define a new relationship. This allows objects to be related with the |
|
503
|
|
|
|
|
|
|
# relate() method, or via a relator method created. |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
sub define_relation |
|
506
|
|
|
|
|
|
|
{ |
|
507
|
11
|
|
|
11
|
0
|
61
|
my $man = shift; |
|
508
|
11
|
|
|
|
|
30
|
my %args = (); |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# Fetch parameters. |
|
511
|
11
|
|
|
|
|
168
|
FetchParams(\@_, \%args, [ |
|
512
|
|
|
|
|
|
|
[ 'req', 'name', undef, 'string' ], |
|
513
|
|
|
|
|
|
|
[ 'opt', 'relate_method', undef, 'string' ], |
|
514
|
|
|
|
|
|
|
[ 'opt', 'unrelate_method', undef, 'string' ], |
|
515
|
|
|
|
|
|
|
[ 'opt', 'related_method', undef, 'string' ], |
|
516
|
|
|
|
|
|
|
[ 'opt', 'related_list_method', undef, 'string' ], |
|
517
|
|
|
|
|
|
|
[ 'opt', 'is_related_method', undef, 'string' ], |
|
518
|
|
|
|
|
|
|
[ 'opt', 'on_remove', undef, 'callback' ], |
|
519
|
|
|
|
|
|
|
[ 'opt', 'flags', 0, 'int' ], |
|
520
|
|
|
|
|
|
|
], 1 ); |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
# Add it. Note that we allow redefinition at will. |
|
523
|
11
|
|
|
|
|
70
|
my $rname = $args{name}; |
|
524
|
11
|
50
|
|
|
|
57
|
$args{relate_method} = $rname |
|
525
|
|
|
|
|
|
|
if (!$args{relate_method}); |
|
526
|
11
|
50
|
|
|
|
67
|
$args{unrelate_method} = "un${rname}" |
|
527
|
|
|
|
|
|
|
if (!$args{unrelate_method}); |
|
528
|
11
|
50
|
|
|
|
312
|
$args{related_method} = "${rname}_to" |
|
529
|
|
|
|
|
|
|
if (!$args{related_method}); |
|
530
|
11
|
50
|
|
|
|
43
|
$args{related_list_method} = "${rname}_list" |
|
531
|
|
|
|
|
|
|
if (!$args{related_list_method}); |
|
532
|
11
|
50
|
|
|
|
69
|
$args{is_related_method} = "is_${rname}" |
|
533
|
|
|
|
|
|
|
if (!$args{is_related_method}); |
|
534
|
11
|
|
|
|
|
44
|
$man->{relation_def}{$rname} = \%args; |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# Create relator. |
|
537
|
11
|
|
|
|
|
63
|
_CreateRelators(%args); |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# Done. |
|
540
|
11
|
|
|
|
|
37
|
1; |
|
541
|
|
|
|
|
|
|
} |
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# Relate two objects. |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub relate |
|
546
|
|
|
|
|
|
|
{ |
|
547
|
32
|
|
|
32
|
0
|
51
|
my $man = shift; |
|
548
|
32
|
|
|
|
|
67
|
my %args = (); |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
# Fetch parameters. Self is the thing being related to, object is |
|
551
|
|
|
|
|
|
|
# the thing being related to it. |
|
552
|
|
|
|
|
|
|
FetchParams(\@_, \%args, [ |
|
553
|
32
|
|
|
32
|
|
405
|
[ 'req', 'how', undef, sub { $man->has_relation(shift); } ], |
|
|
32
|
|
|
|
|
88
|
|
|
554
|
|
|
|
|
|
|
[ 'req', 'self', undef, 'any' ], |
|
555
|
|
|
|
|
|
|
[ 'req', 'object', undef, 'any' ], |
|
556
|
|
|
|
|
|
|
[ 'opt', 'other', undef, 'any' ], |
|
557
|
|
|
|
|
|
|
[ 'opt', 'force', 0, 'boolean' ], |
|
558
|
|
|
|
|
|
|
[ 'opt', 'args', {}, 'hashref' ], |
|
559
|
|
|
|
|
|
|
] ); |
|
560
|
32
|
|
|
|
|
215
|
my $how = $args{how}; |
|
561
|
32
|
|
|
|
|
72
|
my $self = $args{self}; |
|
562
|
32
|
|
|
|
|
51
|
my $object = $args{object}; |
|
563
|
32
|
|
|
|
|
49
|
my $other = $args{other}; |
|
564
|
32
|
|
|
|
|
51
|
my $force = $args{force}; |
|
565
|
32
|
|
|
|
|
52
|
my $aargs = $args{args}; |
|
566
|
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# If other is undefined, then we set it equal to self, meaning we assume |
|
568
|
|
|
|
|
|
|
# that the receipient of the object itself instigated the action. |
|
569
|
32
|
50
|
|
|
|
74
|
$other = $self if (!defined($other)); |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
# Do it. First fetch necesary parameters. |
|
572
|
32
|
|
|
|
|
82
|
my $rel = $man->{relation_def}{$how}; |
|
573
|
32
|
|
|
|
|
73
|
my $doaction = "object:on_" . $rel->{relate_method}; |
|
574
|
32
|
|
|
|
|
78
|
my $tryaction = "object:try_" . $rel->{relate_method}; |
|
575
|
32
|
|
|
|
|
91
|
my $idself = $man->id($self); $self = $man->find($idself); |
|
|
32
|
|
|
|
|
142
|
|
|
576
|
32
|
|
|
|
|
93
|
my $idobject = $man->id($object); $object = $man->find($idobject); |
|
|
32
|
|
|
|
|
77
|
|
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
# Perform check to see if relationship is allowed. We do this |
|
579
|
|
|
|
|
|
|
# before anything else, including attempting to unrelate it from |
|
580
|
|
|
|
|
|
|
# whatever it may be currently related to. This way the relate |
|
581
|
|
|
|
|
|
|
# check code can see how it is related now in case that means |
|
582
|
|
|
|
|
|
|
# anything, plus it prevents orphaned objects (which would happen |
|
583
|
|
|
|
|
|
|
# if we first unrelate()d it and then failed the relate() check). |
|
584
|
32
|
|
66
|
|
|
193
|
my $check = |
|
585
|
|
|
|
|
|
|
$force |
|
586
|
|
|
|
|
|
|
|| |
|
587
|
|
|
|
|
|
|
$self->action( |
|
588
|
|
|
|
|
|
|
action => $tryaction, |
|
589
|
|
|
|
|
|
|
object => $object, |
|
590
|
|
|
|
|
|
|
other => $other, |
|
591
|
|
|
|
|
|
|
args => $aargs); |
|
592
|
32
|
100
|
|
|
|
170
|
return 0 if (!$check); |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
# Relation is allowed, so check to see if already related. |
|
595
|
31
|
100
|
|
|
|
122
|
if (defined($man->{relate_to}{$idobject}{$how})) { |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# Already related in this fashion. |
|
598
|
5
|
50
|
|
|
|
42
|
if ($man->{relate_to}{$idobject}{$how} eq $idself) { |
|
|
|
50
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# And to the same object, so do nothing (successfully). |
|
600
|
0
|
|
|
|
|
0
|
return 1; |
|
601
|
|
|
|
|
|
|
} elsif ($man->unrelate( |
|
602
|
|
|
|
|
|
|
how => $how, |
|
603
|
|
|
|
|
|
|
object => $object, |
|
604
|
|
|
|
|
|
|
force => $force, |
|
605
|
|
|
|
|
|
|
args => { source => 'relate', %$aargs } )) { |
|
606
|
|
|
|
|
|
|
# The unrelate from the previous object succeeded, so |
|
607
|
|
|
|
|
|
|
# invoke myself to try again. |
|
608
|
5
|
|
|
|
|
21
|
return $man->relate(@_); |
|
609
|
|
|
|
|
|
|
} else { |
|
610
|
|
|
|
|
|
|
# The unrelate failed, so no-go. |
|
611
|
0
|
|
|
|
|
0
|
return 0; |
|
612
|
|
|
|
|
|
|
} |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
} |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# Not currently related to anything in this way. The first |
|
617
|
|
|
|
|
|
|
# thing we do is check the REL_NO_CIRCLE flag. If set, |
|
618
|
|
|
|
|
|
|
# then we make a check to see if a circular reference would |
|
619
|
|
|
|
|
|
|
# result from this. If so, then bomb, as this is assumed to |
|
620
|
|
|
|
|
|
|
# be a logic error in the main program. |
|
621
|
26
|
50
|
|
|
|
93
|
if ($rel->{flags} & REL_NO_CIRCLE) { |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
# Check to make sure no circular relationship would result from |
|
624
|
|
|
|
|
|
|
# this (i.e. self is already related to object in this manner). |
|
625
|
26
|
100
|
|
|
|
95
|
croak "Relating $idobject to $idself in manner $how would " . |
|
626
|
|
|
|
|
|
|
"create a circular relationship" |
|
627
|
|
|
|
|
|
|
if ($man->is_related( |
|
628
|
|
|
|
|
|
|
object => $self, |
|
629
|
|
|
|
|
|
|
self => $object, |
|
630
|
|
|
|
|
|
|
how => $how, |
|
631
|
|
|
|
|
|
|
distant => 1)); |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
} |
|
634
|
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
# Do it. |
|
636
|
25
|
|
|
|
|
80
|
$man->{relate_to}{$idobject}{$how} = $idself; |
|
637
|
25
|
100
|
|
|
|
107
|
$man->{relate_from}{$idself}{$how} = [] |
|
638
|
|
|
|
|
|
|
if (!defined($man->{relate_from}{$idself}{$how})); |
|
639
|
25
|
|
|
|
|
35
|
push @{$man->{relate_from}{$idself}{$how}}, $idobject; |
|
|
25
|
|
|
|
|
83
|
|
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# Invoke post-relate actions. |
|
642
|
25
|
|
|
|
|
99
|
$self->action( |
|
643
|
|
|
|
|
|
|
object => $object, |
|
644
|
|
|
|
|
|
|
other => $other, |
|
645
|
|
|
|
|
|
|
action => $doaction, |
|
646
|
|
|
|
|
|
|
args => $aargs, |
|
647
|
|
|
|
|
|
|
); |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# Done. |
|
650
|
25
|
|
|
|
|
228
|
1; |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
} |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
# Return the object to which this one is related (if any) |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
sub related |
|
657
|
|
|
|
|
|
|
{ |
|
658
|
161
|
|
|
161
|
0
|
220
|
my $man = shift; |
|
659
|
161
|
|
|
|
|
244
|
my %args = (); |
|
660
|
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
# Fetch parameters. |
|
662
|
|
|
|
|
|
|
FetchParams(\@_, \%args, [ |
|
663
|
161
|
|
|
161
|
|
1235
|
[ 'req', 'how', undef, sub { $man->has_relation(shift) } ], |
|
|
161
|
|
|
|
|
396
|
|
|
664
|
|
|
|
|
|
|
[ 'req', 'object', undef, 'any' ], |
|
665
|
|
|
|
|
|
|
] ); |
|
666
|
161
|
|
|
|
|
734
|
my $how = $args{how}; |
|
667
|
161
|
|
|
|
|
256
|
my $object = $args{object}; |
|
668
|
161
|
|
|
|
|
364
|
my $id = $man->id($object); $object = $man->find($id); |
|
|
161
|
|
|
|
|
361
|
|
|
669
|
|
|
|
|
|
|
|
|
670
|
161
|
100
|
100
|
|
|
1436
|
defined($man->{relate_to}{$id}) && # @*!&$ autovivication |
|
671
|
|
|
|
|
|
|
defined($man->{relate_to}{$id}{$how}) ? |
|
672
|
|
|
|
|
|
|
$man->find($man->{relate_to}{$id}{$how}) : undef; |
|
673
|
|
|
|
|
|
|
} |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# Return a list of items that are related to a paricular object in a certain |
|
676
|
|
|
|
|
|
|
# way. |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
sub related_list |
|
679
|
|
|
|
|
|
|
{ |
|
680
|
4
|
|
|
4
|
0
|
8
|
my $man = shift; |
|
681
|
4
|
|
|
|
|
9
|
my %args = (); |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
# Fetch parameters. |
|
684
|
|
|
|
|
|
|
FetchParams(\@_, \%args, [ |
|
685
|
4
|
|
|
4
|
|
46
|
[ 'req', 'how', undef, sub { $man->has_relation(shift) } ], |
|
|
4
|
|
|
|
|
11
|
|
|
686
|
|
|
|
|
|
|
[ 'req', 'self', undef, 'any' ], |
|
687
|
|
|
|
|
|
|
] ); |
|
688
|
4
|
|
|
|
|
20
|
my $how = $args{how}; |
|
689
|
4
|
|
|
|
|
10
|
my $self = $args{self}; |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
# Return list of objects. |
|
692
|
4
|
|
|
|
|
9
|
my $id = $man->id($self); |
|
693
|
4
|
|
|
|
|
9
|
my @list = (); |
|
694
|
4
|
50
|
33
|
|
|
32
|
@list = map { $man->find($_) } @{$man->{relate_from}{$id}{$how}} |
|
|
7
|
|
|
|
|
17
|
|
|
|
4
|
|
|
|
|
13
|
|
|
695
|
|
|
|
|
|
|
if (defined($man->{relate_from}{$id}) |
|
696
|
|
|
|
|
|
|
&& defined($man->{relate_from}{$id}{$how})); |
|
697
|
4
|
|
|
|
|
26
|
@list; |
|
698
|
|
|
|
|
|
|
} |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
# Check to see if two objects are related. By default, this checks only if |
|
701
|
|
|
|
|
|
|
# two objects are DIRECTLY related. However, specifying the "distant" flag |
|
702
|
|
|
|
|
|
|
# will perform a recursive check to see if the relationship exists indirectly. |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
sub is_related |
|
705
|
|
|
|
|
|
|
{ |
|
706
|
46
|
|
|
46
|
0
|
68
|
my $man = shift; |
|
707
|
46
|
|
|
|
|
80
|
my %args = (); |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
# Fetch parameters. |
|
710
|
|
|
|
|
|
|
FetchParams(\@_, \%args, [ |
|
711
|
46
|
|
|
46
|
|
437
|
[ 'req', 'how', undef, sub { $man->has_relation(shift); } ], |
|
|
46
|
|
|
|
|
113
|
|
|
712
|
|
|
|
|
|
|
[ 'req', 'object', undef, 'any' ], |
|
713
|
|
|
|
|
|
|
[ 'opt', 'self', undef, 'any' ], |
|
714
|
|
|
|
|
|
|
[ 'opt', 'distant', 0, 'boolean' ], |
|
715
|
|
|
|
|
|
|
] ); |
|
716
|
46
|
|
|
|
|
237
|
my $how = $args{how}; |
|
717
|
46
|
|
|
|
|
118
|
my $idobject = $man->id($args{object}); |
|
718
|
46
|
|
|
|
|
112
|
my $idself = $man->id($args{self}); |
|
719
|
46
|
|
|
|
|
75
|
my $distant = $args{distant}; |
|
720
|
46
|
50
|
33
|
|
|
187
|
return 0 if (!defined($idobject) || !defined($idself)); |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
# If idobject is related to nothing then no relation. |
|
723
|
46
|
100
|
66
|
|
|
354
|
return 0 if (!defined($man->{relate_to}{$idobject}) |
|
724
|
|
|
|
|
|
|
|| !defined($man->{relate_to}{$idobject}{$how})); |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# If there is a direct relationships, success. |
|
727
|
20
|
100
|
|
|
|
361
|
return 1 if ($man->{relate_to}{$idobject}{$how} eq $idself); |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
# If user did not want a distant relationship, then fail. |
|
730
|
14
|
100
|
|
|
|
39
|
return 0 if (!$distant); |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
# Otherwise, check what idobject is related to and see if that is |
|
733
|
|
|
|
|
|
|
# related to idself. |
|
734
|
12
|
|
|
|
|
56
|
$man->is_related( |
|
735
|
|
|
|
|
|
|
object => $man->{relate_to}{$idobject}{$how}, |
|
736
|
|
|
|
|
|
|
self => $idself, |
|
737
|
|
|
|
|
|
|
how => $how, |
|
738
|
|
|
|
|
|
|
distant => 1); |
|
739
|
|
|
|
|
|
|
} |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
# Unrelate an object. |
|
742
|
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
sub unrelate |
|
744
|
|
|
|
|
|
|
{ |
|
745
|
17
|
|
|
17
|
0
|
27
|
my $man = shift; |
|
746
|
17
|
|
|
|
|
34
|
my %args = (); |
|
747
|
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
# Fetch parameters. |
|
749
|
|
|
|
|
|
|
FetchParams(\@_, \%args, [ |
|
750
|
17
|
|
|
17
|
|
186
|
[ 'req', 'how', undef, sub { $man->has_relation(shift) } ], |
|
|
17
|
|
|
|
|
51
|
|
|
751
|
|
|
|
|
|
|
[ 'req', 'object', undef, 'any' ], |
|
752
|
|
|
|
|
|
|
[ 'opt', 'other', undef, 'any' ], |
|
753
|
|
|
|
|
|
|
[ 'opt', 'args', {}, 'hashref' ], |
|
754
|
|
|
|
|
|
|
] ); |
|
755
|
17
|
|
|
|
|
98
|
my $how = $args{how}; |
|
756
|
17
|
|
|
|
|
32
|
my $object = $args{object}; |
|
757
|
17
|
|
|
|
|
24
|
my $other = $args{other}; |
|
758
|
17
|
|
|
|
|
28
|
my $aargs = $args{args}; |
|
759
|
17
|
|
|
|
|
40
|
my $rel = $man->{relation_def}{$how}; |
|
760
|
17
|
|
|
|
|
47
|
my $doaction = "object:on_" . $rel->{unrelate_method}; |
|
761
|
17
|
|
|
|
|
31
|
my $tryaction = "object:try_" . $rel->{unrelate_method}; |
|
762
|
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
# Set the source if not already defined. |
|
764
|
17
|
100
|
|
|
|
56
|
$aargs->{source} = 'direct' if (!defined($aargs->{source})); |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
# Get ID and check if related. |
|
767
|
17
|
|
|
|
|
47
|
my $idobject = $man->id($object); $object = $man->find($idobject); |
|
|
17
|
|
|
|
|
46
|
|
|
768
|
17
|
100
|
66
|
|
|
142
|
if (defined($man->{relate_to}{$idobject}) |
|
769
|
|
|
|
|
|
|
&& defined($man->{relate_to}{$idobject}{$how})) { |
|
770
|
|
|
|
|
|
|
# Yes it is, so check that object to see if we can unrelate. |
|
771
|
12
|
|
|
|
|
26
|
my $idself = $man->{relate_to}{$idobject}{$how}; |
|
772
|
12
|
|
|
|
|
28
|
my $self = $man->find($idself); |
|
773
|
12
|
100
|
|
|
|
30
|
$other = $self if (!defined($other)); |
|
774
|
12
|
|
|
|
|
67
|
my $check = |
|
775
|
|
|
|
|
|
|
$self->action( |
|
776
|
|
|
|
|
|
|
object => $object, |
|
777
|
|
|
|
|
|
|
other => $other, |
|
778
|
|
|
|
|
|
|
action => $tryaction, |
|
779
|
|
|
|
|
|
|
args => { %$aargs }, |
|
780
|
|
|
|
|
|
|
); |
|
781
|
12
|
50
|
|
|
|
37
|
if ($check) { |
|
782
|
|
|
|
|
|
|
# Check succeeded, so unrelate them. |
|
783
|
12
|
|
|
|
|
46
|
delete $man->{relate_to}{$idobject}{$how}; |
|
784
|
12
|
|
|
|
|
20
|
my @nlist = (); |
|
785
|
12
|
|
|
|
|
19
|
foreach my $item (@{$man->{relate_from}{$idself}{$how}}) { |
|
|
12
|
|
|
|
|
40
|
|
|
786
|
30
|
100
|
|
|
|
96
|
push @nlist, $item if ($item ne $idobject); |
|
787
|
|
|
|
|
|
|
} |
|
788
|
12
|
|
|
|
|
26
|
@{$man->{relate_from}{$idself}{$how}} = @nlist; |
|
|
12
|
|
|
|
|
42
|
|
|
789
|
|
|
|
|
|
|
# Invoke post-unrelate actions. |
|
790
|
12
|
|
|
|
|
52
|
$self->action( |
|
791
|
|
|
|
|
|
|
object => $object, |
|
792
|
|
|
|
|
|
|
other => $other, |
|
793
|
|
|
|
|
|
|
action => $doaction, |
|
794
|
|
|
|
|
|
|
args => $aargs, |
|
795
|
|
|
|
|
|
|
); |
|
796
|
12
|
|
|
|
|
82
|
1; |
|
797
|
|
|
|
|
|
|
} else { |
|
798
|
0
|
|
|
|
|
0
|
0; |
|
799
|
|
|
|
|
|
|
} |
|
800
|
|
|
|
|
|
|
} else { |
|
801
|
|
|
|
|
|
|
# Not related to anything in this manner. Since the end result |
|
802
|
|
|
|
|
|
|
# is the same as the original condition, we consider this to |
|
803
|
|
|
|
|
|
|
# be success. |
|
804
|
5
|
|
|
|
|
53
|
1; |
|
805
|
|
|
|
|
|
|
} |
|
806
|
|
|
|
|
|
|
} |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
1; |