line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Class::Tangram; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Copyright (c) 2001 - 2005, Sam Vilain. All right reserved. This |
4
|
|
|
|
|
|
|
# file is licensed under the terms of the Perl Artistic license. |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 NAME |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
Class::Tangram - Tangram-friendly classes, DWIM attributes |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 SYNOPSIS |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
package MyObject; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use base qw(Class::Tangram); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our $fields = { int => [ qw(foo bar) ], |
17
|
|
|
|
|
|
|
string => [ qw(baz quux) ] }; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
package main; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my $object = MyObject->new(foo => 2, baz => "hello"); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
print $object->baz(); # prints "hello" |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
$object->set_quux("Something"); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
$object->set_foo("Something"); # dies - not an integer |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 DESCRIPTION |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
Class::Tangram is a tool for defining objects attributes. Simply |
32
|
|
|
|
|
|
|
define your object's fields/attributes using the same data structure |
33
|
|
|
|
|
|
|
introduced in _A Guided Tour of Tangram_ (see L<SEE ALSO>) and |
34
|
|
|
|
|
|
|
detailed in L<Tangram::Schema>, and you get objects that work As You'd |
35
|
|
|
|
|
|
|
Expect(tm). |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Class::Tangram has no dependancy upon Tangram, and vice versa. |
38
|
|
|
|
|
|
|
Neither requires anything special of your objects, nor do they insert |
39
|
|
|
|
|
|
|
any special fields into your objects. This is a very important |
40
|
|
|
|
|
|
|
feature with innumerable benefits, and few (if any) other object |
41
|
|
|
|
|
|
|
persistence tools have this feature. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
So, fluff aside, let's run through how you use Class::Tangram to make |
44
|
|
|
|
|
|
|
objects. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
First, you decide upon the attributes your object is going to have. |
47
|
|
|
|
|
|
|
You might do this using UML, or you might pick an existing database |
48
|
|
|
|
|
|
|
table and declare each column to be an attribute (you can leave out |
49
|
|
|
|
|
|
|
"id"; that one is implicit; also, leave out foreign keys until later). |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Your object should use Class::Tangram as a base class; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
use base qw(Class::Tangram) |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
or for older versions of perl: |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
use Class::Tangram; |
58
|
|
|
|
|
|
|
use vars qw(@ISA); |
59
|
|
|
|
|
|
|
@ISA = qw(Class::Tangram) |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
You should then define a C<$fields> variable in the scope of the |
62
|
|
|
|
|
|
|
package, that is a B<hash> from attribute B<types> (see |
63
|
|
|
|
|
|
|
L<Tangram::Type>) to either an B<array> of B<attribute names>, or |
64
|
|
|
|
|
|
|
another B<hash> from B<attribute names> to B<options hashes> (or |
65
|
|
|
|
|
|
|
C<undef>). The layout of this structure coincides exactly with the |
66
|
|
|
|
|
|
|
C<fields> portion of a tangram schema (see L<Tangram::Schema>), though |
67
|
|
|
|
|
|
|
there are some extra options available. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
This will hereon in be referred to as the `object schema' or just |
70
|
|
|
|
|
|
|
`schema'. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
For example, |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
package Orange; |
75
|
|
|
|
|
|
|
use base qw(Class::Tangram); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
our $fields = { |
78
|
|
|
|
|
|
|
int => { |
79
|
|
|
|
|
|
|
juiciness => undef, |
80
|
|
|
|
|
|
|
segments => { |
81
|
|
|
|
|
|
|
# this code reference is called when this |
82
|
|
|
|
|
|
|
# attribute is set, to check the value is |
83
|
|
|
|
|
|
|
# OK - note, no object is passed, this is for |
84
|
|
|
|
|
|
|
# simple marshalling only. |
85
|
|
|
|
|
|
|
check_func => sub { |
86
|
|
|
|
|
|
|
die "too many segments" |
87
|
|
|
|
|
|
|
if (${(shift)} > 30); |
88
|
|
|
|
|
|
|
}, |
89
|
|
|
|
|
|
|
# the default for this attribute. |
90
|
|
|
|
|
|
|
init_default => 7, |
91
|
|
|
|
|
|
|
}, |
92
|
|
|
|
|
|
|
}, |
93
|
|
|
|
|
|
|
ref => { |
94
|
|
|
|
|
|
|
grower => { |
95
|
|
|
|
|
|
|
}, |
96
|
|
|
|
|
|
|
}, |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# 'required' attributes - insist that these fields are |
99
|
|
|
|
|
|
|
# set, both with constructor and set()/set_X methods |
100
|
|
|
|
|
|
|
string => { |
101
|
|
|
|
|
|
|
# true: 'type' must have non-empty value (for |
102
|
|
|
|
|
|
|
# strings) or be logically true (for other types) |
103
|
|
|
|
|
|
|
type => { required => 1 }, |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# false: 'tag' must be defined but may be empty |
106
|
|
|
|
|
|
|
tag => { required => '' }, |
107
|
|
|
|
|
|
|
}, |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# fields allowed by Class::Tangram but not ever |
110
|
|
|
|
|
|
|
# stored by Tangram - no type checking by default |
111
|
|
|
|
|
|
|
transient => [ qw(_tangible) ], |
112
|
|
|
|
|
|
|
}; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
It is of critical importance to your sanity that you understand how |
115
|
|
|
|
|
|
|
anonymous hashes and anonymous arrays work in Perl. Some additional |
116
|
|
|
|
|
|
|
features are used above that have not yet been introduced, but you |
117
|
|
|
|
|
|
|
should be able to look at the above data structure and see that it |
118
|
|
|
|
|
|
|
satisfies the conditions stated in the paragraph before it. If it is |
119
|
|
|
|
|
|
|
hazy, I recommend reading L<perlref> or L<perlreftut>. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
When the schema for the object is first imported (see L<Schema |
122
|
|
|
|
|
|
|
import>), Class::Tangram defines accessor functions for each of the |
123
|
|
|
|
|
|
|
attributes defined in the schema. These accessor functions are then |
124
|
|
|
|
|
|
|
available as C<$object-E<gt>function> on created objects. By virtue |
125
|
|
|
|
|
|
|
of inheritance, various other methods are available. |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
From Class::Tangram 1.12 onwards, perl's C<AUTOLOAD> feature is not |
128
|
|
|
|
|
|
|
used to implement accessors; closures are compiled when the class is |
129
|
|
|
|
|
|
|
first used. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=cut |
132
|
|
|
|
|
|
|
|
133
|
7
|
|
|
7
|
|
450121
|
use strict 'vars', 'subs'; |
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
367
|
|
134
|
7
|
|
|
7
|
|
39
|
use Carp; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
692
|
|
135
|
|
|
|
|
|
|
|
136
|
7
|
|
|
7
|
|
41
|
use vars qw($VERSION %defaults @ISA); |
|
7
|
|
|
|
|
18
|
|
|
7
|
|
|
|
|
621
|
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
$VERSION = "1.57"; |
139
|
|
|
|
|
|
|
|
140
|
7
|
|
|
7
|
|
37268
|
use Set::Object qw(blessed reftype refaddr ish_int is_int is_double is_key); |
|
7
|
|
|
|
|
80746
|
|
|
7
|
|
|
|
|
61063
|
|
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
#--------------------------------------------------------------------- |
143
|
|
|
|
|
|
|
# run-time globals |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# $types{$class}->{$attribute} is the run-time discovered tangram type |
146
|
|
|
|
|
|
|
# of each attribute |
147
|
|
|
|
|
|
|
our (%types); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# $attribute_options{$class}->{$attribute} is the hash passed to tangram |
150
|
|
|
|
|
|
|
# for the given attribute (ie T2::Class.attribute(foo).options) |
151
|
|
|
|
|
|
|
our (%attribute_options); |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# $check{$class}->{$attribute}->($value) is a function that will die |
154
|
|
|
|
|
|
|
# if $value is not alright, see check_X functions |
155
|
|
|
|
|
|
|
our (%check); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Destructors for each attribute. They are called as |
158
|
|
|
|
|
|
|
# $cleaners{$class}->{$attribute}->($self, $attribute); |
159
|
|
|
|
|
|
|
our (%cleaners); |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# init_default values for each attribute. These could be hash refs, |
162
|
|
|
|
|
|
|
# array refs, code refs, or simple scalars. They will be stored as |
163
|
|
|
|
|
|
|
# $init_defaults{$class}->{$attribute} |
164
|
|
|
|
|
|
|
our (%init_defaults); |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# $required_attributes{$class}->{$attribute} records which attributes |
167
|
|
|
|
|
|
|
# are required... used only by new() at present. |
168
|
|
|
|
|
|
|
our (%required_attributes); |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# companion association registry. |
171
|
|
|
|
|
|
|
# |
172
|
|
|
|
|
|
|
# $companions{$class}->{$attribute} = $rem_attribute |
173
|
|
|
|
|
|
|
# |
174
|
|
|
|
|
|
|
# The inserted/deleted object has; |
175
|
|
|
|
|
|
|
# $object->"${rem_attribute}_insert"($self) |
176
|
|
|
|
|
|
|
# $object->"${rem_attribute}_remove"($self) |
177
|
|
|
|
|
|
|
# The sub is called as $coderef->($attribute, "insert", @objs); |
178
|
|
|
|
|
|
|
# or $coderef->($attribute, "remove", @objs); |
179
|
|
|
|
|
|
|
our (%companions); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# if a class is abstract, complain if one is constructed. |
182
|
|
|
|
|
|
|
our (%abstract); |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# Set when it is detected that Tangram is not installed |
185
|
|
|
|
|
|
|
my $no_tangram; |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head1 METHODS |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
The following methods are available for all Class::Tangram objects |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head2 Constructor |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
A Constructor is a method that returns a new instance of an object. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=over 4 |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item Class-E<gt>new (attribute1 =E<gt> value, attribute2 =E<gt> value) |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
Sets up a new object of type C<Class>, with attributes set to the |
200
|
|
|
|
|
|
|
values supplied. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
Can also be used as an object method (normal use is as a "class |
203
|
|
|
|
|
|
|
method"), in which case it returns a B<copy> of the object, without |
204
|
|
|
|
|
|
|
any deep copying. |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=cut |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub new |
209
|
|
|
|
|
|
|
{ |
210
|
85
|
|
|
85
|
1
|
29251
|
my $invocant = shift; |
211
|
85
|
|
66
|
|
|
552
|
my $class = ref $invocant || $invocant; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# Setup the object |
214
|
85
|
|
|
|
|
167
|
my $self = { }; |
215
|
85
|
|
|
|
|
164
|
bless $self, $class; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# auto-load schema as necessary |
218
|
85
|
100
|
|
|
|
267
|
exists $types{$class} or import_schema($class); |
219
|
|
|
|
|
|
|
|
220
|
85
|
50
|
|
|
|
228
|
croak "Attempt to instantiate an abstract type $class" |
221
|
|
|
|
|
|
|
if ($abstract{$class}); |
222
|
|
|
|
|
|
|
|
223
|
85
|
100
|
|
|
|
178
|
if (ref $invocant) |
224
|
|
|
|
|
|
|
{ |
225
|
|
|
|
|
|
|
# The copy constructor; this could be better :) |
226
|
|
|
|
|
|
|
# this has the side effect of much auto-vivification. |
227
|
3
|
|
|
|
|
18
|
$self->set( $invocant->_copy(@_) ); # override with @values |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
else |
230
|
|
|
|
|
|
|
{ |
231
|
82
|
|
|
|
|
505
|
$self->set (@_); # start with @values |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
79
|
|
|
|
|
443
|
$self->_fill_init_default(); |
235
|
79
|
|
|
|
|
403
|
$self->_check_required(); |
236
|
|
|
|
|
|
|
|
237
|
73
|
|
|
|
|
552
|
return $self; |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub _fill_init_default { |
242
|
79
|
|
|
79
|
|
101
|
my $self = shift; |
243
|
79
|
50
|
|
|
|
192
|
my $class = ref $self or confess "_fill_init_default usage error"; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# fill in fields that have defaults |
246
|
79
|
|
|
|
|
98
|
while ( my ($attribute, $default) = |
|
175
|
|
|
|
|
833
|
|
247
|
|
|
|
|
|
|
each %{$init_defaults{$class}} ) { |
248
|
|
|
|
|
|
|
|
249
|
96
|
100
|
|
|
|
357
|
next if (exists $self->{$attribute}); |
250
|
|
|
|
|
|
|
|
251
|
66
|
|
|
|
|
188
|
my $setter = "set_$attribute"; |
252
|
66
|
100
|
|
|
|
217
|
if (ref $default eq "CODE") { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# sub { }, attribute gets return value |
254
|
41
|
|
|
|
|
97
|
$self->$setter( $default->($self) ); |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
} elsif (ref $default eq "HASH") { |
257
|
|
|
|
|
|
|
# hash ref, copy hash |
258
|
0
|
|
|
|
|
0
|
$self->$setter( { %{ $default } } ); |
|
0
|
|
|
|
|
0
|
|
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
} elsif (ref $default eq "ARRAY") { |
261
|
|
|
|
|
|
|
# array ref, copy array |
262
|
0
|
|
|
|
|
0
|
$self->$setter( [ @{ $default } ] ); |
|
0
|
|
|
|
|
0
|
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
} else { |
265
|
|
|
|
|
|
|
# something else, an object or a scalar |
266
|
25
|
|
|
|
|
226
|
$self->$setter($default); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub _check_required { |
272
|
79
|
|
|
79
|
|
160
|
my $self = shift; |
273
|
79
|
|
|
|
|
156
|
my $class = ref $self; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# make sure field is not undef if 'required' option is set |
276
|
79
|
100
|
|
|
|
243
|
if (my $required = $required_attributes{$class}) { |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# find the immediate caller outside of this package |
279
|
21
|
|
|
|
|
30
|
my $i = 0; |
280
|
21
|
|
50
|
|
|
233
|
$i++ while UNIVERSAL::isa($self, scalar(caller($i))||";->"); |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# give Tangram some lenience - it is exempt from the effects |
283
|
|
|
|
|
|
|
# of the "required" option |
284
|
21
|
100
|
|
|
|
68
|
unless ( caller($i) =~ m/^Tangram::/ ) { |
285
|
18
|
|
|
|
|
22
|
my @missing; |
286
|
18
|
|
|
|
|
167
|
while ( my ($attribute, $value) = each %$required ) { |
287
|
42
|
100
|
|
|
|
265
|
push(@missing, $attribute) |
288
|
|
|
|
|
|
|
if ! exists $self->{$attribute}; |
289
|
|
|
|
|
|
|
} |
290
|
18
|
100
|
|
|
|
1196
|
croak("object missing required attribute(s): " |
291
|
|
|
|
|
|
|
.join(', ',@missing).'.') if @missing; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# $obj->_copy($target): copy self into the first arg |
297
|
|
|
|
|
|
|
sub _copy { |
298
|
3
|
|
|
3
|
|
4
|
my $self = shift; |
299
|
3
|
|
|
|
|
5
|
my $class = ref $self; |
300
|
3
|
|
33
|
|
|
9
|
my $types = $types{$class} || do { import_schema($class); |
301
|
|
|
|
|
|
|
$types{$class}; }; |
302
|
3
|
|
|
|
|
8
|
my %passed = (@_); |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# This will pretty much autovivify everything nearby. |
305
|
|
|
|
|
|
|
# c'est la vie |
306
|
3
|
|
|
|
|
4
|
my @rv; |
307
|
3
|
|
|
|
|
23
|
for my $field ( sort keys %$types ) { |
308
|
21
|
100
|
|
|
|
45
|
next if exists $passed{$field}; |
309
|
20
|
|
|
|
|
31
|
my $func = "get_$field"; |
310
|
20
|
|
|
|
|
65
|
push @rv, ($field => scalar($self->$func())); |
311
|
|
|
|
|
|
|
} |
312
|
3
|
|
|
|
|
96
|
return @rv, %passed; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=back |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=head2 Accessing & Setting Attributes |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=over |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=item $instance->set(attribute => $value, ...) |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
Sets the attributes of the given instance to the given values. croaks |
325
|
|
|
|
|
|
|
if there is a problem with the values. |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
This function simply calls C<$instance-E<gt>set_attribute($value)> for |
328
|
|
|
|
|
|
|
each of the C<attribute =E<gt> $value> pairs passed to it. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=cut |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
sub set { |
333
|
86
|
|
|
86
|
1
|
1575
|
my $self = shift; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# yes, this is a lot to do. yes, it's slow. But I'm fairly |
336
|
|
|
|
|
|
|
# certain that this could be handled efficiently if it were to be |
337
|
|
|
|
|
|
|
# moved inside the Perl interpreter or an XS module |
338
|
86
|
50
|
|
|
|
477
|
UNIVERSAL::isa($self, "Class::Tangram") or croak "type mismatch"; |
339
|
86
|
|
|
|
|
137
|
my $class = ref $self; |
340
|
86
|
50
|
|
|
|
216
|
exists $check{$class} or import_schema($class); |
341
|
86
|
50
|
|
|
|
216
|
croak "set must be called with an even number of arguments" |
342
|
|
|
|
|
|
|
if (scalar(@_) & 1); |
343
|
|
|
|
|
|
|
|
344
|
86
|
|
|
|
|
408
|
while (my ($name, $value) = splice @_, 0, 2) { |
345
|
|
|
|
|
|
|
|
346
|
147
|
|
|
|
|
231
|
my $setter = "set_".$name; |
347
|
|
|
|
|
|
|
|
348
|
147
|
100
|
66
|
|
|
1069
|
croak "attempt to set an illegal field $name in a $class" |
349
|
|
|
|
|
|
|
unless $self->can($setter) or $self->can("AUTOLOAD"); |
350
|
|
|
|
|
|
|
|
351
|
146
|
|
|
|
|
426
|
$self->$setter($value); |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=item $instance->get("attribute") |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Gets the value of C<$attribute>. This simply calls |
358
|
|
|
|
|
|
|
C<$instance-E<gt>get_attribute>. If multiple attributes are listed, |
359
|
|
|
|
|
|
|
then a list of the attribute values is returned in order. Note that |
360
|
|
|
|
|
|
|
you get back the results of the scalar context C<get_attribute> call |
361
|
|
|
|
|
|
|
in this case. |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=cut |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub get { |
366
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
367
|
0
|
0
|
|
|
|
0
|
croak "get what?" unless @_; |
368
|
0
|
0
|
|
|
|
0
|
UNIVERSAL::isa($self, "Class::Tangram") or croak "type mismatch"; |
369
|
|
|
|
|
|
|
|
370
|
0
|
|
|
|
|
0
|
my $class = ref $self; |
371
|
0
|
0
|
|
|
|
0
|
exists $check{$class} or import_schema($class); |
372
|
|
|
|
|
|
|
|
373
|
0
|
|
|
|
|
0
|
my $multiget = (scalar(@_) != 1); |
374
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
0
|
my @return; |
376
|
0
|
|
|
|
|
0
|
while ( my $field = shift ) { |
377
|
0
|
|
|
|
|
0
|
my $getter = "get_".$field; |
378
|
0
|
0
|
0
|
|
|
0
|
croak "attempt to read an illegal field $field in a $class" |
379
|
|
|
|
|
|
|
unless $self->can($getter) or $self->can("AUTOLOAD"); |
380
|
|
|
|
|
|
|
|
381
|
0
|
0
|
|
|
|
0
|
if ( $multiget ) { |
382
|
0
|
|
|
|
|
0
|
push @return, scalar($self->$getter()); |
383
|
|
|
|
|
|
|
} else { |
384
|
0
|
|
|
|
|
0
|
return $self->$getter(); |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
|
388
|
0
|
|
|
|
|
0
|
return @return; |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=item $instance->attribute($value) |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
For DWIM's sake, the behaviour of this function depends on the type of |
394
|
|
|
|
|
|
|
the attribute. |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=over |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=for the keen eye |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
This function, along with the get_attribute and set_attribute |
401
|
|
|
|
|
|
|
functions, are actually written inside a loop of the import_schema() |
402
|
|
|
|
|
|
|
function. The rationale for this is that a single closure is faster |
403
|
|
|
|
|
|
|
than two functions. |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=item scalar attributes |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
If C<$value> is not given, then |
408
|
|
|
|
|
|
|
this is equivalent to C<$instance-E<gt>get_attribute> |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
If C<$value> is given, then this is equivalent to |
411
|
|
|
|
|
|
|
C<$instance-E<gt>set_attribute($value)>. This usage issues a warning |
412
|
|
|
|
|
|
|
if warnings are on; you should change your code to use the |
413
|
|
|
|
|
|
|
set_attribute syntax for better readability. OO veterans will tell |
414
|
|
|
|
|
|
|
you that for maintainability object method names should always be a |
415
|
|
|
|
|
|
|
verb. |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
=item associations |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
With attributes that are associations, the default action when a |
420
|
|
|
|
|
|
|
parameter is given depends on what the argument list looks like. If |
421
|
|
|
|
|
|
|
it appears to be a series of C<(key =E<gt> value)> pairs (with or |
422
|
|
|
|
|
|
|
without the keys), then it is translated into call to C<set>. |
423
|
|
|
|
|
|
|
Containers (or C<undef>) are also allowed in place of values. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
If the argument list contains only keys (ie, scalars) then it is |
426
|
|
|
|
|
|
|
assumed you mean to `get' attributes. |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
If you pass this method an ambiguous argument list (eg, Key Key Value |
429
|
|
|
|
|
|
|
or Value Key) then you get an exception. |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=back |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=item $instance->get_attribute([@keys]) |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=over |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=item scalar attributes |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
Returns the value of the attribute. This may be a normal scalar, for |
440
|
|
|
|
|
|
|
C<int>, C<string>, and the C<datetime> related types, or an ARRAY or |
441
|
|
|
|
|
|
|
HASH REF, in the case of C<flat_array> or C<flat_hash> types. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=item associations |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
The association types - C<ref>, C<set>, C<array> and C<hash> return |
446
|
|
|
|
|
|
|
different results depending upon the context and presence of keys in |
447
|
|
|
|
|
|
|
the method's parameter list. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
In list context with no parameters, always returns the entire contents |
450
|
|
|
|
|
|
|
of the container, as a list, without keys. No sorting is applied, |
451
|
|
|
|
|
|
|
unless there is an implicit order due to the type of container the |
452
|
|
|
|
|
|
|
association uses (ie, arrays). |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
In scalar context with no parameters, always returns the container - a |
455
|
|
|
|
|
|
|
Set::Object, Array or Hash (or, for single element containers, the |
456
|
|
|
|
|
|
|
single element or C<undef> if it is empty). |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
In list context with parameters, the parameters are assumed to be a |
459
|
|
|
|
|
|
|
list of keys to look up. The container does its best to look up items |
460
|
|
|
|
|
|
|
corresponding to the keys given, and then returns them in the same |
461
|
|
|
|
|
|
|
order as the keys. |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
In scalar context with one parameter, the function returns that |
464
|
|
|
|
|
|
|
element best described by that key, or C<undef> if it is not present |
465
|
|
|
|
|
|
|
in the container. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=back |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=cut |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
sub looks_like_KVKV { |
472
|
2
|
50
|
|
2
|
0
|
5
|
my $input = join("", map { is_key($_) ? "K" : "V" } @_); |
|
2
|
|
|
|
|
9
|
|
473
|
2
|
|
|
|
|
50
|
return ($input =~ m/^(K?V)+$/g); |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
sub looks_like_KK { |
477
|
2
|
50
|
|
2
|
0
|
5
|
my $input = join("", map { is_key($_) ? "K" : "V" } @_); |
|
2
|
|
|
|
|
7
|
|
478
|
2
|
|
|
|
|
35
|
return ($input =~ m/^K+$/g); |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=over 4 |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=item `ref' attributes get |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
`ref' attributes are modelled as a container with a single element. |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
The accessor always returns the single element. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=cut |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub _get_X_ref { |
492
|
13
|
|
|
13
|
|
19
|
my $self = shift; |
493
|
13
|
|
|
|
|
19
|
my $X = shift; |
494
|
13
|
|
|
|
|
39
|
my $rv = $self->{$X}; |
495
|
|
|
|
|
|
|
# work around perl 5.8.0 tie() bug |
496
|
13
|
|
|
|
|
24
|
my $t = tied $self->{$X}; |
497
|
13
|
50
|
33
|
|
|
43
|
untie($self->{$X}) if ($t and $t =~ m/^Tangram/); |
498
|
13
|
|
|
|
|
61
|
return $self->{$X}; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=item `array' attributes get |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=cut |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
sub _get_X_array { |
506
|
15
|
|
|
15
|
|
19
|
my $self = shift; |
507
|
15
|
|
|
|
|
18
|
my $X = shift; |
508
|
15
|
|
100
|
|
|
66
|
my $a = ($self->{$X} ||= [ ]); |
509
|
|
|
|
|
|
|
# work around perl 5.8.0 tie() bug |
510
|
15
|
|
|
|
|
25
|
my $t = tied $self->{$X}; |
511
|
15
|
50
|
33
|
|
|
56
|
untie($self->{$X}) if ($t and $t =~ m/^Tangram/); |
512
|
|
|
|
|
|
|
|
513
|
15
|
50
|
|
|
|
29
|
if (@_) { |
514
|
0
|
|
|
|
|
0
|
my @rv; |
515
|
0
|
|
|
|
|
0
|
while (@_) { |
516
|
0
|
|
|
|
|
0
|
my $key = shift; |
517
|
0
|
0
|
|
|
|
0
|
if (defined $key) { |
518
|
0
|
0
|
|
|
|
0
|
if (defined(my $n = ish_int($key))) { |
519
|
0
|
|
|
|
|
0
|
push @rv, $a->[$n]; |
520
|
|
|
|
|
|
|
} else { |
521
|
0
|
0
|
|
|
|
0
|
carp("Keyed lookup to array container " |
522
|
|
|
|
|
|
|
.ref($self)."->$X($key), returning last " |
523
|
|
|
|
|
|
|
."member of array") |
524
|
|
|
|
|
|
|
if $^W; |
525
|
0
|
|
|
|
|
0
|
push @rv, $a->[$#{$a}]; |
|
0
|
|
|
|
|
0
|
|
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
} |
529
|
0
|
0
|
0
|
|
|
0
|
if (wantarray or @rv > 1) { |
530
|
0
|
|
|
|
|
0
|
return @rv; |
531
|
|
|
|
|
|
|
} else { |
532
|
0
|
|
|
|
|
0
|
return $rv[0]; |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
} else { |
535
|
15
|
100
|
|
|
|
36
|
if (wantarray) { |
536
|
7
|
|
|
|
|
9
|
return @{$a}; |
|
7
|
|
|
|
|
23
|
|
537
|
|
|
|
|
|
|
} else { |
538
|
8
|
|
|
|
|
31
|
return $a; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=item `set' attributes get |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
=cut |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
sub _get_X_set { |
548
|
112
|
|
|
112
|
|
124
|
my $self = shift; |
549
|
112
|
|
|
|
|
124
|
my $X = shift; |
550
|
112
|
|
66
|
|
|
454
|
my $a = ($self->{$X} ||= Set::Object->new()); |
551
|
|
|
|
|
|
|
# work around perl 5.8.0 tie() bug |
552
|
112
|
|
|
|
|
568
|
my $t = tied $self->{$X}; |
553
|
112
|
50
|
33
|
|
|
316
|
untie($self->{$X}) if ($t and $t =~ m/^Tangram/); |
554
|
|
|
|
|
|
|
|
555
|
112
|
100
|
|
|
|
212
|
if (@_) { |
556
|
|
|
|
|
|
|
# uh-oh, asking a set for keyed values. hmm. |
557
|
2
|
|
|
|
|
8
|
my @members = $a->members(); # maybe should shuffle |
558
|
2
|
|
|
|
|
4
|
my @rv; |
559
|
2
|
|
|
|
|
6
|
while (@_) { |
560
|
3
|
|
|
|
|
5
|
my $key = shift; |
561
|
3
|
50
|
33
|
|
|
17
|
if (defined $key and @members) { |
562
|
3
|
|
|
|
|
9
|
push @rv, (shift @members); |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
} |
565
|
2
|
100
|
66
|
|
|
17
|
if (wantarray or @rv > 1) { |
566
|
1
|
|
|
|
|
6
|
return @rv; |
567
|
|
|
|
|
|
|
} else { |
568
|
1
|
|
|
|
|
7
|
return $rv[0]; |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
} else { |
571
|
110
|
100
|
|
|
|
181
|
if (wantarray) { |
572
|
30
|
|
|
|
|
131
|
return $a->members(); |
573
|
|
|
|
|
|
|
} else { |
574
|
80
|
|
|
|
|
310
|
return $a; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=item `hash' attributes get |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=cut |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
sub _get_X_hash { |
584
|
11
|
|
|
11
|
|
20
|
my $self = shift; |
585
|
11
|
|
|
|
|
16
|
my $X = shift; |
586
|
11
|
|
100
|
|
|
54
|
my $a = ($self->{$X} ||= {}); |
587
|
|
|
|
|
|
|
# work around perl 5.8.0 tie() bug |
588
|
11
|
|
|
|
|
23
|
my $t = tied $self->{$X}; |
589
|
11
|
50
|
33
|
|
|
36
|
untie($self->{$X}) if ($t and $t =~ m/^Tangram/); |
590
|
|
|
|
|
|
|
|
591
|
11
|
100
|
|
|
|
41
|
if (@_) { |
592
|
2
|
|
|
|
|
4
|
my @rv; |
593
|
2
|
|
|
|
|
8
|
while (@_) { |
594
|
2
|
|
|
|
|
4
|
my $key = shift; |
595
|
2
|
50
|
|
|
|
6
|
if (defined $key) { |
596
|
2
|
|
|
|
|
8
|
push @rv, $a->{$key}; |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
} |
599
|
2
|
50
|
33
|
|
|
13
|
if (wantarray or @rv > 1) { |
600
|
0
|
|
|
|
|
0
|
return @rv; |
601
|
|
|
|
|
|
|
} else { |
602
|
2
|
|
|
|
|
18
|
return $rv[0]; |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
} else { |
605
|
9
|
50
|
|
|
|
37
|
if (wantarray) { |
606
|
0
|
|
|
|
|
0
|
return values %$a; |
607
|
|
|
|
|
|
|
} else { |
608
|
9
|
|
|
|
|
60
|
return $a; |
609
|
|
|
|
|
|
|
} |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=back |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=item $instance->set_attribute($value) |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
The normative way of setting attributes. If you wish to override the |
619
|
|
|
|
|
|
|
behaviour of an object when getting or setting an attribute, override |
620
|
|
|
|
|
|
|
these functions. They will be called when you use |
621
|
|
|
|
|
|
|
C<$instance-E<gt>attribute>, C<$instance-E<gt>get()>, constructors, |
622
|
|
|
|
|
|
|
etc. |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
When attributes that are associations are changed via other functions, |
625
|
|
|
|
|
|
|
a new container with the new contents is built, and then passed to |
626
|
|
|
|
|
|
|
this function. |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=over |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=item `ref' attributes set |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
Like all other container set methods, this method may be passed a Set, |
633
|
|
|
|
|
|
|
Array or Hash, and all the members are added in order to (single |
634
|
|
|
|
|
|
|
element) container. If the resultant container has more than one |
635
|
|
|
|
|
|
|
item, it raises a run-time warning. |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=cut |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
sub _set_X_ref { |
640
|
21
|
|
|
21
|
|
27
|
my $self = shift; |
641
|
21
|
|
|
|
|
71
|
my $base_type = shift; |
642
|
21
|
|
|
|
|
25
|
my $companion = shift; |
643
|
21
|
|
|
|
|
36
|
my $X = shift; |
644
|
21
|
|
|
|
|
28
|
my $class = ref $self; |
645
|
|
|
|
|
|
|
|
646
|
21
|
|
|
|
|
24
|
my @ncc; |
647
|
21
|
|
|
|
|
52
|
while (@_) { |
648
|
20
|
|
|
|
|
24
|
my $value = shift; |
649
|
20
|
100
|
|
|
|
81
|
if (blessed($value)) { |
650
|
19
|
50
|
|
|
|
151
|
if ($value->isa("Set::Object")) { |
651
|
0
|
|
|
|
|
0
|
push @ncc, $value->members(); |
652
|
|
|
|
|
|
|
} else { |
653
|
19
|
|
|
|
|
69
|
push @ncc, $value; |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
} else { |
656
|
1
|
|
|
|
|
3
|
my $ref = ref $value; |
657
|
1
|
50
|
|
|
|
8
|
if ($ref eq "ARRAY") { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
658
|
0
|
|
|
|
|
0
|
@ncc = @$value; |
659
|
|
|
|
|
|
|
} elsif ($ref eq "HASH") { |
660
|
0
|
|
|
|
|
0
|
@ncc = values %$value; |
661
|
|
|
|
|
|
|
} elsif (defined $value) { |
662
|
1
|
|
|
|
|
4
|
push @ncc, $value; |
663
|
|
|
|
|
|
|
} |
664
|
|
|
|
|
|
|
} |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
|
667
|
21
|
100
|
|
|
|
48
|
if (@ncc) { |
668
|
20
|
50
|
|
|
|
61
|
if (my $checkit = \$check{$class}->{$X}) { |
669
|
|
|
|
|
|
|
# There's a check function! Use it! |
670
|
20
|
|
|
|
|
33
|
$ {$checkit}->(\$ncc[0]); |
|
20
|
|
|
|
|
50
|
|
671
|
|
|
|
|
|
|
} else { |
672
|
0
|
0
|
|
|
|
0
|
if (@ncc > 1) { |
673
|
0
|
0
|
|
|
|
0
|
carp ("container ".ref($self)."->$X overflowed! " |
674
|
|
|
|
|
|
|
."Rejecting members at end!") |
675
|
|
|
|
|
|
|
if $^W; |
676
|
0
|
|
|
|
|
0
|
@ncc = $ncc[0]; |
677
|
|
|
|
|
|
|
} |
678
|
0
|
0
|
|
|
|
0
|
croak("Tried to place `$ncc[0]' in a ref container") |
679
|
|
|
|
|
|
|
unless (ref $ncc[0]); |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
|
683
|
20
|
|
|
|
|
45
|
my $old = $self->{$X}; |
684
|
20
|
|
|
|
|
43
|
my $chosen = $self->{$X} = $ncc[0]; |
685
|
|
|
|
|
|
|
|
686
|
20
|
100
|
66
|
|
|
147
|
if ($companion and refaddr($self->{$X}) != refaddr($old)) { |
687
|
6
|
|
|
|
|
12
|
my $remove = $companion."_remove"; |
688
|
6
|
|
|
|
|
11
|
my $insert = $companion."_insert"; |
689
|
6
|
|
|
|
|
15
|
my $includes = $companion."_includes"; |
690
|
|
|
|
|
|
|
|
691
|
6
|
50
|
66
|
|
|
65
|
$old->$remove($self) |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
692
|
|
|
|
|
|
|
if ($old and $old->can($remove) |
693
|
|
|
|
|
|
|
and $old->can($includes) |
694
|
|
|
|
|
|
|
and $old->$includes($self)); |
695
|
|
|
|
|
|
|
|
696
|
6
|
100
|
66
|
|
|
90
|
$chosen->$insert($self) |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
697
|
|
|
|
|
|
|
if ($chosen and $chosen->can($insert) |
698
|
|
|
|
|
|
|
and $chosen->can($includes) |
699
|
|
|
|
|
|
|
and !$chosen->$includes($self)); |
700
|
|
|
|
|
|
|
} |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=item `set' attributes set |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=cut |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
sub _set_X_set { |
709
|
93
|
|
|
93
|
|
114
|
my $self = shift; |
710
|
93
|
|
|
|
|
111
|
my $base_type = shift; |
711
|
93
|
|
|
|
|
101
|
my $companion = shift; |
712
|
93
|
|
|
|
|
102
|
my $X = shift; |
713
|
93
|
|
|
|
|
125
|
my $class = ref $self; |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
# Shortcut to avoid penalty when simply setting to a new container |
716
|
93
|
100
|
100
|
|
|
515
|
if (@_ == 1 and !$companion and |
|
|
|
100
|
|
|
|
|
717
|
|
|
|
|
|
|
UNIVERSAL::isa($_[0], "Set::Object")) { |
718
|
25
|
50
|
|
|
|
83
|
if (my $checkit = \$check{$class}->{$X}) { |
719
|
|
|
|
|
|
|
# There's a check function! Use it! |
720
|
25
|
|
|
|
|
38
|
$ {$checkit}->(\($_[0])); |
|
25
|
|
|
|
|
65
|
|
721
|
|
|
|
|
|
|
} |
722
|
25
|
|
|
|
|
45
|
delete $self->{$X}; # make sure it's not tied - 5.8.0 bug |
723
|
25
|
|
|
|
|
136
|
return $self->{$X} = $_[0]; |
724
|
|
|
|
|
|
|
} |
725
|
|
|
|
|
|
|
|
726
|
68
|
|
|
|
|
77
|
my @ncc; |
727
|
68
|
|
|
|
|
135
|
while (@_) { |
728
|
84
|
|
|
|
|
116
|
my $value = (shift @_); |
729
|
84
|
100
|
|
|
|
354
|
if (blessed($value)) { |
730
|
81
|
100
|
|
|
|
323
|
if ($value->isa("Set::Object")) { |
731
|
46
|
|
|
|
|
183
|
push @ncc, $value->members(); |
732
|
|
|
|
|
|
|
} else { |
733
|
35
|
|
|
|
|
121
|
push @ncc, $value; |
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
} else { |
736
|
3
|
|
|
|
|
5
|
my $ref = ref $value; |
737
|
3
|
100
|
|
|
|
10
|
if ($ref eq "ARRAY") { |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
738
|
2
|
|
|
|
|
7
|
push @ncc, @$value; |
739
|
|
|
|
|
|
|
} elsif ($ref eq "HASH") { |
740
|
1
|
|
|
|
|
5
|
push @ncc, values %$value; |
741
|
|
|
|
|
|
|
} elsif (defined(ish_int($value))) { |
742
|
0
|
|
|
|
|
0
|
$ncc[$value] = (shift @_); |
743
|
|
|
|
|
|
|
} else { |
744
|
|
|
|
|
|
|
# some other type of key, ignore it |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
} |
747
|
|
|
|
|
|
|
} |
748
|
|
|
|
|
|
|
|
749
|
68
|
|
|
|
|
79
|
my ($old, $new); |
750
|
|
|
|
|
|
|
|
751
|
68
|
100
|
|
|
|
173
|
if ($companion) { |
752
|
|
|
|
|
|
|
# ordering is ignored for arrays when it comes to |
753
|
|
|
|
|
|
|
# companions |
754
|
59
|
100
|
|
|
|
227
|
$old = Set::Object->new( $self->{$X} ? $self->{$X}->members |
755
|
|
|
|
|
|
|
: () ); |
756
|
|
|
|
|
|
|
} |
757
|
68
|
|
|
|
|
473
|
$new = Set::Object->new(@ncc); |
758
|
|
|
|
|
|
|
|
759
|
68
|
50
|
|
|
|
207
|
if (my $checkit = \$check{$class}->{$X}) { |
760
|
|
|
|
|
|
|
# There's a check function! Use it! |
761
|
68
|
|
|
|
|
80
|
$ {$checkit}->(\$new); |
|
68
|
|
|
|
|
151
|
|
762
|
|
|
|
|
|
|
} |
763
|
68
|
|
|
|
|
237
|
$self->{$X} = $new; |
764
|
|
|
|
|
|
|
|
765
|
68
|
100
|
|
|
|
209
|
if ($companion) { |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
# I love Set::Object, it should be a builtin data type :-) |
768
|
59
|
|
|
|
|
336
|
my $gone = $old - $new; |
769
|
59
|
|
|
|
|
1230
|
my $added = $new - $old; |
770
|
|
|
|
|
|
|
|
771
|
59
|
|
|
|
|
1129
|
my $includes_func = $companion."_includes"; |
772
|
|
|
|
|
|
|
|
773
|
59
|
100
|
|
|
|
182
|
if ($gone->size) { |
774
|
9
|
|
|
|
|
17
|
my $remove_func = $companion."_remove"; |
775
|
9
|
|
|
|
|
23
|
for my $gonner ($gone->members) { |
776
|
9
|
100
|
66
|
|
|
95
|
if ($gonner->can($remove_func) && |
|
|
|
100
|
|
|
|
|
777
|
|
|
|
|
|
|
$gonner->can($includes_func) && |
778
|
|
|
|
|
|
|
$gonner->$includes_func($self)) { |
779
|
5
|
|
|
|
|
21
|
$gonner->$remove_func($self); |
780
|
|
|
|
|
|
|
} |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
} |
783
|
|
|
|
|
|
|
|
784
|
59
|
100
|
|
|
|
401
|
if ($added->size) { |
785
|
23
|
|
|
|
|
32
|
my $insert_func = $companion."_insert"; |
786
|
23
|
|
|
|
|
70
|
for my $new_mate ($added->members) { |
787
|
24
|
100
|
66
|
|
|
239
|
if ($new_mate->can($insert_func) && |
|
|
|
100
|
|
|
|
|
788
|
|
|
|
|
|
|
$new_mate->can($includes_func) && |
789
|
|
|
|
|
|
|
!$new_mate->$includes_func($self) ) { |
790
|
15
|
|
|
|
|
36
|
$new_mate->$insert_func($self); |
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
} |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
} |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
=item `array' attributes set |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
=cut |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
sub _set_X_array { |
802
|
7
|
|
|
7
|
|
9
|
my $self = shift; |
803
|
7
|
|
|
|
|
8
|
my $base_type = shift; |
804
|
7
|
|
|
|
|
9
|
my $companion = shift; |
805
|
7
|
|
|
|
|
10
|
my $X = shift; |
806
|
7
|
|
|
|
|
11
|
my $class = ref $self; |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
# Shortcut to avoid penalty when simply setting to a new container |
809
|
7
|
50
|
66
|
|
|
36
|
if (@_ == 1 and !$companion and ref $_[0] eq "ARRAY") { |
|
|
|
33
|
|
|
|
|
810
|
0
|
|
|
|
|
0
|
delete $self->{$X}; # make sure it's not tied - 5.8.0 bug |
811
|
0
|
0
|
|
|
|
0
|
if (my $checkit = \$check{$class}->{$X}) { |
812
|
|
|
|
|
|
|
# There's a check function! Use it! |
813
|
0
|
|
|
|
|
0
|
$ {$checkit}->(\($_[0])); |
|
0
|
|
|
|
|
0
|
|
814
|
|
|
|
|
|
|
} |
815
|
0
|
|
|
|
|
0
|
return $self->{$X} = $_[0]; |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
|
818
|
7
|
|
|
|
|
15
|
my @ncc; |
819
|
7
|
|
|
|
|
17
|
while (@_) { |
820
|
8
|
|
|
|
|
11
|
my ($value) = (shift @_); |
821
|
8
|
100
|
|
|
|
154
|
if (blessed($value)) { |
822
|
7
|
50
|
|
|
|
35
|
if ($value->isa("Set::Object")) { |
823
|
0
|
|
|
|
|
0
|
push @ncc, $value->members(); |
824
|
|
|
|
|
|
|
} else { |
825
|
7
|
|
|
|
|
25
|
push @ncc, $value; |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
} else { |
828
|
1
|
|
|
|
|
3
|
my $ref = ref $value; |
829
|
1
|
50
|
|
|
|
4
|
if ($ref eq "ARRAY") { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
830
|
1
|
|
|
|
|
5
|
push @ncc, @$value; |
831
|
|
|
|
|
|
|
} elsif ($ref eq "HASH") { |
832
|
0
|
|
|
|
|
0
|
push @ncc, values %$value; |
833
|
|
|
|
|
|
|
} elsif (defined(ish_int($value))) { |
834
|
0
|
|
|
|
|
0
|
$ncc[$value] = (shift @_); |
835
|
|
|
|
|
|
|
} else { |
836
|
|
|
|
|
|
|
# some other type of key, ignore it |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
} |
839
|
|
|
|
|
|
|
} |
840
|
|
|
|
|
|
|
|
841
|
7
|
|
|
|
|
9
|
my ($set, $ncc); |
842
|
|
|
|
|
|
|
|
843
|
7
|
50
|
|
|
|
15
|
if ($companion) { |
844
|
|
|
|
|
|
|
# ordering is ignored for arrays when it comes to |
845
|
|
|
|
|
|
|
# companions |
846
|
0
|
|
|
|
|
0
|
$set = Set::Object->new( blessed($self->{$X}) |
847
|
7
|
50
|
|
|
|
44
|
? (grep { ref $_ } $self->{$X}->members) |
848
|
|
|
|
|
|
|
: () ); |
849
|
7
|
|
|
|
|
11
|
$ncc = Set::Object->new(grep { ref $_ } @ncc); |
|
8
|
|
|
|
|
36
|
|
850
|
|
|
|
|
|
|
} |
851
|
|
|
|
|
|
|
|
852
|
7
|
50
|
|
|
|
28
|
if (my $checkit = $check{$class}->{$X}) { |
853
|
|
|
|
|
|
|
# There's a check function! Use it! |
854
|
7
|
|
|
|
|
52
|
$checkit->(\\@ncc); |
855
|
|
|
|
|
|
|
} else { |
856
|
0
|
|
|
|
|
0
|
confess "no checkit for $self - $class, X is $X, checkit is $$checkit\n"; |
857
|
|
|
|
|
|
|
} |
858
|
|
|
|
|
|
|
|
859
|
7
|
|
|
|
|
14
|
$self->{$X} = \@ncc; |
860
|
|
|
|
|
|
|
|
861
|
7
|
50
|
|
|
|
16
|
if ($companion) { |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
# I love Set::Object, it should be a builtin data type :-) |
864
|
7
|
|
|
|
|
20
|
my $gone = $set - $ncc; |
865
|
7
|
|
|
|
|
130
|
my $new = $ncc - $set; |
866
|
|
|
|
|
|
|
|
867
|
7
|
|
|
|
|
138
|
my $includes_func = $companion."_includes"; |
868
|
|
|
|
|
|
|
|
869
|
7
|
50
|
|
|
|
23
|
if ($gone->size) { |
870
|
0
|
|
|
|
|
0
|
my $remove_func = $companion."_remove"; |
871
|
0
|
|
|
|
|
0
|
for my $gonner ($gone->members) { |
872
|
0
|
0
|
0
|
|
|
0
|
if ($gonner->can($remove_func) && |
|
|
|
0
|
|
|
|
|
873
|
|
|
|
|
|
|
$gonner->can($includes_func) && |
874
|
|
|
|
|
|
|
$gonner->$includes_func($self)) { |
875
|
0
|
|
|
|
|
0
|
$gonner->$remove_func($self); |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
} |
878
|
|
|
|
|
|
|
} |
879
|
|
|
|
|
|
|
|
880
|
7
|
50
|
|
|
|
21
|
if ($new->size) { |
881
|
7
|
|
|
|
|
13
|
my $insert_func = $companion."_insert"; |
882
|
7
|
|
|
|
|
19
|
for my $new_mate ($new->members) { |
883
|
7
|
100
|
33
|
|
|
89
|
if ($new_mate->can($insert_func) && |
|
|
|
66
|
|
|
|
|
884
|
|
|
|
|
|
|
$new_mate->can($includes_func) && |
885
|
|
|
|
|
|
|
!$new_mate->$includes_func($self) ) { |
886
|
1
|
|
|
|
|
3
|
$new_mate->$insert_func($self); |
887
|
|
|
|
|
|
|
} |
888
|
|
|
|
|
|
|
} |
889
|
|
|
|
|
|
|
} |
890
|
|
|
|
|
|
|
} |
891
|
|
|
|
|
|
|
} |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
=item `hash' attributes set |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=cut |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
sub _set_X_hash { |
898
|
3
|
|
|
3
|
|
7
|
my $self = shift; |
899
|
3
|
|
|
|
|
6
|
my $base_type = shift; |
900
|
3
|
|
|
|
|
10
|
my $companion = shift; |
901
|
3
|
|
|
|
|
13
|
my $X = shift; |
902
|
3
|
|
|
|
|
7
|
my $class = ref $self; |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
# Shortcut to avoid penalty when simply setting to a new container |
905
|
3
|
50
|
66
|
|
|
23
|
if (@_ == 1 and !$companion and ref $_[0] eq "HASH") { |
|
|
|
66
|
|
|
|
|
906
|
0
|
|
|
|
|
0
|
delete $self->{$X}; # make sure it's not tied - 5.8.0 bug |
907
|
0
|
0
|
|
|
|
0
|
if (my $checkit = \$check{$class}->{$X}) { |
908
|
|
|
|
|
|
|
# There's a check function! Use it! |
909
|
0
|
|
|
|
|
0
|
$ {$checkit}->(\($_[0])); |
|
0
|
|
|
|
|
0
|
|
910
|
|
|
|
|
|
|
} |
911
|
0
|
|
|
|
|
0
|
return $self->{$X} = $_[0]; |
912
|
|
|
|
|
|
|
} |
913
|
|
|
|
|
|
|
|
914
|
3
|
|
|
|
|
4
|
my %ncc; |
915
|
3
|
|
|
|
|
6
|
my $n = 0; |
916
|
|
|
|
|
|
|
my $ins = sub { |
917
|
4
|
|
|
4
|
|
8
|
my $item = shift; |
918
|
4
|
100
|
66
|
|
|
60
|
if (blessed $item and |
919
|
|
|
|
|
|
|
$item->can(my $meth = "${X}_hek")) { |
920
|
3
|
|
|
|
|
11
|
$ncc{$item->$meth} = $item; |
921
|
|
|
|
|
|
|
} else { |
922
|
1
|
|
|
|
|
7
|
$ncc{"".$n++} = $item; |
923
|
|
|
|
|
|
|
} |
924
|
3
|
|
|
|
|
21
|
}; |
925
|
|
|
|
|
|
|
|
926
|
3
|
|
|
|
|
17
|
while (@_) { |
927
|
7
|
|
|
|
|
12
|
my ($value) = (shift @_); |
928
|
7
|
100
|
|
|
|
53
|
if (blessed($value)) { |
929
|
4
|
50
|
|
|
|
38
|
if ($value->isa("Set::Object")) { |
930
|
0
|
|
|
|
|
0
|
$ins->($_) foreach $value->members(); |
931
|
|
|
|
|
|
|
} else { |
932
|
4
|
|
|
|
|
8
|
$ins->($value); |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
} else { |
935
|
3
|
|
|
|
|
6
|
my $ref = ref $value; |
936
|
3
|
50
|
|
|
|
14
|
if ($ref) { |
|
|
50
|
|
|
|
|
|
937
|
0
|
0
|
|
|
|
0
|
if ($ref eq "ARRAY") { |
|
|
0
|
|
|
|
|
|
938
|
0
|
|
|
|
|
0
|
$ins->($_) foreach @$value; |
939
|
|
|
|
|
|
|
} elsif ($ref eq "HASH") { |
940
|
0
|
|
|
|
|
0
|
while (my ($k, $v) = each %$value) { |
941
|
0
|
|
|
|
|
0
|
$ncc{$k} = $v; |
942
|
|
|
|
|
|
|
} |
943
|
|
|
|
|
|
|
} |
944
|
|
|
|
|
|
|
} elsif (defined(ish_int($value))) { |
945
|
|
|
|
|
|
|
# hmmf. A number? Well, just put it on the end. |
946
|
|
|
|
|
|
|
# exact convention to be determined later |
947
|
0
|
|
|
|
|
0
|
$ins->(shift @_); |
948
|
|
|
|
|
|
|
} else { |
949
|
|
|
|
|
|
|
# a plain hash key |
950
|
3
|
|
|
|
|
37
|
$ncc{$value} = (shift @_); |
951
|
|
|
|
|
|
|
} |
952
|
|
|
|
|
|
|
} |
953
|
|
|
|
|
|
|
} |
954
|
|
|
|
|
|
|
|
955
|
3
|
|
50
|
|
|
14
|
my $old = $self->{$X} || {}; |
956
|
3
|
50
|
|
|
|
14
|
if (my $checkit = \$check{$class}->{$X}) { |
957
|
|
|
|
|
|
|
# There's a check function! Use it! |
958
|
3
|
|
|
|
|
14
|
$ {$checkit}->(\\%ncc); |
|
3
|
|
|
|
|
11
|
|
959
|
|
|
|
|
|
|
} |
960
|
3
|
|
|
|
|
7
|
$self->{$X} = \%ncc; |
961
|
|
|
|
|
|
|
|
962
|
3
|
100
|
|
|
|
18
|
if ($companion) { |
963
|
|
|
|
|
|
|
# ordering is ignored for arrays when it comes to |
964
|
|
|
|
|
|
|
# companions |
965
|
2
|
|
|
|
|
20
|
my $set = Set::Object->new(values %$old); |
966
|
2
|
|
|
|
|
18
|
my $ncc = Set::Object->new(values %ncc); |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
# I love Set::Object, it should be a builtin data type :-) |
969
|
2
|
|
|
|
|
101
|
my $gone = $set - $ncc; |
970
|
2
|
|
|
|
|
67
|
my $new = $ncc - $set; |
971
|
|
|
|
|
|
|
|
972
|
2
|
|
|
|
|
50
|
my $includes_func = $companion."_includes"; |
973
|
|
|
|
|
|
|
|
974
|
2
|
50
|
|
|
|
10
|
if ($gone->size) { |
975
|
0
|
|
|
|
|
0
|
my $remove_func = $companion."_remove"; |
976
|
0
|
|
|
|
|
0
|
for my $gonner ($gone->members) { |
977
|
0
|
0
|
0
|
|
|
0
|
if ($gonner->can($remove_func) && |
|
|
|
0
|
|
|
|
|
978
|
|
|
|
|
|
|
$gonner->can($includes_func) && |
979
|
|
|
|
|
|
|
$gonner->$includes_func($self)) { |
980
|
0
|
|
|
|
|
0
|
$gonner->$remove_func($self); |
981
|
|
|
|
|
|
|
} |
982
|
|
|
|
|
|
|
} |
983
|
|
|
|
|
|
|
} |
984
|
|
|
|
|
|
|
|
985
|
2
|
100
|
|
|
|
21
|
if ($new->size) { |
986
|
1
|
|
|
|
|
3
|
my $insert_func = $companion."_insert"; |
987
|
1
|
|
|
|
|
12
|
for my $new_mate ($new->members) { |
988
|
3
|
50
|
33
|
|
|
168
|
if ($new_mate->can($insert_func) && |
|
|
|
33
|
|
|
|
|
989
|
|
|
|
|
|
|
$new_mate->can($includes_func) && |
990
|
|
|
|
|
|
|
!$new_mate->$includes_func($self) ) { |
991
|
3
|
|
|
|
|
8
|
$new_mate->$insert_func($self); |
992
|
|
|
|
|
|
|
} |
993
|
|
|
|
|
|
|
} |
994
|
|
|
|
|
|
|
} |
995
|
|
|
|
|
|
|
} |
996
|
|
|
|
|
|
|
} |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
=back |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
=item $instance->attribute_includes(@objects) |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
Returns true if all of the objects, or object => value pairs, are |
1003
|
|
|
|
|
|
|
present in the container. |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
=cut |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
sub _includes_X_set { |
1008
|
42
|
|
|
42
|
|
47
|
my $self = shift; |
1009
|
42
|
|
|
|
|
47
|
my $X = shift; |
1010
|
42
|
|
|
|
|
72
|
my $getter = "get_$X"; |
1011
|
42
|
|
33
|
|
|
109
|
my $a = $self->$getter || Set::Object->new(); |
1012
|
|
|
|
|
|
|
|
1013
|
42
|
|
|
|
|
200
|
my $all_there = 1; |
1014
|
42
|
|
|
|
|
46
|
my $item; |
1015
|
42
|
|
|
|
|
88
|
while (@_) { |
1016
|
42
|
50
|
33
|
|
|
175
|
if (blessed($item = shift) or reftype($item)) { |
|
|
0
|
|
|
|
|
|
1017
|
42
|
100
|
|
|
|
269
|
$all_there = 0 unless $a->includes($item); |
1018
|
|
|
|
|
|
|
} elsif (defined(my $x = ish_int($item))) { |
1019
|
0
|
0
|
|
|
|
0
|
$all_there = 0 if $x > $a->size; |
1020
|
|
|
|
|
|
|
} else { |
1021
|
0
|
|
|
|
|
0
|
carp("Searched for non-reference `$item' in set"); |
1022
|
|
|
|
|
|
|
} |
1023
|
42
|
100
|
|
|
|
126
|
last unless $all_there; |
1024
|
|
|
|
|
|
|
} |
1025
|
42
|
|
|
|
|
404
|
return $all_there; |
1026
|
|
|
|
|
|
|
} |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
sub _includes_X_ref { |
1029
|
5
|
|
|
5
|
|
12
|
my $self = shift; |
1030
|
5
|
|
|
|
|
8
|
my $X = shift; |
1031
|
5
|
|
|
|
|
10
|
my $getter = "get_$X"; |
1032
|
|
|
|
|
|
|
|
1033
|
5
|
|
|
|
|
7
|
my $all_there = 1; |
1034
|
5
|
|
|
|
|
16
|
while (@_) { |
1035
|
5
|
50
|
|
|
|
23
|
if (blessed(my $item = shift)) { |
|
|
0
|
|
|
|
|
|
1036
|
5
|
100
|
|
|
|
25
|
$all_there = 0 |
1037
|
|
|
|
|
|
|
unless (refaddr($self->$getter) == refaddr($item)); |
1038
|
|
|
|
|
|
|
} elsif (defined(my $x = ish_int($item))) { |
1039
|
0
|
0
|
|
|
|
0
|
$all_there = 0 if $x; |
1040
|
|
|
|
|
|
|
} |
1041
|
5
|
100
|
|
|
|
21
|
last unless $all_there; |
1042
|
|
|
|
|
|
|
} |
1043
|
5
|
|
|
|
|
42
|
return $all_there; |
1044
|
|
|
|
|
|
|
} |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
sub _includes_X_array { |
1047
|
7
|
|
|
7
|
|
12
|
my $self = shift; |
1048
|
7
|
|
|
|
|
9
|
my $X = shift; |
1049
|
7
|
|
|
|
|
14
|
my $getter = "get_$X"; |
1050
|
7
|
|
50
|
|
|
24
|
my $a = $self->$getter || []; |
1051
|
|
|
|
|
|
|
|
1052
|
7
|
|
|
|
|
10
|
my $all_there = 1; |
1053
|
7
|
|
|
|
|
7
|
my $members; |
1054
|
7
|
|
|
|
|
17
|
while (@_) { |
1055
|
7
|
50
|
|
|
|
31
|
if (blessed(my $item = shift)) { |
|
|
0
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
# includes without a key, d'oh! convert to set |
1057
|
7
|
|
33
|
|
|
50
|
$members ||= Set::Object->new(@$a); |
1058
|
7
|
100
|
|
|
|
42
|
$all_there = 0 unless $members->includes($item); |
1059
|
|
|
|
|
|
|
} elsif (defined(my $x = ish_int($item))) { |
1060
|
0
|
0
|
0
|
|
|
0
|
$all_there = 0, last unless ($x >= 0 && $x < @$a); |
1061
|
0
|
0
|
|
|
|
0
|
if (blessed($_[0])) { |
1062
|
0
|
|
|
|
|
0
|
$item = shift; |
1063
|
0
|
0
|
|
|
|
0
|
$all_there = 0 unless (refaddr($a->[$x]) == refaddr($item)); |
1064
|
|
|
|
|
|
|
} |
1065
|
|
|
|
|
|
|
} |
1066
|
7
|
100
|
|
|
|
20
|
last unless $all_there; |
1067
|
|
|
|
|
|
|
} |
1068
|
7
|
|
|
|
|
61
|
return $all_there; |
1069
|
|
|
|
|
|
|
} |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
sub _includes_X_hash { |
1072
|
4
|
|
|
4
|
|
6
|
my $self = shift; |
1073
|
4
|
|
|
|
|
7
|
my $X = shift; |
1074
|
4
|
|
|
|
|
8
|
my $getter = "get_$X"; |
1075
|
4
|
|
50
|
|
|
12
|
my $a = $self->$getter || {}; |
1076
|
|
|
|
|
|
|
|
1077
|
4
|
|
|
|
|
5
|
my $all_there = 1; |
1078
|
4
|
|
|
|
|
7
|
my $members; |
1079
|
4
|
|
|
|
|
12
|
while (@_) { |
1080
|
4
|
50
|
|
|
|
18
|
if (blessed(my $item = shift)) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
# includes without a key, d'oh! convert to set |
1082
|
4
|
|
33
|
|
|
49
|
$members ||= Set::Object->new(values %$a); |
1083
|
4
|
50
|
|
|
|
28
|
$all_there = 0 unless $members->includes($item); |
1084
|
|
|
|
|
|
|
} elsif (defined(my $x = ish_int($item))) { |
1085
|
|
|
|
|
|
|
# lookup by index, ignore key for now |
1086
|
|
|
|
|
|
|
next |
1087
|
0
|
|
|
|
|
0
|
} elsif (!ref($item)) { |
1088
|
|
|
|
|
|
|
# lookup by hash key |
1089
|
0
|
0
|
|
|
|
0
|
$all_there = 0, last unless exists $a->{$item}; |
1090
|
0
|
0
|
|
|
|
0
|
if (blessed($_[0])) { |
1091
|
0
|
|
|
|
|
0
|
my $key; |
1092
|
0
|
|
|
|
|
0
|
($key, $item) = ($item, shift); |
1093
|
0
|
0
|
|
|
|
0
|
$all_there = 0 unless refaddr($a->{$key}) == refaddr($item); |
1094
|
|
|
|
|
|
|
} |
1095
|
|
|
|
|
|
|
} |
1096
|
4
|
50
|
|
|
|
18
|
last unless $all_there; |
1097
|
|
|
|
|
|
|
} |
1098
|
4
|
|
|
|
|
57
|
return $all_there; |
1099
|
|
|
|
|
|
|
} |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
=item $instance->attribute_insert([key] => $object, [...]) |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
Inserts all of the items into the collection. |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
Where possible, if the collection type can avoid a collision (perhaps |
1107
|
|
|
|
|
|
|
by duplicating an entry for a key or inserting a slot into an ordered |
1108
|
|
|
|
|
|
|
list), then such action is taken. |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
If you're inserting a list of objects into an array by number, ensure |
1111
|
|
|
|
|
|
|
that you list the keys in order, unless you know what you're doing. |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
eg |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
$obj->myarray_insert( 1 => $obj1, 2 => $obj2, 1 => $obj3 ) |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
will yield |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
$obj->myarray() == ( $obj3, $obj1, $obj2 ); |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
Empty slots are shifted along with the rest of them. |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
=cut |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
sub _insert_X_ref { |
1126
|
3
|
|
|
3
|
|
5
|
my $self = shift; |
1127
|
3
|
|
|
|
|
6
|
my $X = shift; |
1128
|
3
|
|
|
|
|
71
|
my $setter = "set_$X"; |
1129
|
3
|
|
|
|
|
5
|
my $getter = "get_$X"; |
1130
|
3
|
|
50
|
|
|
23
|
return $self->$setter($_[0] || scalar($self->$getter)); |
1131
|
|
|
|
|
|
|
} |
1132
|
|
|
|
|
|
|
sub _insert_X_set { |
1133
|
22
|
|
|
22
|
|
27
|
my $self = shift; |
1134
|
22
|
|
|
|
|
28
|
my $X = shift; |
1135
|
22
|
|
|
|
|
35
|
my $setter = "set_$X"; |
1136
|
22
|
|
|
|
|
32
|
my $getter = "get_$X"; |
1137
|
22
|
|
|
|
|
60
|
my @new = (scalar($self->$getter), @_); |
1138
|
22
|
|
|
|
|
63
|
return $self->$setter(@new); |
1139
|
|
|
|
|
|
|
} |
1140
|
|
|
|
|
|
|
sub _insert_X_array { |
1141
|
6
|
|
|
6
|
|
9
|
my $self = shift; |
1142
|
6
|
|
|
|
|
7
|
my $X = shift; |
1143
|
6
|
|
|
|
|
12
|
my $setter = "set_$X"; |
1144
|
6
|
|
|
|
|
9
|
my $getter = "get_$X"; |
1145
|
|
|
|
|
|
|
|
1146
|
6
|
|
|
|
|
14
|
my @ncc = $self->$getter(); |
1147
|
6
|
|
|
|
|
16
|
while (@_) { |
1148
|
6
|
|
|
|
|
39
|
my ($value) = (shift @_); |
1149
|
6
|
50
|
|
|
|
23
|
if (blessed($value)) { |
1150
|
6
|
50
|
|
|
|
35
|
if ($value->isa("Set::Object")) { |
1151
|
0
|
|
|
|
|
0
|
push @ncc, $value->members(); |
1152
|
|
|
|
|
|
|
} else { |
1153
|
6
|
|
|
|
|
22
|
push @ncc, $value; |
1154
|
|
|
|
|
|
|
} |
1155
|
|
|
|
|
|
|
} else { |
1156
|
0
|
|
|
|
|
0
|
my $ref = ref $value; |
1157
|
0
|
0
|
|
|
|
0
|
if ($ref eq "ARRAY") { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1158
|
0
|
|
|
|
|
0
|
push @ncc, @$value; |
1159
|
|
|
|
|
|
|
} elsif ($ref eq "HASH") { |
1160
|
0
|
|
|
|
|
0
|
push @ncc, values %$value; |
1161
|
|
|
|
|
|
|
} elsif (defined(ish_int($value))) { |
1162
|
|
|
|
|
|
|
# FIXME - what about $object->insert(7 => \@obj) ? |
1163
|
0
|
|
|
|
|
0
|
@ncc = (@ncc[0..$value-1], (shift @_), |
1164
|
|
|
|
|
|
|
@ncc[$value..$#ncc]); |
1165
|
|
|
|
|
|
|
} else { |
1166
|
|
|
|
|
|
|
# some other type of key, ignore it |
1167
|
|
|
|
|
|
|
} |
1168
|
|
|
|
|
|
|
} |
1169
|
|
|
|
|
|
|
} |
1170
|
6
|
|
|
|
|
28
|
return $self->$setter(@ncc); |
1171
|
|
|
|
|
|
|
} |
1172
|
|
|
|
|
|
|
sub _insert_X_hash { |
1173
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
1174
|
2
|
|
|
|
|
4
|
my $X = shift; |
1175
|
2
|
|
|
|
|
6
|
my $setter = "set_$X"; |
1176
|
2
|
|
|
|
|
5
|
my $getter = "${X}_pairs"; |
1177
|
2
|
|
|
|
|
33
|
return $self->$setter($self->$getter, @_); |
1178
|
|
|
|
|
|
|
} |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
=item $instance->attribute_replace([key] => $object, [...]) |
1182
|
|
|
|
|
|
|
|
1183
|
|
|
|
|
|
|
"Replace" is, for the most part, identical to "insert". However, if |
1184
|
|
|
|
|
|
|
collisions occur (whatever that means for the collection type you are |
1185
|
|
|
|
|
|
|
inserting to), then the target will be replaced, no duplications of |
1186
|
|
|
|
|
|
|
elements will occur in collection types supporting duplicates. |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
=cut |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
sub _replace_X_ref { |
1191
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1192
|
0
|
|
|
|
|
0
|
my $X = shift; |
1193
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1194
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1195
|
0
|
|
|
|
|
0
|
return $self->$setter((@_, scalar($self->$getter))[0]); |
1196
|
|
|
|
|
|
|
} |
1197
|
|
|
|
|
|
|
sub _replace_X_set { |
1198
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1199
|
0
|
|
|
|
|
0
|
my $X = shift; |
1200
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1201
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1202
|
0
|
|
|
|
|
0
|
return $self->$setter(scalar($self->$getter), @_); |
1203
|
|
|
|
|
|
|
} |
1204
|
|
|
|
|
|
|
sub _replace_X_array { |
1205
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1206
|
0
|
|
|
|
|
0
|
my $X = shift; |
1207
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1208
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1209
|
0
|
|
|
|
|
0
|
return $self->$setter(scalar($self->$getter), @_); |
1210
|
|
|
|
|
|
|
} |
1211
|
|
|
|
|
|
|
sub _replace_X_hash { |
1212
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1213
|
0
|
|
|
|
|
0
|
my $X = shift; |
1214
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1215
|
0
|
|
|
|
|
0
|
my $getter = "${X}_pairs"; |
1216
|
0
|
|
|
|
|
0
|
return $self->$setter(scalar($self->$getter), @_); |
1217
|
|
|
|
|
|
|
} |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
=item $instance->attribute_pairs |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
=cut |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
sub _pairs_X_ref { |
1225
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1226
|
0
|
|
|
|
|
0
|
my $X = shift; |
1227
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1228
|
0
|
|
|
|
|
0
|
return map { ("" => $_) } $self->$getter(@_); |
|
0
|
|
|
|
|
0
|
|
1229
|
|
|
|
|
|
|
} |
1230
|
|
|
|
|
|
|
sub _pairs_X_set { |
1231
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1232
|
0
|
|
|
|
|
0
|
my $X = shift; |
1233
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1234
|
0
|
|
|
|
|
0
|
return map { ("" => $_) } $self->$getter(@_); |
|
0
|
|
|
|
|
0
|
|
1235
|
|
|
|
|
|
|
} |
1236
|
|
|
|
|
|
|
sub _pairs_X_array { |
1237
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1238
|
0
|
|
|
|
|
0
|
my $X = shift; |
1239
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1240
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1241
|
0
|
|
|
|
|
0
|
my $n = 0; |
1242
|
0
|
|
|
|
|
0
|
return map { ($n++ => $_) } $self->$getter(@_); |
|
0
|
|
|
|
|
0
|
|
1243
|
|
|
|
|
|
|
} |
1244
|
|
|
|
|
|
|
sub _pairs_X_hash { |
1245
|
4
|
|
|
4
|
|
7
|
my $self = shift; |
1246
|
4
|
|
|
|
|
9
|
my $X = shift; |
1247
|
4
|
|
|
|
|
15
|
my $getter = "get_$X"; |
1248
|
4
|
|
|
|
|
7
|
return %{$self->$getter} |
|
4
|
|
|
|
|
19
|
|
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
=item $instance->attribute_size |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
FETCHSIZE |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
=cut |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
sub _size_X_ref { |
1258
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1259
|
0
|
|
|
|
|
0
|
my $X = shift; |
1260
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1261
|
0
|
0
|
|
|
|
0
|
return ($self->$getter ? 1 : 0); |
1262
|
|
|
|
|
|
|
} |
1263
|
|
|
|
|
|
|
sub _size_X_set { |
1264
|
9
|
|
|
9
|
|
17
|
my $self = shift; |
1265
|
9
|
|
|
|
|
14
|
my $X = shift; |
1266
|
9
|
|
|
|
|
20
|
my $getter = "get_$X"; |
1267
|
9
|
|
|
|
|
39
|
return $self->$getter->size(); |
1268
|
|
|
|
|
|
|
} |
1269
|
|
|
|
|
|
|
sub _size_X_array { |
1270
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1271
|
0
|
|
|
|
|
0
|
my $X = shift; |
1272
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1273
|
0
|
|
|
|
|
0
|
return scalar(@{$self->$getter}); |
|
0
|
|
|
|
|
0
|
|
1274
|
|
|
|
|
|
|
} |
1275
|
|
|
|
|
|
|
sub _size_X_hash { |
1276
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1277
|
0
|
|
|
|
|
0
|
my $X = shift; |
1278
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1279
|
0
|
|
|
|
|
0
|
return scalar(keys %{$self->$getter}); |
|
0
|
|
|
|
|
0
|
|
1280
|
|
|
|
|
|
|
} |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
=item $instance->attribute_clear |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
Empties a collection |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
=cut |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
sub _clear_X_ref { |
1290
|
3
|
|
|
3
|
|
8
|
my $self = shift; |
1291
|
3
|
|
|
|
|
8
|
my $X = shift; |
1292
|
3
|
|
|
|
|
10
|
my $setter = "set_$X"; |
1293
|
3
|
|
|
|
|
13
|
return ($self->$setter()); |
1294
|
|
|
|
|
|
|
} |
1295
|
3
|
|
|
3
|
|
16
|
sub _clear_X_set { _clear_X_ref(@_) } |
1296
|
0
|
|
|
0
|
|
0
|
sub _clear_X_array { _clear_X_ref(@_) } |
1297
|
0
|
|
|
0
|
|
0
|
sub _clear_X_hash { _clear_X_ref(@_) } |
1298
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
=item $instance->attribute_push |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
Place an element on the end of a collection; identical to foo_insert |
1302
|
|
|
|
|
|
|
without an index. |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
=cut |
1305
|
|
|
|
|
|
|
|
1306
|
0
|
|
|
0
|
|
0
|
sub _push_X_ref { _insert_X_ref(@_) } |
1307
|
1
|
|
|
1
|
|
3
|
sub _push_X_set { _insert_X_set(@_) } |
1308
|
1
|
|
|
1
|
|
4
|
sub _push_X_array { _insert_X_array(@_) } |
1309
|
0
|
|
|
0
|
|
0
|
sub _push_X_hash { _insert_X_hash(@_) } |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
=item $instance->attribute_unshift |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
Place an element on the end of a collection; identical to foo_insert |
1314
|
|
|
|
|
|
|
without an index. |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
=cut |
1317
|
|
|
|
|
|
|
|
1318
|
0
|
|
|
0
|
|
0
|
sub _unshift_X_ref { _insert_X_ref(@_) } |
1319
|
0
|
|
|
0
|
|
0
|
sub _unshift_X_set { _insert_X_set(@_) } |
1320
|
|
|
|
|
|
|
sub _unshift_X_array { |
1321
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1322
|
0
|
|
|
|
|
0
|
my $X = shift; |
1323
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1324
|
0
|
|
|
|
|
0
|
my @ncc = $self->$getter(); |
1325
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1326
|
0
|
|
|
|
|
0
|
return $self->$setter(@_, @ncc); |
1327
|
|
|
|
|
|
|
} |
1328
|
0
|
|
|
0
|
|
0
|
sub _unshift_X_hash { _insert_X_hash(@_) } |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
=item $instance->attribute_pop |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
Returns the last element in a collection, and deletes that item from |
1333
|
|
|
|
|
|
|
the collection, but not necessarily in that order. No parameters are |
1334
|
|
|
|
|
|
|
accepted. |
1335
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
=cut |
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
sub _pop_X_ref { |
1339
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1340
|
0
|
|
|
|
|
0
|
my $X = shift; |
1341
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1342
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1343
|
0
|
0
|
|
|
|
0
|
if (wantarray) { |
1344
|
0
|
|
|
|
|
0
|
my @rv = ($self->$getter()); |
1345
|
0
|
|
|
|
|
0
|
$self->$setter(); |
1346
|
0
|
|
|
|
|
0
|
return @rv; |
1347
|
|
|
|
|
|
|
} else { |
1348
|
0
|
|
|
|
|
0
|
my $rv = $self->$getter(); |
1349
|
0
|
|
|
|
|
0
|
$self->$setter(); |
1350
|
0
|
|
|
|
|
0
|
return $rv; |
1351
|
|
|
|
|
|
|
} |
1352
|
|
|
|
|
|
|
} |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
sub _pop_X_set { |
1355
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1356
|
0
|
|
|
|
|
0
|
my $X = shift; |
1357
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1358
|
|
|
|
|
|
|
# sets don't have an order, so just delete any member |
1359
|
0
|
0
|
|
|
|
0
|
if (my $val = $self->$getter(0)) { |
1360
|
0
|
|
|
|
|
0
|
my $toaster = "${X}_remove"; |
1361
|
0
|
|
|
|
|
0
|
$self->$toaster($val); |
1362
|
0
|
|
|
|
|
0
|
return $val; |
1363
|
|
|
|
|
|
|
} else { |
1364
|
0
|
0
|
|
|
|
0
|
return (wantarray ? () : undef); |
1365
|
|
|
|
|
|
|
} |
1366
|
|
|
|
|
|
|
} |
1367
|
|
|
|
|
|
|
sub _pop_X_array { |
1368
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1369
|
0
|
|
|
|
|
0
|
my $X = shift; |
1370
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1371
|
0
|
|
|
|
|
0
|
my @ncc = $self->$getter(); |
1372
|
0
|
|
|
|
|
0
|
my $rv = pop @ncc; |
1373
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1374
|
0
|
|
|
|
|
0
|
$self->$setter(@ncc); |
1375
|
0
|
|
|
|
|
0
|
return $rv; |
1376
|
|
|
|
|
|
|
} |
1377
|
|
|
|
|
|
|
sub _pop_X_hash { |
1378
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1379
|
0
|
|
|
|
|
0
|
my $X = shift; |
1380
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1381
|
0
|
|
|
|
|
0
|
my $killer = "${X}_remove"; |
1382
|
0
|
|
|
|
|
0
|
my $hashref = $self->$getter(); |
1383
|
0
|
|
|
|
|
0
|
my ($key, $gonner) = (each %$hashref); |
1384
|
0
|
0
|
|
|
|
0
|
$self->$killer($key => $gonner) if defined $key; |
1385
|
0
|
|
|
|
|
0
|
return $gonner; |
1386
|
|
|
|
|
|
|
} |
1387
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
=item $instance->attribute_shift |
1389
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
Remove an element on the beginning of a collection, and return it |
1391
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
=cut |
1393
|
|
|
|
|
|
|
|
1394
|
0
|
|
|
0
|
|
0
|
sub _shift_X_ref { _pop_X_ref(@_) } |
1395
|
0
|
|
|
0
|
|
0
|
sub _shift_X_set { _pop_X_set(@_) } |
1396
|
|
|
|
|
|
|
sub _shift_X_array { |
1397
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1398
|
0
|
|
|
|
|
0
|
my $X = shift; |
1399
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1400
|
0
|
|
|
|
|
0
|
my @ncc = $self->$getter(); |
1401
|
0
|
|
|
|
|
0
|
my $rv = shift @ncc; |
1402
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1403
|
0
|
|
|
|
|
0
|
$self->$setter(@ncc); |
1404
|
0
|
|
|
|
|
0
|
return $rv; |
1405
|
|
|
|
|
|
|
} |
1406
|
0
|
|
|
0
|
|
0
|
sub _shift_X_hash { _pop_X_hash(@_) } |
1407
|
|
|
|
|
|
|
|
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
=item $instance->attribute_splice($offset, $length, @objects) |
1410
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
Pretends that the collection is an array and splices it. |
1412
|
|
|
|
|
|
|
|
1413
|
|
|
|
|
|
|
=cut |
1414
|
|
|
|
|
|
|
|
1415
|
0
|
|
|
0
|
|
0
|
sub _splice_X_ref { _splice_X_array(@_) } |
1416
|
0
|
|
|
0
|
|
0
|
sub _splice_X_set { _splice_X_array(@_) } |
1417
|
|
|
|
|
|
|
sub _splice_X_array { |
1418
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1419
|
0
|
|
|
|
|
0
|
my $X = shift; |
1420
|
0
|
|
|
|
|
0
|
my $getter = "get_${X}"; |
1421
|
0
|
|
|
|
|
0
|
my $setter = "set_${X}"; |
1422
|
0
|
|
|
|
|
0
|
my @list = $self->$getter(); |
1423
|
0
|
0
|
|
|
|
0
|
if (wantarray) { |
1424
|
0
|
|
|
|
|
0
|
my @rv = splice @list, @_; |
1425
|
0
|
|
|
|
|
0
|
$self->$setter(@list); |
1426
|
0
|
|
|
|
|
0
|
return @rv; |
1427
|
|
|
|
|
|
|
} else { |
1428
|
0
|
|
|
|
|
0
|
my $rv = splice @list, @_; |
1429
|
0
|
|
|
|
|
0
|
$self->$setter(@list); |
1430
|
0
|
|
|
|
|
0
|
return $rv; |
1431
|
|
|
|
|
|
|
} |
1432
|
|
|
|
|
|
|
} |
1433
|
|
|
|
|
|
|
sub _splice_X_hash { |
1434
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1435
|
0
|
|
|
|
|
0
|
my $X = shift; |
1436
|
0
|
|
|
|
|
0
|
my $getter = "${X}_pairs"; |
1437
|
0
|
|
|
|
|
0
|
my $setter = "set_${X}"; |
1438
|
0
|
|
|
|
|
0
|
my @list = $self->$getter(); |
1439
|
0
|
0
|
|
|
|
0
|
if (wantarray) { |
1440
|
0
|
|
|
|
|
0
|
my @rv = splice @list, @_; |
1441
|
0
|
|
|
|
|
0
|
$self->$setter(@list); |
1442
|
0
|
|
|
|
|
0
|
return @rv; |
1443
|
|
|
|
|
|
|
} else { |
1444
|
0
|
|
|
|
|
0
|
my $rv = splice @list, @_; |
1445
|
0
|
|
|
|
|
0
|
$self->$setter(@list); |
1446
|
0
|
|
|
|
|
0
|
return $rv; |
1447
|
|
|
|
|
|
|
} |
1448
|
|
|
|
|
|
|
} |
1449
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
=item $instance->attribute_remove(@objects) |
1451
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
translates logically to a search for that item or index, followed by a |
1453
|
|
|
|
|
|
|
delete |
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
This suite of functions applies to attributes that are sets (C<iset> |
1456
|
|
|
|
|
|
|
or C<set>). It could in theory also apply generally to all |
1457
|
|
|
|
|
|
|
collections - ie also arrays (C<iarray> or C<array>), and hashes |
1458
|
|
|
|
|
|
|
(C<hash>, C<ihash>). |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
All of these modifications build a new container, then call |
1461
|
|
|
|
|
|
|
$object->set_attribute($container) |
1462
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
It is up to the set_attribute() function to update all related |
1464
|
|
|
|
|
|
|
classes. |
1465
|
|
|
|
|
|
|
|
1466
|
|
|
|
|
|
|
=cut |
1467
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
sub _listify { |
1469
|
16
|
50
|
|
16
|
|
25
|
map { (blessed($_) |
|
16
|
0
|
|
|
|
198
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1470
|
|
|
|
|
|
|
? ( |
1471
|
|
|
|
|
|
|
$_->isa("Set::Object") |
1472
|
|
|
|
|
|
|
? $_->members() |
1473
|
|
|
|
|
|
|
: $_ |
1474
|
|
|
|
|
|
|
) |
1475
|
|
|
|
|
|
|
: (ref $_ eq "HASH" |
1476
|
|
|
|
|
|
|
? (keys %$_) |
1477
|
|
|
|
|
|
|
: (ref $_ eq "ARRAY" |
1478
|
|
|
|
|
|
|
? @$_ |
1479
|
|
|
|
|
|
|
: ()))) } @_ |
1480
|
|
|
|
|
|
|
} |
1481
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
sub _remove_X_ref { |
1483
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
1484
|
1
|
|
|
|
|
2
|
my $X = shift; |
1485
|
1
|
|
|
|
|
4
|
my $setter = "set_$X"; |
1486
|
1
|
|
|
|
|
4
|
my $getter = "get_$X"; |
1487
|
1
|
|
|
|
|
5
|
my $remove = Set::Object->new(_listify(@_)); |
1488
|
1
|
|
|
|
|
4
|
return $self->$setter(grep { !$remove->includes($_) } |
|
1
|
|
|
|
|
212
|
|
1489
|
|
|
|
|
|
|
$self->$getter); |
1490
|
|
|
|
|
|
|
} |
1491
|
|
|
|
|
|
|
sub _remove_X_set { |
1492
|
15
|
|
|
15
|
|
18
|
my $self = shift; |
1493
|
15
|
|
|
|
|
21
|
my $X = shift; |
1494
|
15
|
|
|
|
|
28
|
my $setter = "set_$X"; |
1495
|
15
|
|
|
|
|
27
|
my $getter = "get_$X"; |
1496
|
15
|
|
|
|
|
28
|
my $remove = Set::Object->new(_listify(@_)); |
1497
|
15
|
|
|
|
|
47
|
return $self->$setter(grep { !$remove->includes($_) } |
|
24
|
|
|
|
|
87
|
|
1498
|
|
|
|
|
|
|
$self->$getter); |
1499
|
|
|
|
|
|
|
} |
1500
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
sub _remove_X_array { |
1502
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1503
|
0
|
|
|
|
|
0
|
my $X = shift; |
1504
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1505
|
0
|
|
|
|
|
0
|
my $getter = "get_$X"; |
1506
|
0
|
|
|
|
|
0
|
my @new = ($self->$getter); |
1507
|
0
|
|
|
|
|
0
|
my %gone; |
1508
|
0
|
|
|
|
|
0
|
while (@_) { |
1509
|
0
|
|
|
|
|
0
|
my $item = shift; |
1510
|
0
|
0
|
|
|
|
0
|
if (blessed($item)) { |
|
|
0
|
|
|
|
|
|
1511
|
0
|
|
|
|
|
0
|
for (my $i = 0; $i < @new; $i++) { |
1512
|
0
|
0
|
|
|
|
0
|
$gone{$i} = 1, last |
1513
|
|
|
|
|
|
|
if (refaddr($item) == refaddr($new[$i])); |
1514
|
|
|
|
|
|
|
} |
1515
|
|
|
|
|
|
|
} elsif (defined(ish_int($item))) { |
1516
|
0
|
|
|
|
|
0
|
$gone{$item} = 1; |
1517
|
|
|
|
|
|
|
} else { |
1518
|
0
|
|
|
|
|
0
|
for (my $i = 0; $i < @new; $i++) { |
1519
|
0
|
0
|
|
|
|
0
|
$gone{$i} = 1, last unless $gone{$i}; |
1520
|
|
|
|
|
|
|
} |
1521
|
|
|
|
|
|
|
} |
1522
|
|
|
|
|
|
|
} |
1523
|
0
|
|
|
|
|
0
|
delete @new[keys %gone]; |
1524
|
0
|
|
|
|
|
0
|
return $self->$setter(@new); |
1525
|
|
|
|
|
|
|
} |
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
sub _remove_X_hash { |
1528
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1529
|
0
|
|
|
|
|
0
|
my $X = shift; |
1530
|
0
|
|
|
|
|
0
|
my $getter = "${X}_pairs"; |
1531
|
0
|
|
|
|
|
0
|
my $setter = "set_$X"; |
1532
|
0
|
|
|
|
|
0
|
my %new = $self->$getter(); |
1533
|
0
|
|
|
|
|
0
|
while (@_) { |
1534
|
0
|
|
|
|
|
0
|
my $item = shift; |
1535
|
|
|
|
|
|
|
|
1536
|
0
|
0
|
|
|
|
0
|
if (blessed($item)) { |
|
|
0
|
|
|
|
|
|
1537
|
0
|
|
|
|
|
0
|
while (my ($k, $v) = each %new) { |
1538
|
0
|
0
|
|
|
|
0
|
$item = $k if (refaddr($item) == refaddr($v)); |
1539
|
|
|
|
|
|
|
} |
1540
|
|
|
|
|
|
|
} elsif (blessed($_[0])) { |
1541
|
|
|
|
|
|
|
# FIXME - only delete if the values match, perhaps? |
1542
|
|
|
|
|
|
|
} |
1543
|
|
|
|
|
|
|
|
1544
|
0
|
0
|
|
|
|
0
|
($item) = next %new if (defined(ish_int($item))); |
1545
|
|
|
|
|
|
|
|
1546
|
0
|
|
|
|
|
0
|
delete $new{$item}; |
1547
|
|
|
|
|
|
|
} |
1548
|
0
|
|
|
|
|
0
|
return $self->$setter(%new); |
1549
|
|
|
|
|
|
|
} |
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
|
1553
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
=back |
1555
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
B<Note:> The above functions can be overridden, but they may not be |
1557
|
|
|
|
|
|
|
called with the C<$self-E<gt>SUPER::> superclass chaining method. |
1558
|
|
|
|
|
|
|
This is because they are not defined within the scope of |
1559
|
|
|
|
|
|
|
Class::Tangram, only your package. |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
=cut |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
=head1 ATTRIBUTE TYPE CHECKING |
1564
|
|
|
|
|
|
|
|
1565
|
|
|
|
|
|
|
Class::Tangram provides type checking of attributes when attributes |
1566
|
|
|
|
|
|
|
are set - either using the default C<set_attribute> functions, or |
1567
|
|
|
|
|
|
|
created via the C<new> constructor. |
1568
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
The checking has default behaviour for each type of attribute (see |
1570
|
|
|
|
|
|
|
L<Default Type Checking>), and can be extended arbitrarily via a |
1571
|
|
|
|
|
|
|
per-attribute C<check_func>, described below. Critical attributes can |
1572
|
|
|
|
|
|
|
be marked as such with the C<required> flag. |
1573
|
|
|
|
|
|
|
|
1574
|
|
|
|
|
|
|
The specification of this type checking is placed in the class schema, |
1575
|
|
|
|
|
|
|
in the per-attribute B<options hash>. This is a Class::Tangram |
1576
|
|
|
|
|
|
|
extension to the Tangram schema structure. |
1577
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
=over |
1579
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
=item check_func |
1581
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
A function that is called with a B<reference> to the new value in |
1583
|
|
|
|
|
|
|
C<$_[0]>. It should call C<die()> if the value is bad. Note that |
1584
|
|
|
|
|
|
|
this check_func will never be passed an undefined value; this is |
1585
|
|
|
|
|
|
|
covered by the "required" option, below. |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
In the example schema (above), the attribute C<segments> has a |
1588
|
|
|
|
|
|
|
C<check_func> that prevents setting the value to anything greater than |
1589
|
|
|
|
|
|
|
30. Note that it does not prevent you from setting the value to |
1590
|
|
|
|
|
|
|
something that is not an integer; if you define a C<check_func>, it |
1591
|
|
|
|
|
|
|
replaces the default. |
1592
|
|
|
|
|
|
|
|
1593
|
|
|
|
|
|
|
=item required |
1594
|
|
|
|
|
|
|
|
1595
|
|
|
|
|
|
|
If this option is set to a true value, then the attribute must be set |
1596
|
|
|
|
|
|
|
to a true value to pass type checking. For string attributes, this |
1597
|
|
|
|
|
|
|
means that the string must be defined and non-empty (so "0" is true). |
1598
|
|
|
|
|
|
|
For other attribute types, the normal Perl definition of logical truth |
1599
|
|
|
|
|
|
|
is used. |
1600
|
|
|
|
|
|
|
|
1601
|
|
|
|
|
|
|
If the required option is defined but logically false, (ie "" or 0), |
1602
|
|
|
|
|
|
|
then the attribute must also be defined, but may be set to a logically |
1603
|
|
|
|
|
|
|
false value. |
1604
|
|
|
|
|
|
|
|
1605
|
|
|
|
|
|
|
If the required option is undefined, then the attribute may be set to |
1606
|
|
|
|
|
|
|
an undefined value. |
1607
|
|
|
|
|
|
|
|
1608
|
|
|
|
|
|
|
For integration with tangram, the C<new()> function has a special |
1609
|
|
|
|
|
|
|
hack; if it is being invoked from within Tangram, then the required |
1610
|
|
|
|
|
|
|
test is skipped. |
1611
|
|
|
|
|
|
|
|
1612
|
|
|
|
|
|
|
=back |
1613
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
=head2 Other per-attribute options |
1615
|
|
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
Any of the following options may be inserted into the per-attribute |
1617
|
|
|
|
|
|
|
B<options hash>: |
1618
|
|
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
=over |
1620
|
|
|
|
|
|
|
|
1621
|
|
|
|
|
|
|
=item init_default |
1622
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
This value specifies the default value of the attribute when |
1624
|
|
|
|
|
|
|
it is created with C<new()>. It is a scalar value, it is |
1625
|
|
|
|
|
|
|
copied to the fresh object. If it is a code reference, that |
1626
|
|
|
|
|
|
|
code reference is called and its return value inserted into |
1627
|
|
|
|
|
|
|
the attribute. If it is an ARRAY or HASH reference, then |
1628
|
|
|
|
|
|
|
that array or hash is COPIED into the attribute. |
1629
|
|
|
|
|
|
|
|
1630
|
|
|
|
|
|
|
=item destroy_func |
1631
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
If anything special needs to happen to this attribute before the |
1633
|
|
|
|
|
|
|
object is destroyed (or when someone calls |
1634
|
|
|
|
|
|
|
C<$object-E<gt>clear_refs()>), then define this. It is called as |
1635
|
|
|
|
|
|
|
C<$sub-E<gt>($object, "attribute")>. |
1636
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
=back |
1638
|
|
|
|
|
|
|
|
1639
|
|
|
|
|
|
|
=head2 Default Type Checking |
1640
|
|
|
|
|
|
|
|
1641
|
|
|
|
|
|
|
Default type checking s |
1642
|
|
|
|
|
|
|
|
1643
|
|
|
|
|
|
|
=cut |
1644
|
|
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
=over |
1647
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
=item check_X (\$value) |
1649
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
This series of internal functions are built-in C<check_func> functions |
1651
|
|
|
|
|
|
|
defined for all of the standard Tangram attribute types. |
1652
|
|
|
|
|
|
|
|
1653
|
|
|
|
|
|
|
=over |
1654
|
|
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
=item check_string |
1656
|
|
|
|
|
|
|
|
1657
|
|
|
|
|
|
|
checks that the supplied value is less than 255 characters long. |
1658
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
=cut |
1660
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
sub check_string { |
1662
|
1
|
|
|
|
|
190
|
croak "string too long (${$_[0]})" |
|
86
|
|
|
|
|
258
|
|
1663
|
86
|
100
|
|
86
|
1
|
92
|
if (length ${$_[0]} > 255); |
1664
|
|
|
|
|
|
|
} |
1665
|
|
|
|
|
|
|
|
1666
|
|
|
|
|
|
|
=item check_int |
1667
|
|
|
|
|
|
|
|
1668
|
|
|
|
|
|
|
checks that the value is a (possibly signed) integer |
1669
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
=cut |
1671
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
sub check_int { |
1673
|
7
|
|
|
7
|
|
104
|
no warnings; |
|
7
|
|
|
|
|
18
|
|
|
7
|
|
|
|
|
10485
|
|
1674
|
1
|
|
|
|
|
140
|
croak "not an integer (${$_[0]})" |
|
9
|
|
|
|
|
64
|
|
1675
|
9
|
100
|
100
|
9
|
1
|
18
|
unless (is_int ${$_[0]} or ${$_[0]}+0 eq ${$_[0]}); |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
25
|
|
1676
|
|
|
|
|
|
|
} |
1677
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
=item check_real |
1679
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
checks that the value is a real number, by stringifying it and |
1681
|
|
|
|
|
|
|
matching it against (C<m/^-?\d*(\.\d*)?(e-?\d*)?$/>). Inefficient? |
1682
|
|
|
|
|
|
|
Yes. Patches welcome. |
1683
|
|
|
|
|
|
|
|
1684
|
|
|
|
|
|
|
With my cries for help, where are the user-submitted patches?! Well, |
1685
|
|
|
|
|
|
|
this function now checks the scalar flags that indicate that it |
1686
|
|
|
|
|
|
|
contains a number, which isn't flawless, but a lot faster :) |
1687
|
|
|
|
|
|
|
|
1688
|
|
|
|
|
|
|
=cut |
1689
|
|
|
|
|
|
|
|
1690
|
|
|
|
|
|
|
my $real_re = qr/^-?\d*(\.\d*)?(e-?\d*)?$/; |
1691
|
|
|
|
|
|
|
sub check_real { |
1692
|
1
|
|
|
|
|
117
|
croak "not a real number (${$_[0]})" |
|
7
|
|
|
|
|
84
|
|
1693
|
7
|
|
|
|
|
37
|
unless (is_double(${$_[0]}) or is_int(${$_[0]}) |
|
7
|
|
|
|
|
73
|
|
1694
|
7
|
100
|
33
|
7
|
1
|
8
|
or ${$_[0]} =~ m/$real_re/); |
|
|
|
66
|
|
|
|
|
1695
|
|
|
|
|
|
|
} |
1696
|
|
|
|
|
|
|
|
1697
|
|
|
|
|
|
|
=item check_obj |
1698
|
|
|
|
|
|
|
|
1699
|
|
|
|
|
|
|
checks that the supplied variable is a reference to a blessed object |
1700
|
|
|
|
|
|
|
|
1701
|
|
|
|
|
|
|
=cut |
1702
|
|
|
|
|
|
|
|
1703
|
|
|
|
|
|
|
# this pattern matches a regular reference |
1704
|
|
|
|
|
|
|
sub check_obj { |
1705
|
1
|
|
|
|
|
135
|
croak "${$_[0]} is not an object reference" |
|
20
|
|
|
|
|
114
|
|
1706
|
1
|
|
|
|
|
7
|
unless (blessed ${ $_[0] } |
1707
|
20
|
100
|
66
|
20
|
1
|
26
|
or !${ $_[0] }); |
1708
|
|
|
|
|
|
|
} |
1709
|
|
|
|
|
|
|
|
1710
|
|
|
|
|
|
|
=item check_flat_array |
1711
|
|
|
|
|
|
|
|
1712
|
|
|
|
|
|
|
checks that $value is a ref ARRAY and that all elements are unblessed |
1713
|
|
|
|
|
|
|
scalars. Does NOT currently check that all values are of the correct |
1714
|
|
|
|
|
|
|
type (int vs real vs string, etc) |
1715
|
|
|
|
|
|
|
|
1716
|
|
|
|
|
|
|
=cut |
1717
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
sub check_flat_array { |
1719
|
0
|
|
|
|
|
0
|
croak "${$_[0]} is not a flat array" |
|
3
|
|
|
|
|
14
|
|
1720
|
3
|
50
|
|
3
|
1
|
3
|
if (ref ${$_[0]} ne "ARRAY"); |
1721
|
0
|
0
|
|
|
|
0
|
croak "flat array ${$_[0]} may not contain references" |
|
0
|
|
|
|
|
0
|
|
1722
|
3
|
50
|
|
|
|
4
|
if (map { (ref $_ ? "1" : ()) } @${$_[0]}); |
|
3
|
|
|
|
|
10
|
|
1723
|
|
|
|
|
|
|
} |
1724
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
=item check_rawdate |
1726
|
|
|
|
|
|
|
|
1727
|
|
|
|
|
|
|
checks that $value is of the form YYYY-MM-DD, or YYYYMMDD, or YYMMDD. |
1728
|
|
|
|
|
|
|
|
1729
|
|
|
|
|
|
|
=cut |
1730
|
|
|
|
|
|
|
|
1731
|
|
|
|
|
|
|
# YYYY-MM-DD HH:MM:SS |
1732
|
|
|
|
|
|
|
my $rawdate_re = qr/^(?: \d{4}-\d{2}-\d{2} |
1733
|
|
|
|
|
|
|
| (?:\d\d){3,4} |
1734
|
|
|
|
|
|
|
)$/x; |
1735
|
|
|
|
|
|
|
sub check_rawdate { |
1736
|
1
|
|
|
|
|
125
|
croak "invalid SQL rawdate `${$_[0]}'" |
|
2
|
|
|
|
|
18
|
|
1737
|
2
|
100
|
|
2
|
1
|
3
|
unless (${$_[0]} =~ m/$rawdate_re/o); |
1738
|
|
|
|
|
|
|
} |
1739
|
|
|
|
|
|
|
|
1740
|
|
|
|
|
|
|
=item check_rawtime |
1741
|
|
|
|
|
|
|
|
1742
|
|
|
|
|
|
|
checks that $value is of the form HH:MM(:SS)? |
1743
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
=cut |
1745
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
# YYYY-MM-DD HH:MM:SS |
1747
|
|
|
|
|
|
|
my $rawtime_re = qr/^\d{1,2}:\d{2}(?::\d{2})?$/; |
1748
|
|
|
|
|
|
|
sub check_rawtime { |
1749
|
1
|
|
|
|
|
119
|
croak "invalid SQL rawtime `${$_[0]}'" |
|
2
|
|
|
|
|
15
|
|
1750
|
2
|
100
|
|
2
|
1
|
3
|
unless (${$_[0]} =~ m/$rawtime_re/o); |
1751
|
|
|
|
|
|
|
} |
1752
|
|
|
|
|
|
|
|
1753
|
|
|
|
|
|
|
=item check_rawdatetime |
1754
|
|
|
|
|
|
|
|
1755
|
|
|
|
|
|
|
checks that $value is of the form YYYY-MM-DD HH:MM(:SS)? (the time |
1756
|
|
|
|
|
|
|
and/or the date can be missing), or a string of numbers between 6 and |
1757
|
|
|
|
|
|
|
14 numbers long. |
1758
|
|
|
|
|
|
|
|
1759
|
|
|
|
|
|
|
=cut |
1760
|
|
|
|
|
|
|
|
1761
|
|
|
|
|
|
|
my $rawdatetime_re = qr/^(?: |
1762
|
|
|
|
|
|
|
# YYYY-MM-DD HH:MM:SS |
1763
|
|
|
|
|
|
|
(?: (?:\d{4}-\d{2}-\d{2}\s+)? |
1764
|
|
|
|
|
|
|
\d{1,2}:\d{2}(?::\d{2})? |
1765
|
|
|
|
|
|
|
| \d{4}-\d{2}-\d{2} |
1766
|
|
|
|
|
|
|
) |
1767
|
|
|
|
|
|
|
| # YYMMDD, etc |
1768
|
|
|
|
|
|
|
(?:\d\d){3,7} |
1769
|
|
|
|
|
|
|
)$/x; |
1770
|
|
|
|
|
|
|
sub check_rawdatetime { |
1771
|
2
|
|
|
|
|
434
|
croak "invalid SQL rawdatetime `${$_[0]}'" |
|
9
|
|
|
|
|
79
|
|
1772
|
9
|
100
|
|
9
|
1
|
13
|
unless (${$_[0]} =~ m/$rawdatetime_re/o); |
1773
|
|
|
|
|
|
|
} |
1774
|
|
|
|
|
|
|
|
1775
|
|
|
|
|
|
|
=item check_dmdatetime |
1776
|
|
|
|
|
|
|
|
1777
|
|
|
|
|
|
|
checks that $value is of the form YYYYMMDDHH:MM:SS, or those allowed |
1778
|
|
|
|
|
|
|
for rawdatetime. |
1779
|
|
|
|
|
|
|
|
1780
|
|
|
|
|
|
|
=cut |
1781
|
|
|
|
|
|
|
|
1782
|
|
|
|
|
|
|
sub check_dmdatetime { |
1783
|
1
|
|
|
|
|
24300
|
croak "invalid dmdatetime `${$_[0]}'" |
|
2
|
|
|
|
|
75
|
|
1784
|
1
|
|
|
|
|
7
|
unless (${$_[0]} =~ m/^\d{10}:\d\d:\d\d$|$rawdatetime_re/o |
1785
|
2
|
100
|
66
|
2
|
1
|
5
|
or Date::Manip::ParseDate(${$_[0]})); |
1786
|
|
|
|
|
|
|
} |
1787
|
|
|
|
|
|
|
|
1788
|
|
|
|
|
|
|
=item check_flat_hash |
1789
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
checks that $value is a ref HASH and all values are scalars. Does NOT |
1791
|
|
|
|
|
|
|
currently check that all values are of the correct type (int vs real |
1792
|
|
|
|
|
|
|
vs string, etc) |
1793
|
|
|
|
|
|
|
|
1794
|
|
|
|
|
|
|
=cut |
1795
|
|
|
|
|
|
|
|
1796
|
|
|
|
|
|
|
sub check_flat_hash { |
1797
|
0
|
|
|
|
|
0
|
croak "${$_[0]} is not a hash" |
|
3
|
|
|
|
|
13
|
|
1798
|
3
|
50
|
|
3
|
1
|
4
|
unless (ref ${$_[0]} eq "HASH"); |
1799
|
3
|
|
|
|
|
5
|
while (my ($k, $v) = each %${$_[0]}) { |
|
3
|
|
|
|
|
12
|
|
1800
|
0
|
0
|
0
|
|
|
0
|
croak "hash not flat" |
1801
|
|
|
|
|
|
|
if (ref $k or ref $v); |
1802
|
|
|
|
|
|
|
} |
1803
|
|
|
|
|
|
|
} |
1804
|
|
|
|
|
|
|
|
1805
|
|
|
|
|
|
|
=item check_set |
1806
|
|
|
|
|
|
|
|
1807
|
|
|
|
|
|
|
Checks that the passed value is a Set::Object |
1808
|
|
|
|
|
|
|
|
1809
|
|
|
|
|
|
|
=cut |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
sub check_set { |
1812
|
0
|
|
|
|
|
0
|
confess "${$_[0]} is not a set" |
|
93
|
|
|
|
|
419
|
|
1813
|
93
|
50
|
|
93
|
1
|
105
|
unless (UNIVERSAL::isa(${$_[0]}, "Set::Object")); |
1814
|
|
|
|
|
|
|
} |
1815
|
|
|
|
|
|
|
|
1816
|
|
|
|
|
|
|
=item check_hash |
1817
|
|
|
|
|
|
|
|
1818
|
|
|
|
|
|
|
Checks that the passed value is a perl HV |
1819
|
|
|
|
|
|
|
|
1820
|
|
|
|
|
|
|
=cut |
1821
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
sub check_hash { |
1823
|
0
|
|
|
|
|
0
|
confess "${$_[0]} is not a hash" |
|
3
|
|
|
|
|
170
|
|
1824
|
3
|
50
|
|
3
|
1
|
6
|
unless (reftype(${$_[0]}) eq "HASH"); |
1825
|
|
|
|
|
|
|
} |
1826
|
|
|
|
|
|
|
|
1827
|
|
|
|
|
|
|
=item check_array |
1828
|
|
|
|
|
|
|
|
1829
|
|
|
|
|
|
|
Checks that the passed value is a perl AV |
1830
|
|
|
|
|
|
|
|
1831
|
|
|
|
|
|
|
=cut |
1832
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
sub check_array { |
1834
|
0
|
|
|
|
|
0
|
confess "${$_[0]} is not an array" |
|
7
|
|
|
|
|
43
|
|
1835
|
7
|
50
|
|
7
|
1
|
9
|
unless (reftype(${$_[0]}) eq "ARRAY"); |
1836
|
|
|
|
|
|
|
} |
1837
|
|
|
|
|
|
|
|
1838
|
|
|
|
|
|
|
=item check_nothing |
1839
|
|
|
|
|
|
|
|
1840
|
|
|
|
|
|
|
checks whether Australians like sport |
1841
|
|
|
|
|
|
|
|
1842
|
|
|
|
|
|
|
=cut |
1843
|
|
|
|
|
|
|
|
1844
|
1
|
|
|
1
|
1
|
2
|
sub check_nothing { } |
1845
|
|
|
|
|
|
|
|
1846
|
|
|
|
|
|
|
=back |
1847
|
|
|
|
|
|
|
|
1848
|
|
|
|
|
|
|
=item destroy_X ($instance, $attr) |
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
Similar story with the check_X series of functions, these are called |
1851
|
|
|
|
|
|
|
during object destruction on every attribute that has a reference that |
1852
|
|
|
|
|
|
|
might need breaking. Note: B<these functions all assume that |
1853
|
|
|
|
|
|
|
attributes belonging to an object that is being destroyed may be |
1854
|
|
|
|
|
|
|
destroyed also>. In other words, do not allow distinct objects to |
1855
|
|
|
|
|
|
|
share Set::Object containers or hash references in their attributes, |
1856
|
|
|
|
|
|
|
otherwise when one gets destroyed the others will lose their data. |
1857
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
Available functions: |
1859
|
|
|
|
|
|
|
|
1860
|
|
|
|
|
|
|
=over |
1861
|
|
|
|
|
|
|
|
1862
|
|
|
|
|
|
|
=item destroy_array |
1863
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
empties an array |
1865
|
|
|
|
|
|
|
|
1866
|
|
|
|
|
|
|
=cut |
1867
|
|
|
|
|
|
|
|
1868
|
|
|
|
|
|
|
sub destroy_array { |
1869
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
1870
|
1
|
|
|
|
|
3
|
my $attr = shift; |
1871
|
1
|
|
|
|
|
3
|
my $t = tied $self->{$attr}; |
1872
|
1
|
50
|
33
|
|
|
5
|
@{$self->{$attr}} = () |
|
1
|
|
|
|
|
2
|
|
1873
|
|
|
|
|
|
|
unless (defined $t and $t =~ m,Tangram::CollOnDemand,); |
1874
|
1
|
|
|
|
|
4
|
delete $self->{$attr}; |
1875
|
|
|
|
|
|
|
} |
1876
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
=item destroy_set |
1878
|
|
|
|
|
|
|
|
1879
|
|
|
|
|
|
|
Calls Set::Object::clear to clear the set |
1880
|
|
|
|
|
|
|
|
1881
|
|
|
|
|
|
|
=cut |
1882
|
|
|
|
|
|
|
|
1883
|
|
|
|
|
|
|
sub destroy_set { |
1884
|
34
|
|
|
34
|
1
|
53
|
my $self = shift; |
1885
|
34
|
|
|
|
|
44
|
my $attr = shift; |
1886
|
|
|
|
|
|
|
|
1887
|
|
|
|
|
|
|
#return if (reftype $self ne "HASH"); |
1888
|
34
|
|
|
|
|
52
|
my $t = tied $self->{$attr}; |
1889
|
34
|
50
|
33
|
|
|
102
|
return if (defined $t and $t =~ m,Tangram::CollOnDemand,); |
1890
|
34
|
100
|
|
|
|
105
|
if (ref $self->{$attr} eq "Set::Object") { |
1891
|
33
|
|
|
|
|
114
|
$self->{$attr}->clear; |
1892
|
|
|
|
|
|
|
} |
1893
|
34
|
|
|
|
|
149
|
delete $self->{$attr}; |
1894
|
|
|
|
|
|
|
} |
1895
|
|
|
|
|
|
|
|
1896
|
|
|
|
|
|
|
=item destroy_hash |
1897
|
|
|
|
|
|
|
|
1898
|
|
|
|
|
|
|
empties a hash |
1899
|
|
|
|
|
|
|
|
1900
|
|
|
|
|
|
|
=cut |
1901
|
|
|
|
|
|
|
|
1902
|
|
|
|
|
|
|
sub destroy_hash { |
1903
|
2
|
|
|
2
|
1
|
4
|
my $self = shift; |
1904
|
2
|
|
|
|
|
4
|
my $attr = shift; |
1905
|
2
|
|
|
|
|
6
|
my $t = tied $self->{$attr}; |
1906
|
2
|
50
|
33
|
|
|
11
|
%{$self->{$attr}} = () |
|
2
|
|
|
|
|
8
|
|
1907
|
|
|
|
|
|
|
unless (defined $t and $t =~ m,Tangram::CollOnDemand,); |
1908
|
2
|
|
|
|
|
7
|
delete $self->{$attr}; |
1909
|
|
|
|
|
|
|
} |
1910
|
|
|
|
|
|
|
|
1911
|
|
|
|
|
|
|
=item destroy_ref |
1912
|
|
|
|
|
|
|
|
1913
|
|
|
|
|
|
|
destroys a reference. |
1914
|
|
|
|
|
|
|
|
1915
|
|
|
|
|
|
|
=cut |
1916
|
|
|
|
|
|
|
|
1917
|
|
|
|
|
|
|
sub destroy_ref { |
1918
|
12
|
|
|
12
|
1
|
17
|
my $self = shift; |
1919
|
12
|
|
|
|
|
40
|
delete $self->{(shift)}; |
1920
|
|
|
|
|
|
|
} |
1921
|
|
|
|
|
|
|
|
1922
|
|
|
|
|
|
|
=back |
1923
|
|
|
|
|
|
|
|
1924
|
|
|
|
|
|
|
=item parse_X ($attribute, { schema option }) |
1925
|
|
|
|
|
|
|
|
1926
|
|
|
|
|
|
|
Parses the schema option field, and returns one or two closures that |
1927
|
|
|
|
|
|
|
act as a check_X and a destroy_X function for the attribute. |
1928
|
|
|
|
|
|
|
|
1929
|
|
|
|
|
|
|
This is currently a very ugly hack, parsing the SQL type definition of |
1930
|
|
|
|
|
|
|
an object. But it was bloody handy in my case for hacking this in |
1931
|
|
|
|
|
|
|
quickly. This is probably unmanagably unportable across databases; |
1932
|
|
|
|
|
|
|
but send me bug reports on it anyway, and I'll try and make the |
1933
|
|
|
|
|
|
|
parsers work for as many databases as possible. |
1934
|
|
|
|
|
|
|
|
1935
|
|
|
|
|
|
|
This perhaps should be replaced by primitives that go the other way, |
1936
|
|
|
|
|
|
|
building the SQL type definition from a more abstract definition of |
1937
|
|
|
|
|
|
|
the type. |
1938
|
|
|
|
|
|
|
|
1939
|
|
|
|
|
|
|
Available functions: |
1940
|
|
|
|
|
|
|
|
1941
|
|
|
|
|
|
|
=over |
1942
|
|
|
|
|
|
|
|
1943
|
|
|
|
|
|
|
=item parse_string |
1944
|
|
|
|
|
|
|
|
1945
|
|
|
|
|
|
|
parses SQL types of: |
1946
|
|
|
|
|
|
|
|
1947
|
|
|
|
|
|
|
=over |
1948
|
|
|
|
|
|
|
|
1949
|
|
|
|
|
|
|
=cut |
1950
|
|
|
|
|
|
|
|
1951
|
7
|
|
|
7
|
|
52
|
use vars qw($quoted_part $sql_list); |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
36742
|
|
1952
|
|
|
|
|
|
|
|
1953
|
|
|
|
|
|
|
$quoted_part = qr/(?: \"([^\"]+)\" | \'([^\']+)\' )/x; |
1954
|
|
|
|
|
|
|
$sql_list = qr/\(\s* |
1955
|
|
|
|
|
|
|
( |
1956
|
|
|
|
|
|
|
$quoted_part |
1957
|
|
|
|
|
|
|
(?:\s*,\s* $quoted_part )* |
1958
|
|
|
|
|
|
|
) \s*\)/x; |
1959
|
|
|
|
|
|
|
|
1960
|
|
|
|
|
|
|
sub parse_string { |
1961
|
|
|
|
|
|
|
|
1962
|
27
|
|
|
27
|
1
|
45
|
my $attribute = shift; |
1963
|
27
|
|
|
|
|
39
|
my $option = shift; |
1964
|
|
|
|
|
|
|
|
1965
|
|
|
|
|
|
|
# simple case; return the check_string function. We don't |
1966
|
|
|
|
|
|
|
# need a destructor for a string so don't return one. |
1967
|
27
|
100
|
|
|
|
88
|
if (!$option->{sql}) { |
1968
|
23
|
|
|
|
|
86
|
return \&check_string; |
1969
|
|
|
|
|
|
|
} |
1970
|
|
|
|
|
|
|
|
1971
|
4
|
|
|
|
|
9
|
my $sql = $option->{sql}; |
1972
|
|
|
|
|
|
|
|
1973
|
|
|
|
|
|
|
# remove some common suffixes |
1974
|
4
|
|
|
|
|
15
|
$sql =~ s{\s+default\s+\S+}{}si; |
1975
|
4
|
|
|
|
|
53
|
$sql =~ s{(\s+not)?\s+null}{}si; |
1976
|
|
|
|
|
|
|
|
1977
|
|
|
|
|
|
|
=item CHAR(N), VARCHAR(N) |
1978
|
|
|
|
|
|
|
|
1979
|
|
|
|
|
|
|
closure checks length of string is less than N characters |
1980
|
|
|
|
|
|
|
|
1981
|
|
|
|
|
|
|
=cut |
1982
|
|
|
|
|
|
|
|
1983
|
4
|
50
|
|
|
|
250
|
if ($option->{sql} =~ m/^\s*(?:var)?char\s*\(\s*(\d+)\s*\)/ix) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1984
|
0
|
|
|
|
|
0
|
my $max_length = $1; |
1985
|
|
|
|
|
|
|
return sub { |
1986
|
0
|
|
|
|
|
0
|
croak "string too long for $attribute" |
1987
|
0
|
0
|
|
0
|
|
0
|
if (length ${$_[0]} > $max_length); |
1988
|
0
|
|
|
|
|
0
|
}; |
1989
|
|
|
|
|
|
|
|
1990
|
|
|
|
|
|
|
=item TINYBLOB, BLOB, LONGBLOB |
1991
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
checks max. length of string to be 255, 65535 or 16777215 chars |
1993
|
|
|
|
|
|
|
respectively. Also works with "TEXT" instead of "BLOB" |
1994
|
|
|
|
|
|
|
|
1995
|
|
|
|
|
|
|
=cut |
1996
|
|
|
|
|
|
|
|
1997
|
|
|
|
|
|
|
} elsif ($option->{sql} =~ m/^\s*(?:tiny|long|medium)? |
1998
|
|
|
|
|
|
|
(?:blob|text)/ix) { |
1999
|
0
|
0
|
|
|
|
0
|
my $max_length = ($1 ? ($1 eq "tiny"?255:2**24 - 1) |
|
|
0
|
|
|
|
|
|
2000
|
|
|
|
|
|
|
: 2**16 - 1); |
2001
|
|
|
|
|
|
|
return sub { |
2002
|
0
|
|
|
|
|
0
|
croak "string too long for $attribute" |
2003
|
0
|
0
|
0
|
0
|
|
0
|
if (${$_[0]} and length ${$_[0]} > $max_length); |
|
0
|
|
|
|
|
0
|
|
2004
|
0
|
|
|
|
|
0
|
}; |
2005
|
|
|
|
|
|
|
|
2006
|
|
|
|
|
|
|
=item SET("members", "of", "set") |
2007
|
|
|
|
|
|
|
|
2008
|
|
|
|
|
|
|
checks that the value passed is valid as a SQL set type, and that all |
2009
|
|
|
|
|
|
|
of the passed values are allowed to be a member of that set. |
2010
|
|
|
|
|
|
|
|
2011
|
|
|
|
|
|
|
=cut |
2012
|
|
|
|
|
|
|
|
2013
|
|
|
|
|
|
|
} elsif (my ($members) = $option->{sql} =~ |
2014
|
|
|
|
|
|
|
m/^\s*set\s*$sql_list/oi) { |
2015
|
|
|
|
|
|
|
|
2016
|
2
|
|
|
|
|
4
|
my %members; |
2017
|
2
|
|
66
|
|
|
133
|
$members{lc($1 || $2)} = 1 |
2018
|
|
|
|
|
|
|
while ( $members =~ m/\G[,\s]*$quoted_part/cog ); |
2019
|
|
|
|
|
|
|
|
2020
|
|
|
|
|
|
|
return sub { |
2021
|
4
|
|
|
4
|
|
5
|
for my $x (split /\s*,\s*/, ${$_[0]}) { |
|
4
|
|
|
|
|
25
|
|
2022
|
10
|
100
|
|
|
|
234
|
croak ("SQL set badly formed or invalid member $x " |
2023
|
|
|
|
|
|
|
." (SET" . join(",", keys %members). ")") |
2024
|
|
|
|
|
|
|
if (not exists $members{lc($x)}); |
2025
|
|
|
|
|
|
|
} |
2026
|
2
|
|
|
|
|
15
|
}; |
2027
|
|
|
|
|
|
|
|
2028
|
|
|
|
|
|
|
=item ENUM("possible", "values") |
2029
|
|
|
|
|
|
|
|
2030
|
|
|
|
|
|
|
checks that the value passed is one of the allowed values. |
2031
|
|
|
|
|
|
|
|
2032
|
|
|
|
|
|
|
=cut |
2033
|
|
|
|
|
|
|
|
2034
|
|
|
|
|
|
|
} elsif (my ($values) = $option->{sql} =~ |
2035
|
|
|
|
|
|
|
m/^\s*enum\s*$sql_list/oi ) { |
2036
|
|
|
|
|
|
|
|
2037
|
2
|
|
|
|
|
3
|
my %values; |
2038
|
2
|
|
66
|
|
|
156
|
$values{lc($1 || $2)} = 1 |
2039
|
|
|
|
|
|
|
while ( $values =~ m/\G[,\s]*$quoted_part/gc); |
2040
|
|
|
|
|
|
|
|
2041
|
|
|
|
|
|
|
return sub { |
2042
|
2
|
|
|
|
|
376
|
croak ("invalid enum value ${$_[0]} must be (" |
|
6
|
|
|
|
|
18
|
|
2043
|
|
|
|
|
|
|
. join(",", keys %values). ")") |
2044
|
6
|
100
|
|
6
|
|
9
|
if (not exists $values{lc(${$_[0]})}); |
2045
|
|
|
|
|
|
|
} |
2046
|
|
|
|
|
|
|
|
2047
|
|
|
|
|
|
|
|
2048
|
2
|
|
|
|
|
17
|
} else { |
2049
|
0
|
|
|
|
|
0
|
croak ("Please build support for your string SQL type in " |
2050
|
|
|
|
|
|
|
."Class::Tangram (".$option->{sql}.")"); |
2051
|
|
|
|
|
|
|
} |
2052
|
|
|
|
|
|
|
} |
2053
|
|
|
|
|
|
|
|
2054
|
|
|
|
|
|
|
=back |
2055
|
|
|
|
|
|
|
|
2056
|
|
|
|
|
|
|
=back |
2057
|
|
|
|
|
|
|
|
2058
|
|
|
|
|
|
|
=back |
2059
|
|
|
|
|
|
|
|
2060
|
|
|
|
|
|
|
=head2 Quick Object Dumping and Destruction |
2061
|
|
|
|
|
|
|
|
2062
|
|
|
|
|
|
|
=over |
2063
|
|
|
|
|
|
|
|
2064
|
|
|
|
|
|
|
=item $instance->quickdump |
2065
|
|
|
|
|
|
|
|
2066
|
|
|
|
|
|
|
Quickly show the blessed hash of an object, without descending into |
2067
|
|
|
|
|
|
|
it. Primarily useful when you have a large interconnected graph of |
2068
|
|
|
|
|
|
|
objects so don't want to use the B<x> command within the debugger. |
2069
|
|
|
|
|
|
|
It also doesn't have the side effect of auto-vivifying members. |
2070
|
|
|
|
|
|
|
|
2071
|
|
|
|
|
|
|
This function returns a string, suitable for print()ing. It does not |
2072
|
|
|
|
|
|
|
currently escape unprintable characters. |
2073
|
|
|
|
|
|
|
|
2074
|
|
|
|
|
|
|
=cut |
2075
|
|
|
|
|
|
|
|
2076
|
|
|
|
|
|
|
sub quickdump { |
2077
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
2078
|
|
|
|
|
|
|
|
2079
|
0
|
|
|
|
|
0
|
my $r = "REF ". (ref $self). "\n"; |
2080
|
0
|
|
|
|
|
0
|
for my $k (sort keys %$self) { |
2081
|
0
|
|
|
|
|
0
|
eval { |
2082
|
0
|
|
0
|
|
|
0
|
$r .= (" $k => " |
2083
|
|
|
|
|
|
|
. ( |
2084
|
|
|
|
|
|
|
tied $self->{$k} |
2085
|
|
|
|
|
|
|
|| ( ref $self->{$k} |
2086
|
|
|
|
|
|
|
? $self->{$k} |
2087
|
|
|
|
|
|
|
: ( defined ($self->{$k}) |
2088
|
|
|
|
|
|
|
? "'".$self->{$k}."'" |
2089
|
|
|
|
|
|
|
: "undef" ) |
2090
|
|
|
|
|
|
|
) |
2091
|
|
|
|
|
|
|
) |
2092
|
|
|
|
|
|
|
. "\n"); |
2093
|
|
|
|
|
|
|
}; |
2094
|
0
|
0
|
|
|
|
0
|
if ($@) { |
2095
|
0
|
|
|
|
|
0
|
$r .= " $k => Error('$@')\n"; |
2096
|
|
|
|
|
|
|
} |
2097
|
|
|
|
|
|
|
} |
2098
|
0
|
|
|
|
|
0
|
return $r; |
2099
|
|
|
|
|
|
|
} |
2100
|
|
|
|
|
|
|
|
2101
|
|
|
|
|
|
|
|
2102
|
|
|
|
|
|
|
=item $instance->DESTROY |
2103
|
|
|
|
|
|
|
|
2104
|
|
|
|
|
|
|
This function ensures that all of your attributes have their |
2105
|
|
|
|
|
|
|
destructors called. It calls the destroy_X function for attributes |
2106
|
|
|
|
|
|
|
that have it defined, if that attribute exists in the instance that we |
2107
|
|
|
|
|
|
|
are destroying. It calls the destroy_X functions as destroy_X($self, |
2108
|
|
|
|
|
|
|
$k) |
2109
|
|
|
|
|
|
|
|
2110
|
|
|
|
|
|
|
=cut |
2111
|
|
|
|
|
|
|
|
2112
|
|
|
|
|
|
|
sub DESTROY { |
2113
|
70
|
|
|
70
|
|
5332
|
my $self = shift; |
2114
|
|
|
|
|
|
|
|
2115
|
70
|
|
|
|
|
105
|
my $class = ref $self; |
2116
|
|
|
|
|
|
|
|
2117
|
|
|
|
|
|
|
# if no cleaners are known for this class, it hasn't been imported |
2118
|
|
|
|
|
|
|
# yet. Don't call import_schema, that would be a bad idea in a |
2119
|
|
|
|
|
|
|
# destructor. |
2120
|
70
|
50
|
|
|
|
178
|
exists $cleaners{$class} or return; |
2121
|
|
|
|
|
|
|
|
2122
|
|
|
|
|
|
|
# for every attribute that is defined, and has a cleaner function, |
2123
|
|
|
|
|
|
|
# call the cleaner function. |
2124
|
70
|
|
|
|
|
215
|
for my $k (keys %$self) { |
2125
|
191
|
100
|
66
|
|
|
755
|
if (defined $cleaners{$class}->{$k} and exists $self->{$k}) { |
2126
|
48
|
|
|
|
|
272
|
$cleaners{$class}->{$k}->($self, $k); |
2127
|
|
|
|
|
|
|
} |
2128
|
|
|
|
|
|
|
} |
2129
|
70
|
|
|
|
|
1168
|
$self->{_DESTROYED} = 1; |
2130
|
|
|
|
|
|
|
} |
2131
|
|
|
|
|
|
|
|
2132
|
|
|
|
|
|
|
=item $instance->clear_refs |
2133
|
|
|
|
|
|
|
|
2134
|
|
|
|
|
|
|
This clears all references from this object, ie exactly what DESTROY |
2135
|
|
|
|
|
|
|
normally does, but calling an object's destructor method directly is |
2136
|
|
|
|
|
|
|
bad form. Also, this function has no qualms with loading the class' |
2137
|
|
|
|
|
|
|
schema with import_schema() as needed. |
2138
|
|
|
|
|
|
|
|
2139
|
|
|
|
|
|
|
This is useful for breaking circular references, if you know you are |
2140
|
|
|
|
|
|
|
no longer going to be using an object then you can call this method, |
2141
|
|
|
|
|
|
|
which in many cases will end up cleaning up most of the objects you |
2142
|
|
|
|
|
|
|
want to get rid of. |
2143
|
|
|
|
|
|
|
|
2144
|
|
|
|
|
|
|
However, it still won't do anything about Tangram's internal reference |
2145
|
|
|
|
|
|
|
to the object, which must still be explicitly unlinked with the |
2146
|
|
|
|
|
|
|
Tangram::Storage->unload method. |
2147
|
|
|
|
|
|
|
|
2148
|
|
|
|
|
|
|
=cut |
2149
|
|
|
|
|
|
|
|
2150
|
|
|
|
|
|
|
sub clear_refs { |
2151
|
1
|
|
|
1
|
1
|
30
|
my $self = shift; |
2152
|
1
|
|
|
|
|
5
|
my $class = ref $self; |
2153
|
|
|
|
|
|
|
|
2154
|
1
|
50
|
|
|
|
6
|
exists $cleaners{$class} or import_schema($class); |
2155
|
|
|
|
|
|
|
|
2156
|
|
|
|
|
|
|
# break all ref's, sets, arrays |
2157
|
1
|
|
|
|
|
129
|
for my $k (keys %$self) { |
2158
|
1
|
50
|
33
|
|
|
14
|
if (defined $cleaners{$class}->{$k} and exists $self->{$k}) { |
2159
|
1
|
|
|
|
|
6
|
$cleaners{$class}->{$k}->($self, $k); |
2160
|
|
|
|
|
|
|
} |
2161
|
|
|
|
|
|
|
} |
2162
|
1
|
|
|
|
|
5
|
$self->{_NOREFS} = 1; |
2163
|
|
|
|
|
|
|
} |
2164
|
|
|
|
|
|
|
|
2165
|
|
|
|
|
|
|
=back |
2166
|
|
|
|
|
|
|
|
2167
|
|
|
|
|
|
|
=head1 FUNCTIONS |
2168
|
|
|
|
|
|
|
|
2169
|
|
|
|
|
|
|
The following functions are not intended to be called as object |
2170
|
|
|
|
|
|
|
methods. |
2171
|
|
|
|
|
|
|
|
2172
|
|
|
|
|
|
|
=head2 Schema Import |
2173
|
|
|
|
|
|
|
|
2174
|
|
|
|
|
|
|
our $fields = { int => [ qw(foo bar) ], |
2175
|
|
|
|
|
|
|
string => [ qw(baz quux) ] }; |
2176
|
|
|
|
|
|
|
|
2177
|
|
|
|
|
|
|
# Version 1.115 and below compatibility: |
2178
|
|
|
|
|
|
|
our $schema = { |
2179
|
|
|
|
|
|
|
fields => { int => [ qw(foo bar) ], |
2180
|
|
|
|
|
|
|
string => [ qw(baz quux) ] } |
2181
|
|
|
|
|
|
|
}; |
2182
|
|
|
|
|
|
|
|
2183
|
|
|
|
|
|
|
=over |
2184
|
|
|
|
|
|
|
|
2185
|
|
|
|
|
|
|
=item Class::Tangram::import_schema($class) |
2186
|
|
|
|
|
|
|
|
2187
|
|
|
|
|
|
|
Parses a tangram object field list, in C<${"${class}::fields"}> (or |
2188
|
|
|
|
|
|
|
C<${"${class}::schema"}-E<gt>{fields}> to the internal type information |
2189
|
|
|
|
|
|
|
hashes. It will also define all of the attribute accessor and update |
2190
|
|
|
|
|
|
|
methods in the C<$class> package. |
2191
|
|
|
|
|
|
|
|
2192
|
|
|
|
|
|
|
Note that calling this function twice for the same class is not |
2193
|
|
|
|
|
|
|
tested and may produce arbitrary results. Patches welcome. |
2194
|
|
|
|
|
|
|
|
2195
|
|
|
|
|
|
|
=cut |
2196
|
|
|
|
|
|
|
|
2197
|
|
|
|
|
|
|
# "parse" is special - it is passed the options hash given |
2198
|
|
|
|
|
|
|
# by the user and should return (\&check_func, |
2199
|
|
|
|
|
|
|
# \&destroy_func). This is how the magical string type |
2200
|
|
|
|
|
|
|
# checking is performed - see the entry for parse_string(), |
2201
|
|
|
|
|
|
|
# below. |
2202
|
|
|
|
|
|
|
|
2203
|
|
|
|
|
|
|
%defaults = ( |
2204
|
|
|
|
|
|
|
int => { check_func => \&check_int, |
2205
|
|
|
|
|
|
|
load => "Tangram/Scalar.pm", |
2206
|
|
|
|
|
|
|
}, |
2207
|
|
|
|
|
|
|
real => { check_func => \&check_real, |
2208
|
|
|
|
|
|
|
load => "Tangram/Scalar.pm", |
2209
|
|
|
|
|
|
|
}, |
2210
|
|
|
|
|
|
|
string => { parse => \&parse_string, |
2211
|
|
|
|
|
|
|
load => "Tangram/Scalar.pm", |
2212
|
|
|
|
|
|
|
}, |
2213
|
|
|
|
|
|
|
ref => { check_func => \&check_obj, |
2214
|
|
|
|
|
|
|
destroy_func => \&destroy_ref, |
2215
|
|
|
|
|
|
|
load => "Tangram/Ref.pm", |
2216
|
|
|
|
|
|
|
}, |
2217
|
|
|
|
|
|
|
array => { check_func => \&check_array, |
2218
|
|
|
|
|
|
|
destroy_func => \&destroy_array, |
2219
|
|
|
|
|
|
|
load => "Tangram/Array.pm", |
2220
|
|
|
|
|
|
|
}, |
2221
|
|
|
|
|
|
|
iarray => { check_func => \&check_array, |
2222
|
|
|
|
|
|
|
destroy_func => \&destroy_array, |
2223
|
|
|
|
|
|
|
load => "Tangram/IntrArray.pm", |
2224
|
|
|
|
|
|
|
}, |
2225
|
|
|
|
|
|
|
flat_array => { check_func => \&check_flat_array, |
2226
|
|
|
|
|
|
|
load => "Tangram/FlatArray.pm", |
2227
|
|
|
|
|
|
|
}, |
2228
|
|
|
|
|
|
|
set => { check_func => \&check_set, |
2229
|
|
|
|
|
|
|
destroy_func => \&destroy_set, |
2230
|
|
|
|
|
|
|
init_default => sub { Set::Object->new() }, |
2231
|
|
|
|
|
|
|
load => "Tangram/Set.pm", |
2232
|
|
|
|
|
|
|
}, |
2233
|
|
|
|
|
|
|
iset => { check_func => \&check_set, |
2234
|
|
|
|
|
|
|
destroy_func => \&destroy_set, |
2235
|
|
|
|
|
|
|
init_default => sub { Set::Object->new() }, |
2236
|
|
|
|
|
|
|
load => "Tangram/IntrSet.pm", |
2237
|
|
|
|
|
|
|
}, |
2238
|
|
|
|
|
|
|
dmdatetime => { check_func => \&check_dmdatetime, |
2239
|
|
|
|
|
|
|
load => "Tangram/DMDateTime.pm", |
2240
|
|
|
|
|
|
|
}, |
2241
|
|
|
|
|
|
|
rawdatetime => { check_func => \&check_rawdatetime, |
2242
|
|
|
|
|
|
|
load => "Tangram/RawDateTime.pm", |
2243
|
|
|
|
|
|
|
}, |
2244
|
|
|
|
|
|
|
rawdate => { check_func => \&check_rawdate, |
2245
|
|
|
|
|
|
|
load => "Tangram/RawDate.pm", |
2246
|
|
|
|
|
|
|
}, |
2247
|
|
|
|
|
|
|
rawtime => { check_func => \&check_rawtime, |
2248
|
|
|
|
|
|
|
load => "Tangram/RawTime.pm", |
2249
|
|
|
|
|
|
|
}, |
2250
|
|
|
|
|
|
|
flat_hash => { check_func => \&check_flat_hash, |
2251
|
|
|
|
|
|
|
load => "Tangram/FlatHash.pm", |
2252
|
|
|
|
|
|
|
}, |
2253
|
|
|
|
|
|
|
transient => { check_func => \&check_nothing, |
2254
|
|
|
|
|
|
|
}, |
2255
|
|
|
|
|
|
|
hash => { check_func => \&check_hash, |
2256
|
|
|
|
|
|
|
destroy_func => \&destroy_hash, |
2257
|
|
|
|
|
|
|
load => "Tangram/Hash.pm", |
2258
|
|
|
|
|
|
|
}, |
2259
|
|
|
|
|
|
|
ihash => { check_func => \&check_hash, |
2260
|
|
|
|
|
|
|
destroy_func => \&destroy_hash, |
2261
|
|
|
|
|
|
|
load => "Tangram/IntrHash.pm", |
2262
|
|
|
|
|
|
|
}, |
2263
|
|
|
|
|
|
|
perl_dump => { check_func => \&check_nothing, |
2264
|
|
|
|
|
|
|
load => "Tangram/PerlDump.pm", |
2265
|
|
|
|
|
|
|
}, |
2266
|
|
|
|
|
|
|
yaml => { check_func => \&check_nothing, |
2267
|
|
|
|
|
|
|
load => "Tangram/YAML.pm", |
2268
|
|
|
|
|
|
|
}, |
2269
|
|
|
|
|
|
|
backref => { check_func => \&check_nothing, |
2270
|
|
|
|
|
|
|
}, |
2271
|
|
|
|
|
|
|
storable => { check_func => \&check_nothing, |
2272
|
|
|
|
|
|
|
load => "Tangram/Storable.pm", |
2273
|
|
|
|
|
|
|
}, |
2274
|
|
|
|
|
|
|
idbif => { check_func => \&check_nothing, |
2275
|
|
|
|
|
|
|
load => "Tangram/IDBIF.pm", |
2276
|
|
|
|
|
|
|
}, |
2277
|
|
|
|
|
|
|
); |
2278
|
|
|
|
|
|
|
|
2279
|
|
|
|
|
|
|
sub import_schema { # Damn this function is long |
2280
|
44
|
|
|
44
|
1
|
4710
|
my $class = shift; |
2281
|
|
|
|
|
|
|
|
2282
|
44
|
100
|
|
|
|
164
|
return if exists $abstract{$class}; |
2283
|
|
|
|
|
|
|
|
2284
|
33
|
|
|
|
|
58
|
eval { |
2285
|
33
|
|
|
|
|
50
|
my ($fields, $bases, $abstract); |
2286
|
|
|
|
|
|
|
{ |
2287
|
|
|
|
|
|
|
|
2288
|
|
|
|
|
|
|
# Here, we go hunting around for their defined schema and |
2289
|
|
|
|
|
|
|
# options |
2290
|
33
|
|
|
|
|
52
|
local $^W=0; |
|
33
|
|
|
|
|
139
|
|
2291
|
33
|
|
|
|
|
50
|
eval { |
2292
|
|
|
|
|
|
|
$fields = (${"${class}::fields"} || |
2293
|
33
|
|
100
|
|
|
43
|
${"${class}::schema"}->{fields}); |
2294
|
|
|
|
|
|
|
$abstract = (${"${class}::abstract"} || |
2295
|
33
|
|
66
|
|
|
329
|
${"${class}::schema"}->{abstract}); |
2296
|
33
|
|
|
|
|
109
|
$bases = ${"${class}::schema"}->{bases}; |
|
33
|
|
|
|
|
240
|
|
2297
|
|
|
|
|
|
|
}; |
2298
|
33
|
50
|
|
|
|
51
|
if ( my @stack = (grep !/${class}::CT/, |
|
33
|
|
|
|
|
926
|
|
2299
|
|
|
|
|
|
|
@{"${class}::ISA"} )) { |
2300
|
|
|
|
|
|
|
# clean "bases" information from @ISA |
2301
|
33
|
|
|
|
|
63
|
my %seen = map { $_ => 1 } $class, __PACKAGE__; |
|
66
|
|
|
|
|
218
|
|
2302
|
33
|
|
|
|
|
72
|
$bases = []; |
2303
|
33
|
|
|
|
|
311
|
while ( my $super = pop @stack ) { |
2304
|
43
|
100
|
66
|
|
|
60
|
if ( defined ${"${super}::schema"} |
|
43
|
|
|
|
|
281
|
|
|
26
|
|
|
|
|
173
|
|
2305
|
|
|
|
|
|
|
or defined ${"${super}::fields"} ) { |
2306
|
17
|
|
|
|
|
68
|
push @$bases, $super; |
2307
|
|
|
|
|
|
|
} else { |
2308
|
2
|
|
|
|
|
14
|
push @stack, grep { !$seen{$_}++ } |
|
26
|
|
|
|
|
152
|
|
2309
|
26
|
|
|
|
|
44
|
@{"${super}::ISA"}; |
2310
|
|
|
|
|
|
|
} |
2311
|
|
|
|
|
|
|
} |
2312
|
33
|
50
|
66
|
|
|
166
|
if ( !$fields and !@$bases ) { |
2313
|
0
|
|
|
|
|
0
|
croak ("No schema and no Class::Tangram " |
2314
|
|
|
|
|
|
|
."superclass for $class; define " |
2315
|
|
|
|
|
|
|
."${class}::fields!"); |
2316
|
|
|
|
|
|
|
} |
2317
|
|
|
|
|
|
|
} |
2318
|
|
|
|
|
|
|
} |
2319
|
|
|
|
|
|
|
|
2320
|
|
|
|
|
|
|
# play around with the @ISA to insert an intermediate package |
2321
|
33
|
|
|
|
|
212
|
my $target_pkg = $class."::CT"; |
2322
|
33
|
|
|
|
|
41
|
my $target_stash = \%{$target_pkg."::"}; |
|
33
|
|
|
|
|
227
|
|
2323
|
33
|
|
|
|
|
149
|
(@{$target_pkg."::ISA"}, @{$class."::ISA"}) |
|
33
|
|
|
|
|
1105
|
|
|
33
|
|
|
|
|
97
|
|
2324
|
33
|
|
|
|
|
48
|
= @{$class."::ISA"}; |
2325
|
33
|
|
|
|
|
254
|
@{$class."::ISA"} = $target_pkg; |
|
33
|
|
|
|
|
889
|
|
2326
|
|
|
|
|
|
|
|
2327
|
|
|
|
|
|
|
# if this is an abstract type, do not allow it to be |
2328
|
|
|
|
|
|
|
# instantiated |
2329
|
33
|
100
|
|
|
|
162
|
$abstract{$class} = $abstract ? 1 : 0; |
2330
|
|
|
|
|
|
|
|
2331
|
|
|
|
|
|
|
# If there are any base classes, import them first so that the |
2332
|
|
|
|
|
|
|
# check, cleaners and init_defaults can be inherited |
2333
|
33
|
50
|
|
|
|
112
|
if (defined $bases) { |
2334
|
33
|
50
|
|
|
|
107
|
(ref $bases eq "ARRAY") |
2335
|
|
|
|
|
|
|
or croak "bases not an array ref for $class"; |
2336
|
|
|
|
|
|
|
|
2337
|
|
|
|
|
|
|
# Note that the order of your bases is significant, that |
2338
|
|
|
|
|
|
|
# is if you are using multiple iheritance then the later |
2339
|
|
|
|
|
|
|
# classes override the earlier ones. |
2340
|
33
|
|
|
|
|
243
|
for my $super ( @$bases ) { |
2341
|
17
|
100
|
|
|
|
85
|
import_schema($super) unless (exists $check{$super}); |
2342
|
|
|
|
|
|
|
|
2343
|
|
|
|
|
|
|
# copy each of the per-class configuration hashes to |
2344
|
|
|
|
|
|
|
# this class as defaults. |
2345
|
17
|
|
|
|
|
136
|
my ($k, $v); |
2346
|
|
|
|
|
|
|
|
2347
|
|
|
|
|
|
|
# FIXME - this repetition of code is getting silly :) |
2348
|
104
|
|
|
|
|
484
|
$types{$class}->{$k} = $v |
2349
|
17
|
|
|
|
|
25
|
while (($k, $v) = each %{ $types{$super} } ); |
2350
|
104
|
|
|
|
|
663
|
$check{$class}->{$k} = $v |
2351
|
17
|
|
|
|
|
323
|
while (($k, $v) = each %{ $check{$super} } ); |
2352
|
44
|
|
|
|
|
204
|
$cleaners{$class}->{$k} = $v |
2353
|
17
|
|
|
|
|
31
|
while (($k, $v) = each %{ $cleaners{$super} } ); |
2354
|
104
|
|
|
|
|
445
|
$attribute_options{$class}->{$k} = $v |
2355
|
17
|
|
|
|
|
28
|
while (($k, $v) = each %{ $attribute_options{$super} } ); |
2356
|
28
|
|
|
|
|
115
|
$init_defaults{$class}->{$k} = $v |
2357
|
17
|
|
|
|
|
30
|
while (($k, $v) = each %{ $init_defaults{$super} } ); |
2358
|
20
|
|
|
|
|
85
|
$required_attributes{$class}->{$k} = $v |
2359
|
17
|
|
|
|
|
27
|
while (($k, $v) = each %{ $required_attributes{$super} } ); |
2360
|
37
|
|
|
|
|
204
|
$companions{$class}->{$k} = $v |
2361
|
17
|
|
|
|
|
25
|
while (($k, $v) = each %{ $companions{$super} } ); |
2362
|
|
|
|
|
|
|
} |
2363
|
|
|
|
|
|
|
} |
2364
|
|
|
|
|
|
|
|
2365
|
|
|
|
|
|
|
# iterate over each of the *types* of fields (string, int, ref, etc.) |
2366
|
33
|
|
|
|
|
179
|
while (my ($type, $v) = each %$fields) { |
2367
|
89
|
100
|
|
|
|
518
|
if (ref $v eq "ARRAY") { |
2368
|
43
|
|
|
|
|
76
|
$v = { map { $_, undef } @$v }; |
|
48
|
|
|
|
|
324
|
|
2369
|
|
|
|
|
|
|
} |
2370
|
|
|
|
|
|
|
|
2371
|
|
|
|
|
|
|
# iterate each of the *attributes* of a particular type |
2372
|
89
|
|
|
|
|
337
|
while (my ($attribute, $options) = each %$v) { |
2373
|
|
|
|
|
|
|
|
2374
|
114
|
|
|
|
|
265
|
my $accessors = _mk_accessor($attribute, $options, $class, |
2375
|
|
|
|
|
|
|
$target_pkg, $type); |
2376
|
|
|
|
|
|
|
# now export all these accessors into caller's namespace |
2377
|
114
|
|
|
|
|
492
|
while (my ($accessor, $coderef) = each %$accessors) { |
2378
|
954
|
|
|
|
|
1303
|
my $accessor_name = $accessor; |
2379
|
|
|
|
|
|
|
# comes in like $class::$meth, so extract our meth |
2380
|
954
|
|
|
|
|
5575
|
$accessor_name =~ s/(.*\:\:)+(\w+)$/$2/; |
2381
|
954
|
50
|
|
|
|
10106
|
*{$accessor} = $coderef |
|
954
|
|
|
|
|
7103
|
|
2382
|
|
|
|
|
|
|
unless $target_pkg->can($accessor_name); |
2383
|
|
|
|
|
|
|
} |
2384
|
|
|
|
|
|
|
} |
2385
|
|
|
|
|
|
|
} |
2386
|
|
|
|
|
|
|
}; |
2387
|
33
|
|
100
|
|
|
229
|
$cleaners{$class} ||= {}; |
2388
|
|
|
|
|
|
|
|
2389
|
33
|
50
|
|
|
|
232
|
$@ && die "$@ while trying to import schema for $class"; |
2390
|
|
|
|
|
|
|
} |
2391
|
|
|
|
|
|
|
|
2392
|
|
|
|
|
|
|
sub _mk_accessor { |
2393
|
|
|
|
|
|
|
|
2394
|
114
|
|
|
114
|
|
325
|
my ($attribute, $options, $class, $target_pkg, $type, $dontcarp) = @_; |
2395
|
|
|
|
|
|
|
|
2396
|
114
|
|
|
|
|
230
|
my $def = $defaults{$type}; |
2397
|
|
|
|
|
|
|
|
2398
|
|
|
|
|
|
|
# hash of various accessor code refs to return |
2399
|
114
|
|
|
|
|
134
|
my %accessors; |
2400
|
|
|
|
|
|
|
|
2401
|
|
|
|
|
|
|
# this is what we are finding out about each attribute |
2402
|
|
|
|
|
|
|
# $type is already set |
2403
|
114
|
|
|
|
|
127
|
my ($check_func, $default, $required, $cleaner, |
2404
|
|
|
|
|
|
|
$companion, $base_type, $load); |
2405
|
|
|
|
|
|
|
# set defaults from what they give |
2406
|
114
|
|
100
|
|
|
579
|
$options ||= {}; |
2407
|
114
|
100
|
66
|
|
|
437
|
if (ref $options eq "HASH" or |
2408
|
|
|
|
|
|
|
UNIVERSAL::isa($options, 'Tangram::Type')) { |
2409
|
111
|
|
|
|
|
449
|
($check_func, $default, $required, $cleaner, |
2410
|
|
|
|
|
|
|
$companion, $base_type, $load) |
2411
|
111
|
|
|
|
|
181
|
= @{$options}{qw(check_func init_default |
2412
|
|
|
|
|
|
|
required destroy_func |
2413
|
|
|
|
|
|
|
companion class load)}; |
2414
|
|
|
|
|
|
|
} |
2415
|
|
|
|
|
|
|
|
2416
|
|
|
|
|
|
|
# Fill their settings with info from defaults |
2417
|
114
|
50
|
|
|
|
571
|
if (ref $def eq "HASH") { |
2418
|
|
|
|
|
|
|
|
2419
|
|
|
|
|
|
|
# try to magically parse their options |
2420
|
114
|
100
|
66
|
|
|
454
|
if ( $def->{parse} and !($check_func and $cleaner) ) { |
|
|
|
66
|
|
|
|
|
2421
|
27
|
|
|
|
|
90
|
my @a = $def->{parse}->($attribute, $options); |
2422
|
27
|
|
66
|
|
|
132
|
$check_func ||= $a[0]; |
2423
|
27
|
|
33
|
|
|
121
|
$cleaner ||= $a[1]; |
2424
|
|
|
|
|
|
|
} |
2425
|
|
|
|
|
|
|
|
2426
|
|
|
|
|
|
|
# fall back to defaults for this class |
2427
|
114
|
|
66
|
|
|
465
|
$load ||= $def->{load}; |
2428
|
114
|
|
66
|
|
|
398
|
$check_func ||= $def->{check_func}; |
2429
|
114
|
|
66
|
|
|
401
|
$cleaner ||= $def->{destroy_func}; |
2430
|
114
|
100
|
|
|
|
327
|
$default = $def->{init_default} unless defined $default; |
2431
|
|
|
|
|
|
|
} |
2432
|
|
|
|
|
|
|
|
2433
|
|
|
|
|
|
|
# load a Tangram::Type module, if specified |
2434
|
114
|
100
|
66
|
|
|
288
|
unless ($no_tangram or not defined $load) { |
2435
|
7
|
50
|
|
|
|
50
|
if (!exists $INC{$load}) { |
2436
|
7
|
|
|
|
|
3216
|
eval 'require $load'; |
2437
|
7
|
50
|
|
|
|
58
|
$no_tangram = 1 if $@; |
2438
|
|
|
|
|
|
|
} |
2439
|
|
|
|
|
|
|
} |
2440
|
|
|
|
|
|
|
|
2441
|
|
|
|
|
|
|
# everything must be checked! |
2442
|
114
|
50
|
|
|
|
357
|
croak("No check function for ${class}\->$attribute " |
2443
|
|
|
|
|
|
|
."(type $type); set \$Class::Tangram::defaults" |
2444
|
|
|
|
|
|
|
."{backref} to a sub (eg, \&Class::Tangram::" |
2445
|
|
|
|
|
|
|
."check_nothing)") |
2446
|
|
|
|
|
|
|
unless (ref $check_func eq "CODE"); |
2447
|
|
|
|
|
|
|
|
2448
|
114
|
50
|
66
|
|
|
767
|
carp("re-defining attribute `$attribute' in subclass " |
|
|
|
33
|
|
|
|
|
2449
|
|
|
|
|
|
|
."`$class'") if $^W and |
2450
|
|
|
|
|
|
|
exists $types{$class}->{$attribute} and not $dontcarp; |
2451
|
|
|
|
|
|
|
|
2452
|
114
|
|
|
|
|
252
|
$types{$class}->{$attribute} = $type; |
2453
|
114
|
|
|
|
|
230
|
$check{$class}->{$attribute} = $check_func; |
2454
|
|
|
|
|
|
|
{ |
2455
|
114
|
|
|
|
|
127
|
local ($^W) = 0; |
|
114
|
|
|
|
|
326
|
|
2456
|
|
|
|
|
|
|
|
2457
|
|
|
|
|
|
|
# build an appropriate "get_attribute" method, and |
2458
|
|
|
|
|
|
|
# define other per-type methods |
2459
|
114
|
|
|
|
|
171
|
my ($get_closure, $set_closure, $is_assoc, |
2460
|
|
|
|
|
|
|
$method_type); |
2461
|
|
|
|
|
|
|
|
2462
|
|
|
|
|
|
|
# implement with closures for speed |
2463
|
114
|
100
|
|
|
|
543
|
if ( $type =~ m/^i?(set|array|hash|ref)$/ ) { |
2464
|
51
|
|
|
|
|
110
|
$method_type = $1; |
2465
|
51
|
|
|
|
|
60
|
$is_assoc = 1; |
2466
|
51
|
|
|
|
|
117
|
$get_closure = "_get_X_$method_type"; |
2467
|
51
|
|
|
|
|
87
|
$set_closure = "_set_X_$method_type"; |
2468
|
|
|
|
|
|
|
} else { |
2469
|
|
|
|
|
|
|
# GET_$attribute (scalar) |
2470
|
|
|
|
|
|
|
# return value only |
2471
|
63
|
|
|
41
|
|
272
|
$get_closure = sub { $_[0]->{$attribute}; }; |
|
41
|
|
|
|
|
2100
|
|
2472
|
|
|
|
|
|
|
} |
2473
|
|
|
|
|
|
|
|
2474
|
|
|
|
|
|
|
# SET_$attribute (all) |
2475
|
114
|
|
|
|
|
261
|
my $checkit = \$check{$class}->{$attribute}; |
2476
|
|
|
|
|
|
|
|
2477
|
114
|
100
|
66
|
|
|
405
|
unless ($is_assoc or $set_closure) { |
2478
|
|
|
|
|
|
|
# `required' hack for strings - duplicate the code |
2479
|
|
|
|
|
|
|
# to avoid the following string comparison for |
2480
|
|
|
|
|
|
|
# every set |
2481
|
63
|
100
|
|
|
|
544
|
if ( $type eq "string" ) { |
2482
|
|
|
|
|
|
|
$set_closure = sub { |
2483
|
132
|
|
|
132
|
|
1677
|
my $self = shift; |
2484
|
132
|
|
|
|
|
161
|
my $value = shift; |
2485
|
132
|
|
|
|
|
251
|
my $err = ''; |
2486
|
132
|
100
|
66
|
|
|
614
|
if ( defined $value and length $value ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
2487
|
122
|
|
|
|
|
209
|
$ {$checkit}->(\$value); |
|
122
|
|
|
|
|
277
|
|
2488
|
|
|
|
|
|
|
} elsif ( $required ) { |
2489
|
4
|
|
|
|
|
5
|
$err = "value is required"; |
2490
|
|
|
|
|
|
|
} elsif ( defined $required ) { |
2491
|
6
|
50
|
|
|
|
15
|
$err = "value must be defined" |
2492
|
|
|
|
|
|
|
unless defined $value; |
2493
|
|
|
|
|
|
|
} |
2494
|
124
|
100
|
50
|
|
|
1286
|
$err && croak |
2495
|
|
|
|
|
|
|
("value failed type check - ${class}->" |
2496
|
|
|
|
|
|
|
."set_$attribute('".($value || '')."') ($err)"); |
2497
|
120
|
|
|
|
|
6657
|
$self->{$attribute} = $value; |
2498
|
|
|
|
|
|
|
} |
2499
|
27
|
|
|
|
|
186
|
} else { |
2500
|
|
|
|
|
|
|
$set_closure = sub { |
2501
|
49
|
|
|
49
|
|
113103
|
my $self = shift; |
2502
|
49
|
|
|
|
|
81
|
my $value = shift; |
2503
|
49
|
|
|
|
|
67
|
my $err = ''; |
2504
|
49
|
50
|
|
|
|
108
|
if ( defined $value ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2505
|
49
|
|
|
|
|
74
|
$ {$checkit}->(\$value); |
|
49
|
|
|
|
|
138
|
|
2506
|
|
|
|
|
|
|
} elsif ( $required ) { |
2507
|
0
|
|
|
|
|
0
|
$err = "value is required"; |
2508
|
|
|
|
|
|
|
} elsif ( defined $required ) { |
2509
|
0
|
0
|
|
|
|
0
|
$err = "value must be defined" |
2510
|
|
|
|
|
|
|
unless defined $value; |
2511
|
|
|
|
|
|
|
} |
2512
|
40
|
50
|
0
|
|
|
230
|
$err && croak |
2513
|
|
|
|
|
|
|
("value failed type check - ${class}->" |
2514
|
|
|
|
|
|
|
."set_$attribute('".($value || '')."') ($err)"); |
2515
|
40
|
|
|
|
|
194
|
$self->{$attribute} = $value; |
2516
|
|
|
|
|
|
|
} |
2517
|
36
|
|
|
|
|
184
|
} |
2518
|
|
|
|
|
|
|
} |
2519
|
|
|
|
|
|
|
|
2520
|
|
|
|
|
|
|
# flat hashes & arrays |
2521
|
114
|
100
|
|
|
|
280
|
if ( $type =~ m/^flat_(array|hash)$/ ) { |
2522
|
6
|
100
|
|
|
|
34
|
if ($1 eq "hash") { |
2523
|
|
|
|
|
|
|
$get_closure = sub { |
2524
|
5
|
|
|
5
|
|
7
|
my $self = shift; |
2525
|
5
|
|
100
|
|
|
25
|
my $a = ($self->{$attribute} ||= {}); |
2526
|
5
|
100
|
|
|
|
19
|
return (wantarray ? values %{ $a } |
|
1
|
|
|
|
|
8
|
|
2527
|
|
|
|
|
|
|
: $a); |
2528
|
|
|
|
|
|
|
} |
2529
|
3
|
|
|
|
|
14
|
} else { |
2530
|
|
|
|
|
|
|
$get_closure = sub { |
2531
|
5
|
|
|
5
|
|
8
|
my $self = shift; |
2532
|
5
|
|
100
|
|
|
27
|
my $a = ($self->{$attribute} ||= []); |
2533
|
5
|
100
|
|
|
|
20
|
return (wantarray ? @{ $a } : $a); |
|
1
|
|
|
|
|
6
|
|
2534
|
|
|
|
|
|
|
} |
2535
|
3
|
|
|
|
|
28
|
} |
2536
|
|
|
|
|
|
|
} |
2537
|
|
|
|
|
|
|
|
2538
|
|
|
|
|
|
|
# now collect the closures |
2539
|
114
|
|
|
|
|
398
|
my ($getter, $setter) |
2540
|
|
|
|
|
|
|
= ("get_$attribute", "set_$attribute"); |
2541
|
|
|
|
|
|
|
|
2542
|
|
|
|
|
|
|
$accessors{$target_pkg."::".$getter} = |
2543
|
|
|
|
|
|
|
(ref $get_closure ? $get_closure |
2544
|
|
|
|
|
|
|
: sub { |
2545
|
151
|
|
|
151
|
|
318
|
my $self = shift; |
2546
|
151
|
|
|
|
|
710
|
return $self->$get_closure($attribute, @_); |
2547
|
114
|
100
|
|
|
|
664
|
}); |
2548
|
|
|
|
|
|
|
$accessors{$target_pkg."::".$setter} = |
2549
|
|
|
|
|
|
|
(ref $set_closure ? $set_closure |
2550
|
|
|
|
|
|
|
: sub { |
2551
|
124
|
|
|
124
|
|
1367
|
my $self = shift; |
2552
|
124
|
|
|
|
|
595
|
return $self->$set_closure |
2553
|
|
|
|
|
|
|
($base_type, $companion, $attribute, @_); |
2554
|
114
|
100
|
|
|
|
545
|
}); |
2555
|
|
|
|
|
|
|
|
2556
|
114
|
100
|
|
|
|
252
|
if ($is_assoc) { |
2557
|
|
|
|
|
|
|
|
2558
|
51
|
|
|
|
|
107
|
foreach my $func (qw(includes insert replace |
2559
|
|
|
|
|
|
|
pairs size clear remove |
2560
|
|
|
|
|
|
|
push pop shift unshift |
2561
|
|
|
|
|
|
|
splice)) { |
2562
|
612
|
|
|
|
|
1207
|
my $method = $target_pkg."::".$attribute."_".$func; |
2563
|
612
|
|
|
|
|
1741
|
my $real_method = |
2564
|
|
|
|
|
|
|
"_${func}_X_$method_type"; |
2565
|
|
|
|
|
|
|
$accessors{$method} = |
2566
|
|
|
|
|
|
|
sub { |
2567
|
123
|
|
|
123
|
|
2221
|
my $self = shift; |
2568
|
123
|
|
|
|
|
542
|
return $self->$real_method($attribute, @_); |
2569
|
|
|
|
|
|
|
} |
2570
|
612
|
|
|
|
|
3828
|
} |
2571
|
|
|
|
|
|
|
|
2572
|
|
|
|
|
|
|
# XXX - use `Want' to return lvalue subs here |
2573
|
|
|
|
|
|
|
$accessors{$target_pkg."::$attribute"} = sub { |
2574
|
24
|
|
|
24
|
|
4211
|
my $self = shift; |
2575
|
24
|
50
|
66
|
|
|
228
|
if ( @_ && looks_like_KVKV(@_) ) { |
|
|
50
|
66
|
|
|
|
|
2576
|
0
|
0
|
|
|
|
0
|
carp("The OO Police say change your call " |
2577
|
|
|
|
|
|
|
."to ->set_$attribute") if ($^W); |
2578
|
0
|
|
|
|
|
0
|
return $self->$setter(@_); |
2579
|
|
|
|
|
|
|
} elsif ( !@_ || looks_like_KK(@_) ) { |
2580
|
24
|
|
|
|
|
98
|
return $self->$getter(@_); |
2581
|
|
|
|
|
|
|
} else { |
2582
|
0
|
|
|
|
|
0
|
croak("Ambiguous argument list " |
2583
|
|
|
|
|
|
|
."passed to ${class}::" |
2584
|
|
|
|
|
|
|
."${attribute}"); |
2585
|
|
|
|
|
|
|
} |
2586
|
|
|
|
|
|
|
} |
2587
|
|
|
|
|
|
|
|
2588
|
51
|
|
|
|
|
414
|
} else { |
2589
|
|
|
|
|
|
|
|
2590
|
|
|
|
|
|
|
# XXX - use `Want' to return lvalue subs here |
2591
|
|
|
|
|
|
|
$accessors{$target_pkg."::$attribute"} = sub { |
2592
|
33
|
|
|
33
|
|
6949
|
my $self = shift; |
2593
|
33
|
50
|
|
|
|
95
|
if ( @_ ) { |
2594
|
0
|
0
|
|
|
|
0
|
carp("The OO Police say change your call " |
2595
|
|
|
|
|
|
|
."to ->set_$attribute") if ($^W); |
2596
|
0
|
|
|
|
|
0
|
return $self->$setter(@_); |
2597
|
|
|
|
|
|
|
} else { |
2598
|
33
|
|
|
|
|
467
|
return $self->$getter(@_); |
2599
|
|
|
|
|
|
|
} |
2600
|
|
|
|
|
|
|
} |
2601
|
|
|
|
|
|
|
|
2602
|
63
|
|
|
|
|
427
|
} |
2603
|
|
|
|
|
|
|
|
2604
|
114
|
100
|
|
|
|
510
|
$cleaners{$class}->{$attribute} = $cleaner |
2605
|
|
|
|
|
|
|
if (defined $cleaner); |
2606
|
114
|
100
|
|
|
|
552
|
$init_defaults{$class}->{$attribute} = $default |
2607
|
|
|
|
|
|
|
if (defined $default); |
2608
|
114
|
100
|
|
|
|
219
|
$required_attributes{$class}->{$attribute} = $required |
2609
|
|
|
|
|
|
|
if (defined $required); |
2610
|
114
|
|
50
|
|
|
577
|
$attribute_options{$class}->{$attribute} = |
2611
|
|
|
|
|
|
|
( $options || {} ); |
2612
|
114
|
100
|
|
|
|
436
|
$companions{$class}->{$attribute} = $companion |
2613
|
|
|
|
|
|
|
if (defined $companion); |
2614
|
|
|
|
|
|
|
|
2615
|
|
|
|
|
|
|
} |
2616
|
114
|
|
|
|
|
1149
|
return \%accessors; |
2617
|
|
|
|
|
|
|
} |
2618
|
|
|
|
|
|
|
|
2619
|
|
|
|
|
|
|
=back |
2620
|
|
|
|
|
|
|
|
2621
|
|
|
|
|
|
|
=head2 Run-time type information |
2622
|
|
|
|
|
|
|
|
2623
|
|
|
|
|
|
|
It is possible to access the data structures that Class::Tangram uses |
2624
|
|
|
|
|
|
|
internally to verify attributes, create objects and so on. |
2625
|
|
|
|
|
|
|
|
2626
|
|
|
|
|
|
|
This should be considered a B<HIGHLY EXPERIMENTAL> interface to |
2627
|
|
|
|
|
|
|
B<INTERNALS> of Class::Tangram. |
2628
|
|
|
|
|
|
|
|
2629
|
|
|
|
|
|
|
Class::Tangram keeps seven internal hashes: |
2630
|
|
|
|
|
|
|
|
2631
|
|
|
|
|
|
|
=over |
2632
|
|
|
|
|
|
|
|
2633
|
|
|
|
|
|
|
=item C<%types> |
2634
|
|
|
|
|
|
|
|
2635
|
|
|
|
|
|
|
C<$types{$class}-E<gt>{$attribute}> is the tangram type of each attribute, |
2636
|
|
|
|
|
|
|
ie "ref", "iset", etc. See L<Tangram::Type>. |
2637
|
|
|
|
|
|
|
|
2638
|
|
|
|
|
|
|
=item C<%attribute_options> |
2639
|
|
|
|
|
|
|
|
2640
|
|
|
|
|
|
|
C<$attribute_options{$class}-E<gt>{$attribute}> is the options hash |
2641
|
|
|
|
|
|
|
for a given attribute. |
2642
|
|
|
|
|
|
|
|
2643
|
|
|
|
|
|
|
=item C<%required_attributes> |
2644
|
|
|
|
|
|
|
|
2645
|
|
|
|
|
|
|
C<$required_attributes{$class}-E<gt>{$attribute}> is the 'required' |
2646
|
|
|
|
|
|
|
option setting for a given attribute. |
2647
|
|
|
|
|
|
|
|
2648
|
|
|
|
|
|
|
=item C<%check> |
2649
|
|
|
|
|
|
|
|
2650
|
|
|
|
|
|
|
C<$check{$class}-E<gt>{$attribute}> is a function that will be passed |
2651
|
|
|
|
|
|
|
a reference to the value to be checked and either throw an exception |
2652
|
|
|
|
|
|
|
(die) or return true. |
2653
|
|
|
|
|
|
|
|
2654
|
|
|
|
|
|
|
=item C<%cleaners> |
2655
|
|
|
|
|
|
|
|
2656
|
|
|
|
|
|
|
C<$attribute_options{$class}-E<gt>{$attribute}> is a reference to a |
2657
|
|
|
|
|
|
|
destructor function for that attribute. It is called as an object |
2658
|
|
|
|
|
|
|
method on the object being destroyed, and should ensure that any |
2659
|
|
|
|
|
|
|
circular references that this object is involved in get cleared. |
2660
|
|
|
|
|
|
|
|
2661
|
|
|
|
|
|
|
=item C<%abstract> |
2662
|
|
|
|
|
|
|
|
2663
|
|
|
|
|
|
|
C<$abstract-E<gt>{$class}> is set if the class is abstract |
2664
|
|
|
|
|
|
|
|
2665
|
|
|
|
|
|
|
=item C<%init_defaults> |
2666
|
|
|
|
|
|
|
|
2667
|
|
|
|
|
|
|
C<$init_defaults{$class}-E<gt>{$attribute}> represents what an |
2668
|
|
|
|
|
|
|
attribute is set to automatically if it is not specified when an |
2669
|
|
|
|
|
|
|
object is created. If this is a scalar value, the attribute is set to |
2670
|
|
|
|
|
|
|
the value. If it is a function, then that function is called (as a |
2671
|
|
|
|
|
|
|
method) and should return the value to be placed into that attribute. |
2672
|
|
|
|
|
|
|
If it is a hash ref or an array ref, then that structure is COPIED in |
2673
|
|
|
|
|
|
|
to the new object. If you don't want that, you can do something like |
2674
|
|
|
|
|
|
|
this: |
2675
|
|
|
|
|
|
|
|
2676
|
|
|
|
|
|
|
[...] |
2677
|
|
|
|
|
|
|
flat_hash => { |
2678
|
|
|
|
|
|
|
attribute => { |
2679
|
|
|
|
|
|
|
init_default => sub { { key => "value" } }, |
2680
|
|
|
|
|
|
|
}, |
2681
|
|
|
|
|
|
|
}, |
2682
|
|
|
|
|
|
|
[...] |
2683
|
|
|
|
|
|
|
|
2684
|
|
|
|
|
|
|
Now, every new object will share the same hash for that attribute. |
2685
|
|
|
|
|
|
|
|
2686
|
|
|
|
|
|
|
=item C<%companions> |
2687
|
|
|
|
|
|
|
|
2688
|
|
|
|
|
|
|
Any "Companion" relationships between attributes, that are to be |
2689
|
|
|
|
|
|
|
treated as linked pairs of relationships; deleting object A from |
2690
|
|
|
|
|
|
|
container B of object C will also cause object C to be removed from |
2691
|
|
|
|
|
|
|
container D of object A. |
2692
|
|
|
|
|
|
|
|
2693
|
|
|
|
|
|
|
=back |
2694
|
|
|
|
|
|
|
|
2695
|
|
|
|
|
|
|
There are currently four functions that allow you to access parts of |
2696
|
|
|
|
|
|
|
this information. |
2697
|
|
|
|
|
|
|
|
2698
|
|
|
|
|
|
|
=over |
2699
|
|
|
|
|
|
|
|
2700
|
|
|
|
|
|
|
=item Class::Tangram::attribute_options($class) |
2701
|
|
|
|
|
|
|
|
2702
|
|
|
|
|
|
|
Returns a hash ref to a data structure from attribute names to the |
2703
|
|
|
|
|
|
|
option hash for that attribute. |
2704
|
|
|
|
|
|
|
|
2705
|
|
|
|
|
|
|
=cut |
2706
|
|
|
|
|
|
|
|
2707
|
|
|
|
|
|
|
sub attribute_options { |
2708
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2709
|
0
|
|
|
|
|
0
|
return $attribute_options{$class}; |
2710
|
|
|
|
|
|
|
} |
2711
|
|
|
|
|
|
|
|
2712
|
|
|
|
|
|
|
=item Class::Tangram::attribute_types($class) |
2713
|
|
|
|
|
|
|
|
2714
|
|
|
|
|
|
|
Returns a hash ref from attribute names to the tangram type for that |
2715
|
|
|
|
|
|
|
attribute. |
2716
|
|
|
|
|
|
|
|
2717
|
|
|
|
|
|
|
=cut |
2718
|
|
|
|
|
|
|
|
2719
|
|
|
|
|
|
|
sub attribute_types { |
2720
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2721
|
0
|
|
|
|
|
0
|
return $types{$class}; |
2722
|
|
|
|
|
|
|
} |
2723
|
|
|
|
|
|
|
|
2724
|
|
|
|
|
|
|
=item Class::Tangram::required_attributes($class) |
2725
|
|
|
|
|
|
|
|
2726
|
|
|
|
|
|
|
Returns a hash ref from attribute names to the 'required' option setting for |
2727
|
|
|
|
|
|
|
that attribute. May also be called as a method, as in |
2728
|
|
|
|
|
|
|
C<$instance-E<gt>required_attributes>. |
2729
|
|
|
|
|
|
|
|
2730
|
|
|
|
|
|
|
=cut |
2731
|
|
|
|
|
|
|
|
2732
|
|
|
|
|
|
|
sub required_attributes { |
2733
|
0
|
|
0
|
0
|
1
|
0
|
my $class = ref $_[0] || $_[0]; |
2734
|
0
|
|
|
|
|
0
|
return $required_attributes{$class}; |
2735
|
|
|
|
|
|
|
} |
2736
|
|
|
|
|
|
|
|
2737
|
|
|
|
|
|
|
=item Class::Tangram::init_defaults($class) |
2738
|
|
|
|
|
|
|
|
2739
|
|
|
|
|
|
|
Returns a hash ref from attribute names to the default intial values for |
2740
|
|
|
|
|
|
|
that attribute. May also be called as a method, as in |
2741
|
|
|
|
|
|
|
C<$instance-E<gt>init_defaults>. |
2742
|
|
|
|
|
|
|
|
2743
|
|
|
|
|
|
|
=cut |
2744
|
|
|
|
|
|
|
|
2745
|
|
|
|
|
|
|
sub init_defaults { |
2746
|
0
|
|
0
|
0
|
1
|
0
|
my $class = ref $_[0] || $_[0]; |
2747
|
0
|
|
|
|
|
0
|
return $init_defaults{$class}; |
2748
|
|
|
|
|
|
|
} |
2749
|
|
|
|
|
|
|
|
2750
|
|
|
|
|
|
|
=item Class::Tangram::companions($class) |
2751
|
|
|
|
|
|
|
|
2752
|
|
|
|
|
|
|
Returns a hash ref from attribute names to the default intial values for |
2753
|
|
|
|
|
|
|
that attribute. May also be called as a method, as in |
2754
|
|
|
|
|
|
|
C<$instance-E<gt>init_defaults>. |
2755
|
|
|
|
|
|
|
|
2756
|
|
|
|
|
|
|
=cut |
2757
|
|
|
|
|
|
|
|
2758
|
|
|
|
|
|
|
sub companions { |
2759
|
0
|
|
0
|
0
|
1
|
0
|
my $class = ref $_[0] || $_[0]; |
2760
|
0
|
0
|
|
|
|
0
|
if (!defined($class)) { |
2761
|
0
|
|
|
|
|
0
|
return keys %companions; |
2762
|
|
|
|
|
|
|
} else { |
2763
|
0
|
|
|
|
|
0
|
return $companions{$class}; |
2764
|
|
|
|
|
|
|
} |
2765
|
|
|
|
|
|
|
} |
2766
|
|
|
|
|
|
|
|
2767
|
|
|
|
|
|
|
=item Class::Tangram::known_classes |
2768
|
|
|
|
|
|
|
|
2769
|
|
|
|
|
|
|
This function returns a list of all the classes that have had their |
2770
|
|
|
|
|
|
|
object schema imported by Class::Tangram. |
2771
|
|
|
|
|
|
|
|
2772
|
|
|
|
|
|
|
=cut |
2773
|
|
|
|
|
|
|
|
2774
|
|
|
|
|
|
|
sub known_classes { |
2775
|
0
|
|
|
0
|
1
|
0
|
return keys %types; |
2776
|
|
|
|
|
|
|
} |
2777
|
|
|
|
|
|
|
|
2778
|
|
|
|
|
|
|
=item Class::Tangram::is_abstract($class) |
2779
|
|
|
|
|
|
|
|
2780
|
|
|
|
|
|
|
This function returns true if the supplied class is abstract. |
2781
|
|
|
|
|
|
|
|
2782
|
|
|
|
|
|
|
=cut |
2783
|
|
|
|
|
|
|
|
2784
|
|
|
|
|
|
|
sub is_abstract { |
2785
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2786
|
0
|
0
|
|
|
|
0
|
$class eq "Class::Tangram" && ($class = shift); |
2787
|
|
|
|
|
|
|
|
2788
|
0
|
0
|
|
|
|
0
|
exists $cleaners{$class} or import_schema($class); |
2789
|
|
|
|
|
|
|
} |
2790
|
|
|
|
|
|
|
|
2791
|
|
|
|
|
|
|
=item Class->set_init_default(attribute => $value); |
2792
|
|
|
|
|
|
|
|
2793
|
|
|
|
|
|
|
Sets the default value on an attribute for newly created "Class" |
2794
|
|
|
|
|
|
|
objects, as if it had been declared with init_default. Can be called |
2795
|
|
|
|
|
|
|
as a class or an instance method. |
2796
|
|
|
|
|
|
|
|
2797
|
|
|
|
|
|
|
=cut |
2798
|
|
|
|
|
|
|
|
2799
|
|
|
|
|
|
|
sub set_init_default { |
2800
|
2
|
|
|
2
|
1
|
5
|
my $invocant = shift; |
2801
|
2
|
|
66
|
|
|
11
|
my $class = ref $invocant || $invocant; |
2802
|
|
|
|
|
|
|
|
2803
|
2
|
50
|
|
|
|
7
|
exists $init_defaults{$class} or import_schema($class); |
2804
|
|
|
|
|
|
|
|
2805
|
2
|
|
|
|
|
11
|
while ( my ($attribute, $value) = splice @_, 0, 2) { |
2806
|
2
|
|
|
|
|
12
|
$init_defaults{$class}->{$attribute} = $value; |
2807
|
|
|
|
|
|
|
} |
2808
|
|
|
|
|
|
|
} |
2809
|
|
|
|
|
|
|
|
2810
|
|
|
|
|
|
|
=back |
2811
|
|
|
|
|
|
|
|
2812
|
|
|
|
|
|
|
=cut |
2813
|
|
|
|
|
|
|
|
2814
|
|
|
|
|
|
|
# a little embedded package |
2815
|
|
|
|
|
|
|
|
2816
|
|
|
|
|
|
|
package Tangram::Transient; |
2817
|
|
|
|
|
|
|
|
2818
|
|
|
|
|
|
|
BEGIN { |
2819
|
7
|
|
|
7
|
|
834
|
eval "use base qw(Tangram::Type)"; |
|
7
|
|
|
7
|
|
64
|
|
|
7
|
|
|
|
|
16
|
|
|
7
|
|
|
|
|
6573
|
|
2820
|
7
|
50
|
|
|
|
1133
|
if ( $@ ) { |
2821
|
|
|
|
|
|
|
# no tangram |
2822
|
|
|
|
|
|
|
} else { |
2823
|
0
|
|
|
|
|
0
|
$Tangram::Schema::TYPES{transient} = bless {}, __PACKAGE__; |
2824
|
|
|
|
|
|
|
} |
2825
|
|
|
|
|
|
|
} |
2826
|
|
|
|
|
|
|
|
2827
|
0
|
|
|
0
|
|
|
sub coldefs { } |
2828
|
|
|
|
|
|
|
|
2829
|
0
|
|
|
0
|
|
|
sub get_exporter { } |
2830
|
0
|
|
|
0
|
|
|
sub get_importer { } |
2831
|
|
|
|
|
|
|
|
2832
|
|
|
|
|
|
|
sub get_import_cols { |
2833
|
|
|
|
|
|
|
# print "Get_import_cols:" , Dumper \@_; |
2834
|
0
|
|
|
0
|
|
|
return (); |
2835
|
|
|
|
|
|
|
} |
2836
|
|
|
|
|
|
|
|
2837
|
|
|
|
|
|
|
=head1 SEE ALSO |
2838
|
|
|
|
|
|
|
|
2839
|
|
|
|
|
|
|
L<Tangram::Schema> |
2840
|
|
|
|
|
|
|
|
2841
|
|
|
|
|
|
|
B<A guided tour of Tangram, by Sound Object Logic.> |
2842
|
|
|
|
|
|
|
|
2843
|
|
|
|
|
|
|
http://www.soundobjectlogic.com/tangram/guided_tour/fs.html |
2844
|
|
|
|
|
|
|
|
2845
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
2846
|
|
|
|
|
|
|
|
2847
|
|
|
|
|
|
|
The following modules are required to be installed to use |
2848
|
|
|
|
|
|
|
Class::Tangram: |
2849
|
|
|
|
|
|
|
|
2850
|
|
|
|
|
|
|
Set::Object => 1.02 |
2851
|
|
|
|
|
|
|
Test::Simple => 0.18 |
2852
|
|
|
|
|
|
|
Date::Manip => 5.21 |
2853
|
|
|
|
|
|
|
|
2854
|
|
|
|
|
|
|
Test::Simple and Date::Manip are only required to run the test suite. |
2855
|
|
|
|
|
|
|
|
2856
|
|
|
|
|
|
|
If you find Class::Tangram passes the test suite with earlier versions |
2857
|
|
|
|
|
|
|
of the above modules, please send me an e-mail. |
2858
|
|
|
|
|
|
|
|
2859
|
|
|
|
|
|
|
=head2 MODULE RELEASE |
2860
|
|
|
|
|
|
|
|
2861
|
|
|
|
|
|
|
This is Class::Tangram version 1.14. |
2862
|
|
|
|
|
|
|
|
2863
|
|
|
|
|
|
|
=head1 BUGS/TODO |
2864
|
|
|
|
|
|
|
|
2865
|
|
|
|
|
|
|
=over |
2866
|
|
|
|
|
|
|
|
2867
|
|
|
|
|
|
|
=item * |
2868
|
|
|
|
|
|
|
|
2869
|
|
|
|
|
|
|
Inside an over-ridden C<$obj->set_attribute> function, it is not |
2870
|
|
|
|
|
|
|
possible to call C<$self->SUPER::set_attribute>, because that function |
2871
|
|
|
|
|
|
|
does not exist in any superclass' namespace. So, you have to modify |
2872
|
|
|
|
|
|
|
your own hash directly - ie |
2873
|
|
|
|
|
|
|
|
2874
|
|
|
|
|
|
|
$self->{attribute} = $value; |
2875
|
|
|
|
|
|
|
|
2876
|
|
|
|
|
|
|
Instead of the purer OO |
2877
|
|
|
|
|
|
|
|
2878
|
|
|
|
|
|
|
$self->SUPER::set_attribute($value); |
2879
|
|
|
|
|
|
|
|
2880
|
|
|
|
|
|
|
Solutions to this problem may involve creating an intermediate |
2881
|
|
|
|
|
|
|
super-class that contains those functions, and then replacing |
2882
|
|
|
|
|
|
|
C<Class::Tangram> in C<@Class::ISA> with the intermediate class. |
2883
|
|
|
|
|
|
|
|
2884
|
|
|
|
|
|
|
=item * |
2885
|
|
|
|
|
|
|
|
2886
|
|
|
|
|
|
|
Container enhancements; |
2887
|
|
|
|
|
|
|
|
2888
|
|
|
|
|
|
|
=over |
2889
|
|
|
|
|
|
|
|
2890
|
|
|
|
|
|
|
=item copy constructor |
2891
|
|
|
|
|
|
|
|
2892
|
|
|
|
|
|
|
The copy constructor now automatically duplicates |
2893
|
|
|
|
|
|
|
|
2894
|
|
|
|
|
|
|
=back |
2895
|
|
|
|
|
|
|
|
2896
|
|
|
|
|
|
|
|
2897
|
|
|
|
|
|
|
=back |
2898
|
|
|
|
|
|
|
|
2899
|
|
|
|
|
|
|
|
2900
|
|
|
|
|
|
|
- $obj->new() should take a copy of containers etc |
2901
|
|
|
|
|
|
|
|
2902
|
|
|
|
|
|
|
New `array' functions: |
2903
|
|
|
|
|
|
|
- $obj->attr_push() |
2904
|
|
|
|
|
|
|
|
2905
|
|
|
|
|
|
|
* Container notification system |
2906
|
|
|
|
|
|
|
|
2907
|
|
|
|
|
|
|
- all $obj->attr_do functions call $obj->set_attr to provide a |
2908
|
|
|
|
|
|
|
single place to catch modifications of that attribute |
2909
|
|
|
|
|
|
|
|
2910
|
|
|
|
|
|
|
- |
2911
|
|
|
|
|
|
|
|
2912
|
|
|
|
|
|
|
* |
2913
|
|
|
|
|
|
|
|
2914
|
|
|
|
|
|
|
* back-reference notification system |
2915
|
|
|
|
|
|
|
|
2916
|
|
|
|
|
|
|
There should be more functions for breaking loops; in particular, a |
2917
|
|
|
|
|
|
|
standard function called C<drop_refs($obj)>, which replaces references |
2918
|
|
|
|
|
|
|
to $obj with the appropriate C<Tangram::RefOnDemand> object so that an |
2919
|
|
|
|
|
|
|
object can be unloaded via C<Tangram::Storage->unload()> and actually |
2920
|
|
|
|
|
|
|
have a hope of being reclaimed. Another function that would be handy |
2921
|
|
|
|
|
|
|
would be a deep "mark" operation for manual mark & sweep garbage |
2922
|
|
|
|
|
|
|
collection. |
2923
|
|
|
|
|
|
|
|
2924
|
|
|
|
|
|
|
Need to think about writing some functions using C<Inline> for speed. |
2925
|
|
|
|
|
|
|
One of these days... |
2926
|
|
|
|
|
|
|
|
2927
|
|
|
|
|
|
|
Allow C<init_default> values to be set in a default import function? |
2928
|
|
|
|
|
|
|
|
2929
|
|
|
|
|
|
|
ie |
2930
|
|
|
|
|
|
|
|
2931
|
|
|
|
|
|
|
use MyClassTangramObject -defaults => { foo => "bar" }; |
2932
|
|
|
|
|
|
|
|
2933
|
|
|
|
|
|
|
=head1 AUTHOR |
2934
|
|
|
|
|
|
|
|
2935
|
|
|
|
|
|
|
Sam Vilain, <sam@vilain.net> |
2936
|
|
|
|
|
|
|
|
2937
|
|
|
|
|
|
|
=head2 CREDITS |
2938
|
|
|
|
|
|
|
|
2939
|
|
|
|
|
|
|
# Some modifications |
2940
|
|
|
|
|
|
|
# Copyright © 2001 Micro Sharp Technologies, Inc., Vancouver, WA, USA |
2941
|
|
|
|
|
|
|
# Author: Karl M. Hegbloom <karlheg@microsharp.com> |
2942
|
|
|
|
|
|
|
# Perl Artistic Licence. |
2943
|
|
|
|
|
|
|
|
2944
|
|
|
|
|
|
|
Many thanks to Charles Owens and David Wheeler for their feedback, |
2945
|
|
|
|
|
|
|
ideas, patches and bug testing. |
2946
|
|
|
|
|
|
|
|
2947
|
|
|
|
|
|
|
=cut |
2948
|
|
|
|
|
|
|
|
2949
|
|
|
|
|
|
|
69; |
2950
|
|
|
|
|
|
|
|
2951
|
|
|
|
|
|
|
__END__ |
2952
|
|
|
|
|
|
|
|
2953
|
|
|
|
|
|
|
# From old SYNOPSIS, I decided it was too long. A lot of |
2954
|
|
|
|
|
|
|
# the information here needs to be re-integrated into the |
2955
|
|
|
|
|
|
|
# POD. |
2956
|
|
|
|
|
|
|
|
2957
|
|
|
|
|
|
|
package Project; |
2958
|
|
|
|
|
|
|
|
2959
|
|
|
|
|
|
|
# here's where we build the individual object schemas into |
2960
|
|
|
|
|
|
|
# a Tangram::Schema object, which the Tangram::Storage |
2961
|
|
|
|
|
|
|
# class uses to know which tables and columns to find |
2962
|
|
|
|
|
|
|
# objects. |
2963
|
|
|
|
|
|
|
use Tangram::Schema; |
2964
|
|
|
|
|
|
|
|
2965
|
|
|
|
|
|
|
# TIMTOWTDI - this is the condensed manpage version :) |
2966
|
|
|
|
|
|
|
my $dbschema = Tangram::Schema->new |
2967
|
|
|
|
|
|
|
({ classes => |
2968
|
|
|
|
|
|
|
[ 'Orange' => { fields => $Orange::fields }, |
2969
|
|
|
|
|
|
|
'MyObject' => { fields => $MyObject::schema }, ]}); |
2970
|
|
|
|
|
|
|
|
2971
|
|
|
|
|
|
|
sub schema { $dbschema }; |
2972
|
|
|
|
|
|
|
|
2973
|
|
|
|
|
|
|
package main; |
2974
|
|
|
|
|
|
|
|
2975
|
|
|
|
|
|
|
# See Tangram::Relational for instructions on using |
2976
|
|
|
|
|
|
|
# "deploy" to create the database this connects to. You |
2977
|
|
|
|
|
|
|
# only have to do this if you want to write the objects to |
2978
|
|
|
|
|
|
|
# a database. |
2979
|
|
|
|
|
|
|
use Tangram::Relational; |
2980
|
|
|
|
|
|
|
my ($dsn, $u, $p); |
2981
|
|
|
|
|
|
|
my $storage = Tangram::Relational->connect |
2982
|
|
|
|
|
|
|
(Project->schema, $dsn, $u, $p); |
2983
|
|
|
|
|
|
|
|
2984
|
|
|
|
|
|
|
# Create an orange |
2985
|
|
|
|
|
|
|
my $orange = Orange->new( |
2986
|
|
|
|
|
|
|
juiciness => 8, |
2987
|
|
|
|
|
|
|
type => 'Florida', |
2988
|
|
|
|
|
|
|
tag => '', # required |
2989
|
|
|
|
|
|
|
); |
2990
|
|
|
|
|
|
|
|
2991
|
|
|
|
|
|
|
# Store it |
2992
|
|
|
|
|
|
|
$storage->insert($orange); |
2993
|
|
|
|
|
|
|
|
2994
|
|
|
|
|
|
|
# This is how you get values out of the objects |
2995
|
|
|
|
|
|
|
my $juiciness = $orange->juiciness; |
2996
|
|
|
|
|
|
|
|
2997
|
|
|
|
|
|
|
# a "ref" must be set to a blessed object, any object |
2998
|
|
|
|
|
|
|
my $grower = bless { name => "Joe" }, "Farmer"; |
2999
|
|
|
|
|
|
|
$orange->set_grower ($grower); |
3000
|
|
|
|
|
|
|
|
3001
|
|
|
|
|
|
|
# these are all illegal - type checking is fairly strict |
3002
|
|
|
|
|
|
|
my $orange = eval { Orange->new; }; print $@; |
3003
|
|
|
|
|
|
|
eval { $orange->set_juiciness ("Yum"); }; print $@; |
3004
|
|
|
|
|
|
|
eval { $orange->set_segments (31); }; print $@; |
3005
|
|
|
|
|
|
|
eval { $orange->set_grower ("Mr. Nice"); }; print $@; |
3006
|
|
|
|
|
|
|
|
3007
|
|
|
|
|
|
|
# Demonstrate some "required" functionality |
3008
|
|
|
|
|
|
|
eval { $orange->set_type (''); }; print $@; |
3009
|
|
|
|
|
|
|
eval { $orange->set_type (undef); }; print $@; |
3010
|
|
|
|
|
|
|
eval { $orange->set_tag (undef); }; print $@; |
3011
|
|
|
|
|
|
|
|
3012
|
|
|
|
|
|
|
# this works too, but is slower |
3013
|
|
|
|
|
|
|
$orange->get( "juiciness" ); |
3014
|
|
|
|
|
|
|
$orange->set( juiciness => 123, |
3015
|
|
|
|
|
|
|
segments => 17 ); |
3016
|
|
|
|
|
|
|
|
3017
|
|
|
|
|
|
|
# Re-configure init_default - make each new orange have a |
3018
|
|
|
|
|
|
|
# random juiciness |
3019
|
|
|
|
|
|
|
$orange->set_init_default( juiciness => sub { int(rand(45)) } ); |