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; |