1 |
cvsjoko |
1.1 |
package Class::Tangram; |
2 |
|
|
|
3 |
joko |
1.3 |
# Copyright (c) 2001, 2002 Sam Vilain. All right reserved. This file |
4 |
|
|
# is licensed under the terms of the Perl Artistic license. |
5 |
cvsjoko |
1.1 |
|
6 |
|
|
=head1 NAME |
7 |
|
|
|
8 |
|
|
Class::Tangram - create constructors, accessor and update methods for |
9 |
|
|
objects from a Tangram-compatible object specification. |
10 |
|
|
|
11 |
|
|
=head1 SYNOPSIS |
12 |
|
|
|
13 |
joko |
1.3 |
package MyObject; |
14 |
cvsjoko |
1.1 |
|
15 |
|
|
use base qw(Class::Tangram); |
16 |
|
|
|
17 |
joko |
1.3 |
our $fields => { int => [ qw(foo bar) ], |
18 |
|
|
string => [ qw(baz quux) ] }; |
19 |
|
|
|
20 |
|
|
package main; |
21 |
|
|
|
22 |
|
|
my $object = MyObject->new(foo => 2, baz => "hello"); |
23 |
|
|
|
24 |
|
|
print $object->baz(); # prints "hello" |
25 |
|
|
|
26 |
|
|
$object->set_quux("Something"); |
27 |
cvsjoko |
1.1 |
|
28 |
joko |
1.3 |
$object->set_foo("Something"); # dies - not an integer |
29 |
cvsjoko |
1.1 |
|
30 |
joko |
1.3 |
=head1 DESCRIPTION |
31 |
cvsjoko |
1.1 |
|
32 |
joko |
1.3 |
Class::Tangram is a tool for defining objects attributes. Simply |
33 |
|
|
define your object's fields/attributes using the same syntax |
34 |
|
|
introduced in _A Guided Tour of Tangram_, and you get objects that |
35 |
|
|
work As You'd 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). |
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 with the C<fields> |
66 |
|
|
portion of a tangram schema (see L<Tangram::Schema>). Note: the term |
67 |
|
|
`schema' is used frequently to refer to the C<$fields> structure. |
68 |
cvsjoko |
1.1 |
|
69 |
joko |
1.3 |
For example, |
70 |
cvsjoko |
1.1 |
|
71 |
joko |
1.3 |
package MyObject; |
72 |
|
|
use base qw(Class::Tangram); |
73 |
cvsjoko |
1.1 |
|
74 |
joko |
1.3 |
our $fields = { |
75 |
|
|
int => { |
76 |
|
|
juiciness => undef, |
77 |
|
|
segments => { |
78 |
|
|
# this code reference is called when this |
79 |
|
|
# attribute is set, to check the value is |
80 |
|
|
# OK |
81 |
|
|
check_func => sub { |
82 |
|
|
die "too many segments" |
83 |
|
|
if (${(shift)} > 30); |
84 |
|
|
}, |
85 |
|
|
# the default for this attribute. |
86 |
|
|
init_default => 7, |
87 |
|
|
}, |
88 |
|
|
}, |
89 |
|
|
ref => { |
90 |
|
|
grower => undef, |
91 |
|
|
}, |
92 |
cvsjoko |
1.1 |
|
93 |
joko |
1.3 |
# 'required' attributes - insist that these fields are |
94 |
|
|
# set, both with constructor and set()/set_X methods |
95 |
|
|
string => { |
96 |
|
|
# true: 'type' must have non-empty value (for |
97 |
|
|
# strings) or be logically true (for other types) |
98 |
|
|
type => { required => 1 }, |
99 |
cvsjoko |
1.1 |
|
100 |
joko |
1.3 |
# false: 'tag' must be defined but may be empty |
101 |
|
|
tag => { required => '' }, |
102 |
|
|
}, |
103 |
cvsjoko |
1.1 |
|
104 |
joko |
1.3 |
# fields allowed by Class::Tangram but not ever |
105 |
|
|
# stored by Tangram - no type checking by default |
106 |
|
|
transient => [ qw(_tangible) ], |
107 |
|
|
}; |
108 |
cvsjoko |
1.1 |
|
109 |
joko |
1.3 |
It is of critical importance to your sanity that you understand how |
110 |
|
|
anonymous hashes and anonymous arrays work in Perl. Some additional |
111 |
|
|
features are used above that have not yet been introduced, but you |
112 |
|
|
should be able to look at the above data structure and see that it |
113 |
|
|
satisfies the conditions stated in the paragraph before it. If it is |
114 |
|
|
hazy, I recommend reading L<perlref> or L<perlreftut>. |
115 |
|
|
|
116 |
|
|
When the schema for the object is first imported (see L<Schema |
117 |
|
|
import>), Class::Tangram defines accessor functions for each of the |
118 |
|
|
attributes defined in the schema. These accessor functions are then |
119 |
|
|
available as C<$object-E<gt>function> on created objects. By virtue |
120 |
|
|
of inheritance, various other methods are available. |
121 |
cvsjoko |
1.1 |
|
122 |
joko |
1.4 |
From Class::Tangram 1.13 onwards, no use of perl's C<AUTOLOAD> |
123 |
joko |
1.3 |
functionality is used. |
124 |
cvsjoko |
1.1 |
|
125 |
|
|
=cut |
126 |
|
|
|
127 |
|
|
use strict; |
128 |
|
|
use Carp qw(croak cluck); |
129 |
|
|
|
130 |
joko |
1.3 |
use vars qw($VERSION %defaults); |
131 |
cvsjoko |
1.1 |
|
132 |
joko |
1.3 |
use Set::Object; |
133 |
|
|
|
134 |
|
|
use Pod::Constants -trim => 1, |
135 |
|
|
'MODULE RELEASE' => sub { ($VERSION) = /(\d+\.\d+)/ }, |
136 |
|
|
'Default Type Checking' => sub { %defaults = eval; }; |
137 |
cvsjoko |
1.1 |
|
138 |
|
|
# $types{$class}->{$attribute} is the tangram type of each attribute |
139 |
|
|
my (%types); |
140 |
|
|
|
141 |
joko |
1.3 |
# $attribute_options{$class}->{$attribute} is the hash passed to tangram |
142 |
|
|
# for the given attribute |
143 |
|
|
my (%attribute_options); |
144 |
|
|
|
145 |
cvsjoko |
1.1 |
# $check{$class}->{$attribute}->($value) is a function that will die |
146 |
|
|
# if $value is not alright, see check_X functions |
147 |
|
|
my (%check); |
148 |
|
|
|
149 |
|
|
# Destructors for each attribute. They are called as |
150 |
|
|
# $cleaners{$class}->{$attribute}->($self, $attribute); |
151 |
|
|
my (%cleaners); |
152 |
|
|
|
153 |
|
|
# init_default values for each attribute. These could be hash refs, |
154 |
|
|
# array refs, code refs, or simple scalars. They will be stored as |
155 |
|
|
# $init_defaults{$class}->{$attribute} |
156 |
|
|
my (%init_defaults); |
157 |
|
|
|
158 |
joko |
1.3 |
# $required_attributes{$class}->{$attribute} records which attributes |
159 |
|
|
# are required... used only by new() at present. |
160 |
|
|
my (%required_attributes); |
161 |
|
|
|
162 |
cvsjoko |
1.1 |
# if a class is abstract, complain if one is constructed. |
163 |
|
|
my (%abstract); |
164 |
|
|
|
165 |
joko |
1.4 |
# records the inheritances of classes. |
166 |
|
|
my (%bases); |
167 |
|
|
|
168 |
cvsjoko |
1.1 |
=head1 METHODS |
169 |
|
|
|
170 |
joko |
1.3 |
The following methods are available for all Class::Tangram objects |
171 |
|
|
|
172 |
|
|
=head2 Constructor |
173 |
|
|
|
174 |
|
|
A Constructor is a method that returns a new instance of an object. |
175 |
|
|
|
176 |
cvsjoko |
1.1 |
=over 4 |
177 |
|
|
|
178 |
|
|
=item Class-E<gt>new (attribute1 =E<gt> value, attribute2 =E<gt> value) |
179 |
|
|
|
180 |
joko |
1.3 |
Sets up a new object of type C<Class>, with attributes set to the |
181 |
|
|
values supplied. |
182 |
cvsjoko |
1.1 |
|
183 |
joko |
1.3 |
Can also be used as an object method (normal use is as a "class |
184 |
|
|
method"), in which case it returns a B<copy> of the object, without |
185 |
|
|
any deep copying. |
186 |
cvsjoko |
1.1 |
|
187 |
|
|
=cut |
188 |
|
|
|
189 |
|
|
sub new ($@) |
190 |
|
|
{ |
191 |
|
|
my $invocant = shift; |
192 |
|
|
my $class = ref $invocant || $invocant; |
193 |
|
|
|
194 |
joko |
1.3 |
(my @values, @_) = @_; |
195 |
cvsjoko |
1.1 |
|
196 |
|
|
# Setup the object |
197 |
|
|
my $self = { }; |
198 |
|
|
bless $self, $class; |
199 |
|
|
|
200 |
joko |
1.3 |
# auto-load schema as necessary |
201 |
|
|
exists $types{$class} or import_schema($class); |
202 |
cvsjoko |
1.1 |
|
203 |
|
|
croak "Attempt to instantiate an abstract type" |
204 |
|
|
if ($abstract{$class}); |
205 |
|
|
|
206 |
joko |
1.3 |
if (ref $invocant) |
207 |
cvsjoko |
1.1 |
{ |
208 |
|
|
# The copy constructor; this could be better :) |
209 |
|
|
# this has the side effect of much auto-vivification. |
210 |
|
|
%$self = %$invocant; |
211 |
|
|
$self->set (@values); # override with @values |
212 |
|
|
} |
213 |
|
|
else |
214 |
|
|
{ |
215 |
|
|
$self->set (@values); # start with @values |
216 |
joko |
1.3 |
} |
217 |
|
|
|
218 |
|
|
$self->_fill_init_default(); |
219 |
|
|
$self->_check_required(); |
220 |
cvsjoko |
1.1 |
|
221 |
joko |
1.3 |
return $self; |
222 |
|
|
|
223 |
|
|
} |
224 |
cvsjoko |
1.1 |
|
225 |
joko |
1.3 |
sub _fill_init_default { |
226 |
|
|
my $self = shift; |
227 |
|
|
my $class = ref $self or die "_fill_init_default usage error"; |
228 |
|
|
|
229 |
|
|
# fill in fields that have defaults |
230 |
|
|
while ( my ($attribute, $default) = |
231 |
|
|
each %{$init_defaults{$class}} ) { |
232 |
|
|
|
233 |
|
|
next if (exists $self->{$attribute}); |
234 |
|
|
|
235 |
|
|
my $setter = "set_$attribute"; |
236 |
|
|
if (ref $default eq "CODE") { |
237 |
|
|
# sub { }, attribute gets return value |
238 |
|
|
$self->$setter( $default->($self) ); |
239 |
|
|
|
240 |
|
|
} elsif (ref $default eq "HASH") { |
241 |
|
|
# hash ref, copy hash |
242 |
|
|
$self->$setter( { %{ $default } } ); |
243 |
|
|
|
244 |
|
|
} elsif (ref $default eq "ARRAY") { |
245 |
|
|
# array ref, copy array |
246 |
|
|
$self->$setter( [ @{ $default } ] ); |
247 |
cvsjoko |
1.1 |
|
248 |
joko |
1.3 |
} else { |
249 |
|
|
# something else, an object or a scalar |
250 |
|
|
$self->$setter($default); |
251 |
|
|
} |
252 |
|
|
} |
253 |
|
|
} |
254 |
cvsjoko |
1.1 |
|
255 |
joko |
1.3 |
sub _check_required { |
256 |
|
|
my $self = shift; |
257 |
|
|
my $class = ref $self; |
258 |
|
|
|
259 |
|
|
# make sure field is not undef if 'required' option is set |
260 |
|
|
if (my $required = $required_attributes{$class}) { |
261 |
|
|
|
262 |
|
|
# find the immediate caller outside of this package |
263 |
|
|
my $i = 0; |
264 |
|
|
$i++ while UNIVERSAL::isa($self, scalar(caller($i))||";->"); |
265 |
|
|
|
266 |
|
|
# give Tangram some lenience - it is exempt from the effects |
267 |
|
|
# of the "required" option |
268 |
|
|
unless ( caller($i) =~ m/^Tangram::/ ) { |
269 |
|
|
my @missing; |
270 |
|
|
while ( my ($attribute, $value) = each %$required ) { |
271 |
|
|
push(@missing, $attribute) |
272 |
|
|
if ! exists $self->{$attribute}; |
273 |
cvsjoko |
1.1 |
} |
274 |
joko |
1.3 |
croak("object missing required attribute(s): " |
275 |
|
|
.join(', ',@missing).'.') if @missing; |
276 |
cvsjoko |
1.1 |
} |
277 |
|
|
} |
278 |
|
|
} |
279 |
|
|
|
280 |
joko |
1.3 |
=back |
281 |
|
|
|
282 |
|
|
=head2 Accessing & Setting Attributes |
283 |
|
|
|
284 |
|
|
=over |
285 |
|
|
|
286 |
cvsjoko |
1.1 |
=item $instance->set(attribute => $value, ...) |
287 |
|
|
|
288 |
|
|
Sets the attributes of the given instance to the given values. croaks |
289 |
|
|
if there is a problem with the values. |
290 |
|
|
|
291 |
joko |
1.3 |
This function simply calls C<$instance-E<gt>set_attribute($value)> for |
292 |
|
|
each of the C<attribute =E<gt> $value> pairs passed to it. |
293 |
|
|
|
294 |
cvsjoko |
1.1 |
=cut |
295 |
|
|
|
296 |
joko |
1.3 |
sub set { |
297 |
|
|
my $self = shift; |
298 |
cvsjoko |
1.1 |
|
299 |
|
|
# yes, this is a lot to do. yes, it's slow. But I'm fairly |
300 |
|
|
# certain that this could be handled efficiently if it were to be |
301 |
|
|
# moved inside the Perl interpreter or an XS module |
302 |
joko |
1.3 |
UNIVERSAL::isa($self, "Class::Tangram") or croak "type mismatch"; |
303 |
cvsjoko |
1.1 |
my $class = ref $self; |
304 |
|
|
exists $check{$class} or import_schema($class); |
305 |
joko |
1.3 |
croak "set must be called with an even number of arguments" |
306 |
|
|
if (scalar(@_) & 1); |
307 |
cvsjoko |
1.1 |
|
308 |
joko |
1.3 |
while (my ($name, $value) = splice @_, 0, 2) { |
309 |
cvsjoko |
1.1 |
|
310 |
joko |
1.3 |
my $setter = "set_".$name; |
311 |
cvsjoko |
1.1 |
|
312 |
joko |
1.3 |
croak "attempt to set an illegal field $name in a $class" |
313 |
|
|
unless $self->can($setter); |
314 |
cvsjoko |
1.1 |
|
315 |
joko |
1.3 |
$self->$setter($value); |
316 |
cvsjoko |
1.1 |
} |
317 |
|
|
} |
318 |
|
|
|
319 |
joko |
1.3 |
=item $instance->get("attribute") |
320 |
cvsjoko |
1.1 |
|
321 |
joko |
1.3 |
Gets the value of C<$attribute>. This simply calls |
322 |
|
|
C<$instance-E<gt>get_attribute>. If multiple attributes are listed, |
323 |
|
|
then a list of the attribute values is returned in order. Note that |
324 |
|
|
you get back the results of the scalar context C<get_attribute> call |
325 |
|
|
in this case. |
326 |
cvsjoko |
1.1 |
|
327 |
|
|
=cut |
328 |
|
|
|
329 |
joko |
1.3 |
sub get { |
330 |
|
|
my $self = shift; |
331 |
|
|
croak "get what?" unless @_; |
332 |
|
|
UNIVERSAL::isa($self, "Class::Tangram") or croak "type mismatch"; |
333 |
|
|
|
334 |
cvsjoko |
1.1 |
my $class = ref $self; |
335 |
|
|
exists $check{$class} or import_schema($class); |
336 |
|
|
|
337 |
joko |
1.3 |
my $multiget = (scalar(@_) != 1); |
338 |
|
|
|
339 |
|
|
my @return; |
340 |
|
|
while ( my $field = shift ) { |
341 |
|
|
my $getter = "get_".$field; |
342 |
|
|
croak "attempt to read an illegal field $field in a $class" |
343 |
|
|
unless $self->can($getter); |
344 |
|
|
|
345 |
|
|
if ( $multiget ) { |
346 |
|
|
push @return, scalar($self->$getter()); |
347 |
|
|
} else { |
348 |
|
|
return $self->$getter(); |
349 |
cvsjoko |
1.1 |
} |
350 |
|
|
} |
351 |
|
|
|
352 |
joko |
1.3 |
return @return; |
353 |
cvsjoko |
1.1 |
} |
354 |
|
|
|
355 |
|
|
=item $instance->attribute($value) |
356 |
|
|
|
357 |
joko |
1.3 |
If C<$value> is not given, then |
358 |
|
|
this is equivalent to C<$instance-E<gt>get_attribute> |
359 |
cvsjoko |
1.1 |
|
360 |
joko |
1.3 |
If C<$value> is given, then this is equivalent to |
361 |
|
|
C<$instance-E<gt>set_attribute($value)>. This usage issues a warning |
362 |
|
|
if warnings are on; you should change your code to use the |
363 |
|
|
set_attribute syntax for better readability. OO veterans will tell |
364 |
|
|
you that for maintainability object method names should always be a |
365 |
|
|
verb. |
366 |
cvsjoko |
1.1 |
|
367 |
|
|
=item $instance->get_attribute |
368 |
|
|
|
369 |
|
|
=item $instance->set_attribute($value) |
370 |
|
|
|
371 |
joko |
1.3 |
The normative way of getting and setting attributes. If you wish to |
372 |
|
|
override the behaviour of an object when getting or setting an |
373 |
|
|
attribute, override these functions. They will be called when you use |
374 |
|
|
C<$instance-E<gt>attribute>, C<$instance-E<gt>get()>, constructors, |
375 |
|
|
etc. |
376 |
cvsjoko |
1.1 |
|
377 |
|
|
=item $instance->attribute_includes(@objects) |
378 |
|
|
|
379 |
|
|
=item $instance->attribute_insert(@objects) |
380 |
|
|
|
381 |
|
|
=item $instance->attribute_size |
382 |
|
|
|
383 |
|
|
=item $instance->attribute_clear |
384 |
|
|
|
385 |
|
|
=item $instance->attribute_remove(@objects) |
386 |
|
|
|
387 |
joko |
1.3 |
This suite of functions applies to attributes that are sets (C<iset> |
388 |
|
|
or C<set>). It could in theory also apply generally to all |
389 |
|
|
collections - ie also arrays (C<iarray> or C<array>), and hashes |
390 |
|
|
(C<hash>, C<ihash>). This will be implemented subject to user demand. |
391 |
|
|
|
392 |
|
|
=back |
393 |
|
|
|
394 |
|
|
B<Note:> The above functions can be overridden, but they may not be |
395 |
|
|
called with the C<$self-E<gt>SUPER::> superclass chaining method. |
396 |
|
|
This is because they are not defined within the scope of |
397 |
|
|
Class::Tangram, only your package. |
398 |
cvsjoko |
1.1 |
|
399 |
|
|
=cut |
400 |
|
|
|
401 |
joko |
1.3 |
=head1 ATTRIBUTE TYPE CHECKING |
402 |
|
|
|
403 |
|
|
Class::Tangram provides type checking of attributes when attributes |
404 |
|
|
are set - either using the default C<set_attribute> functions, or |
405 |
|
|
created via the C<new> constructor. |
406 |
|
|
|
407 |
|
|
The checking has default behaviour for each type of attribute (see |
408 |
|
|
L<Default Type Checking>), and can be extended arbitrarily via a |
409 |
|
|
per-attribute C<check_func>, described below. Critical attributes can |
410 |
|
|
be marked as such with the C<required> flag. |
411 |
|
|
|
412 |
|
|
The specification of this type checking is placed in the class schema, |
413 |
|
|
in the per-attribute B<options hash>. This is a Class::Tangram |
414 |
|
|
extension to the Tangram schema structure. |
415 |
|
|
|
416 |
|
|
=over |
417 |
|
|
|
418 |
|
|
=item check_func |
419 |
|
|
|
420 |
|
|
A function that is called with a B<reference> to the new value in |
421 |
|
|
C<$_[0]>. It should call C<die()> if the value is bad. Note that |
422 |
|
|
this check_func will never be passed an undefined value; this is |
423 |
|
|
covered by the "required" option, below. |
424 |
|
|
|
425 |
|
|
In the example schema (above), the attribute C<segments> has a |
426 |
|
|
C<check_func> that prevents setting the value to anything greater than |
427 |
|
|
30. Note that it does not prevent you from setting the value to |
428 |
|
|
something that is not an integer; if you define a C<check_func>, it |
429 |
|
|
replaces the default. |
430 |
|
|
|
431 |
|
|
=item required |
432 |
|
|
|
433 |
|
|
If this option is set to a true value, then the attribute must be set |
434 |
|
|
to a true value to pass type checking. For string attributes, this |
435 |
|
|
means that the string must be defined and non-empty (so "0" is true). |
436 |
|
|
For other attribute types, the normal Perl definition of logical truth |
437 |
|
|
is used. |
438 |
|
|
|
439 |
|
|
If the required option is defined but logically false, (ie "" or 0), |
440 |
|
|
then the attribute must also be defined, but may be set to a logically |
441 |
|
|
false value. |
442 |
|
|
|
443 |
|
|
If the required option is undefined, then the attribute may be set to |
444 |
|
|
an undefined value. |
445 |
|
|
|
446 |
|
|
For integration with tangram, the C<new()> function has a special |
447 |
|
|
hack; if it is being invoked from within Tangram, then the required |
448 |
|
|
test is skipped. |
449 |
|
|
|
450 |
|
|
=back |
451 |
|
|
|
452 |
|
|
=head2 Other per-attribute options |
453 |
|
|
|
454 |
|
|
Any of the following options may be inserted into the per-attribute |
455 |
|
|
B<options hash>: |
456 |
|
|
|
457 |
|
|
=over |
458 |
cvsjoko |
1.1 |
|
459 |
joko |
1.3 |
=item init_default |
460 |
cvsjoko |
1.1 |
|
461 |
joko |
1.3 |
This value specifies the default value of the attribute when |
462 |
|
|
it is created with C<new()>. It is a scalar value, it is |
463 |
|
|
copied to the fresh object. If it is a code reference, that |
464 |
|
|
code reference is called and its return value inserted into |
465 |
|
|
the attribute. If it is an ARRAY or HASH reference, then |
466 |
|
|
that array or hash is COPIED into the attribute. |
467 |
cvsjoko |
1.1 |
|
468 |
joko |
1.3 |
=item destroy_func |
469 |
cvsjoko |
1.1 |
|
470 |
joko |
1.3 |
If anything special needs to happen to this attribute before the |
471 |
|
|
object is destroyed (or when someone calls |
472 |
|
|
C<$object-E<gt>clear_refs()>), then define this. It is called as |
473 |
|
|
C<$sub-E<gt>($object, "attribute")>. |
474 |
cvsjoko |
1.1 |
|
475 |
joko |
1.3 |
=back |
476 |
cvsjoko |
1.1 |
|
477 |
joko |
1.3 |
=head2 Default Type Checking |
478 |
cvsjoko |
1.1 |
|
479 |
joko |
1.3 |
# The following list is eval'ed from this documentation |
480 |
|
|
# when Class::Tangram loads, and used as default attribute |
481 |
|
|
# options for the specified types. So, eg, the default |
482 |
|
|
# "init_default" for "set" types is a subroutine that |
483 |
|
|
# returns a new Set::Object container. |
484 |
|
|
|
485 |
|
|
# "parse" is special - it is passed the options hash given |
486 |
|
|
# by the user and should return (\&check_func, |
487 |
|
|
# \&destroy_func). This is how the magical string type |
488 |
|
|
# checking is performed - see the entry for parse_string(), |
489 |
|
|
# below. |
490 |
|
|
|
491 |
|
|
int => { check_func => \&check_int }, |
492 |
|
|
real => { check_func => \&check_real }, |
493 |
|
|
string => { parse => \&parse_string }, |
494 |
|
|
ref => { check_func => \&check_obj, |
495 |
|
|
destroy_func => \&destroy_ref }, |
496 |
|
|
array => { check_func => \&check_array, |
497 |
|
|
destroy_func => \&destroy_array }, |
498 |
|
|
iarray => { check_func => \&check_array, |
499 |
|
|
destroy_func => \&destroy_array }, |
500 |
|
|
flat_array => { check_func => \&check_flat_array }, |
501 |
|
|
set => { check_func => \&check_set, |
502 |
|
|
destroy_func => \&destroy_set, |
503 |
|
|
init_default => sub { Set::Object->new() } }, |
504 |
|
|
iset => { check_func => \&check_set, |
505 |
|
|
destroy_func => \&destroy_set, |
506 |
|
|
init_default => sub { Set::Object->new() } }, |
507 |
|
|
dmdatetime => { check_func => \&check_dmdatetime }, |
508 |
|
|
rawdatetime => { check_func => \&check_rawdatetime }, |
509 |
|
|
rawdate => { check_func => \&check_rawdate }, |
510 |
|
|
rawtime => { check_func => \&check_rawtime }, |
511 |
|
|
flat_hash => { check_func => \&check_flat_hash }, |
512 |
|
|
transient => { check_func => \&check_nothing }, |
513 |
|
|
hash => { check_func => \&check_hash, |
514 |
|
|
destroy_func => \&destroy_hash, |
515 |
|
|
get_func => \&get_hash }, |
516 |
|
|
perl_dump => { check_func => \&check_nothing } |
517 |
cvsjoko |
1.1 |
|
518 |
joko |
1.3 |
=over |
519 |
cvsjoko |
1.1 |
|
520 |
|
|
=item check_X (\$value) |
521 |
|
|
|
522 |
joko |
1.3 |
This series of internal functions are built-in C<check_func> functions |
523 |
|
|
defined for all of the standard Tangram attribute types. |
524 |
|
|
|
525 |
|
|
=over |
526 |
cvsjoko |
1.1 |
|
527 |
joko |
1.3 |
=item check_string |
528 |
cvsjoko |
1.1 |
|
529 |
joko |
1.3 |
checks that the supplied value is less than 255 characters long. |
530 |
cvsjoko |
1.1 |
|
531 |
|
|
=cut |
532 |
|
|
|
533 |
|
|
sub check_string { |
534 |
joko |
1.3 |
croak "string too long" |
535 |
cvsjoko |
1.1 |
if (length ${$_[0]} > 255); |
536 |
|
|
} |
537 |
|
|
|
538 |
joko |
1.3 |
=item check_int |
539 |
cvsjoko |
1.1 |
|
540 |
joko |
1.3 |
checks that the value is a (possibly signed) integer |
541 |
cvsjoko |
1.1 |
|
542 |
|
|
=cut |
543 |
|
|
|
544 |
|
|
my $int_re = qr/^-?\d+$/; |
545 |
|
|
sub check_int { |
546 |
joko |
1.3 |
croak "not an integer" |
547 |
|
|
if (${$_[0]} !~ m/$int_re/o); |
548 |
cvsjoko |
1.1 |
} |
549 |
|
|
|
550 |
joko |
1.3 |
=item check_real |
551 |
cvsjoko |
1.1 |
|
552 |
joko |
1.3 |
checks that the value is a real number, by stringifying it and |
553 |
|
|
matching it against (C<m/^-?\d*(\.\d*)?(e-?\d*)?$/>). Inefficient? |
554 |
|
|
Yes. Patches welcome. |
555 |
cvsjoko |
1.1 |
|
556 |
|
|
=cut |
557 |
|
|
|
558 |
|
|
my $real_re = qr/^-?\d*(\.\d*)?(e-?\d*)?$/; |
559 |
|
|
sub check_real { |
560 |
joko |
1.3 |
croak "not a real number" |
561 |
|
|
if (${$_[0]} !~ m/$real_re/o); |
562 |
cvsjoko |
1.1 |
} |
563 |
|
|
|
564 |
joko |
1.3 |
=item check_obj |
565 |
cvsjoko |
1.1 |
|
566 |
joko |
1.3 |
checks that the supplied variable is a reference to a blessed object |
567 |
cvsjoko |
1.1 |
|
568 |
|
|
=cut |
569 |
|
|
|
570 |
|
|
# this pattern matches a regular reference |
571 |
|
|
my $obj_re = qr/^(?:HASH|ARRAY|SCALAR)?$/; |
572 |
|
|
sub check_obj { |
573 |
|
|
croak "not an object reference" |
574 |
joko |
1.3 |
if ((ref ${$_[0]}) =~ m/$obj_re/o); |
575 |
cvsjoko |
1.1 |
} |
576 |
|
|
|
577 |
joko |
1.3 |
=item check_flat_array |
578 |
cvsjoko |
1.1 |
|
579 |
joko |
1.3 |
checks that $value is a ref ARRAY and that all elements are unblessed |
580 |
|
|
scalars. Does NOT currently check that all values are of the correct |
581 |
|
|
type (int vs real vs string, etc) |
582 |
cvsjoko |
1.1 |
|
583 |
|
|
=cut |
584 |
|
|
|
585 |
|
|
sub check_flat_array { |
586 |
|
|
croak "not a flat array" |
587 |
|
|
if (ref ${$_[0]} ne "ARRAY"); |
588 |
joko |
1.3 |
croak "flat array may not contain references" |
589 |
|
|
if (map { (ref $_ ? "1" : ()) } @{$_[0]}); |
590 |
cvsjoko |
1.1 |
} |
591 |
|
|
|
592 |
joko |
1.3 |
=item check_array |
593 |
cvsjoko |
1.1 |
|
594 |
joko |
1.3 |
checks that $value is a ref ARRAY, and that each element in the array |
595 |
|
|
is a reference to a blessed object. |
596 |
cvsjoko |
1.1 |
|
597 |
|
|
=cut |
598 |
|
|
|
599 |
|
|
sub check_array { |
600 |
|
|
croak "array attribute not passed an array ref" |
601 |
|
|
if (ref ${$_[0]} ne "ARRAY"); |
602 |
|
|
for my $a (@{${$_[0]}}) { |
603 |
|
|
croak "member in array not an object reference" |
604 |
joko |
1.3 |
if ((ref $a) =~ m/$obj_re/o); |
605 |
cvsjoko |
1.1 |
} |
606 |
|
|
} |
607 |
|
|
|
608 |
joko |
1.3 |
=item check_set |
609 |
cvsjoko |
1.1 |
|
610 |
joko |
1.3 |
checks that $value->isa("Set::Object") |
611 |
cvsjoko |
1.1 |
|
612 |
|
|
=cut |
613 |
|
|
|
614 |
|
|
sub check_set { |
615 |
|
|
croak "set type not passed a Set::Object" |
616 |
joko |
1.3 |
unless (UNIVERSAL::isa(${$_[0]}, "Set::Object")); |
617 |
cvsjoko |
1.1 |
} |
618 |
|
|
|
619 |
joko |
1.3 |
=item check_rawdate |
620 |
cvsjoko |
1.1 |
|
621 |
joko |
1.3 |
checks that $value is of the form YYYY-MM-DD, or YYYYMMDD, or YYMMDD. |
622 |
cvsjoko |
1.1 |
|
623 |
|
|
=cut |
624 |
|
|
|
625 |
|
|
# YYYY-MM-DD HH:MM:SS |
626 |
joko |
1.3 |
my $rawdate_re = qr/^(?: \d{4}-\d{2}-\d{2} |
627 |
|
|
| (?:\d\d){3,4} |
628 |
|
|
)$/x; |
629 |
|
|
sub check_rawdate { |
630 |
|
|
croak "invalid SQL rawdate" |
631 |
|
|
unless (${$_[0]} =~ m/$rawdate_re/o); |
632 |
|
|
} |
633 |
|
|
|
634 |
|
|
=item check_rawtime |
635 |
|
|
|
636 |
|
|
checks that $value is of the form HH:MM(:SS)? |
637 |
|
|
|
638 |
|
|
=cut |
639 |
|
|
|
640 |
|
|
# YYYY-MM-DD HH:MM:SS |
641 |
|
|
my $rawtime_re = qr/^\d{1,2}:\d{2}(?::\d{2})?$/; |
642 |
|
|
sub check_rawtime { |
643 |
|
|
croak "invalid SQL rawtime" |
644 |
|
|
unless (${$_[0]} =~ m/$rawtime_re/o); |
645 |
cvsjoko |
1.1 |
} |
646 |
|
|
|
647 |
joko |
1.3 |
=item check_rawdatetime |
648 |
cvsjoko |
1.1 |
|
649 |
joko |
1.3 |
checks that $value is of the form YYYY-MM-DD HH:MM(:SS)? (the time |
650 |
|
|
and/or the date can be missing), or a string of numbers between 6 and |
651 |
|
|
14 numbers long. |
652 |
cvsjoko |
1.1 |
|
653 |
|
|
=cut |
654 |
|
|
|
655 |
joko |
1.3 |
my $rawdatetime_re = qr/^(?: |
656 |
|
|
# YYYY-MM-DD HH:MM:SS |
657 |
|
|
(?: (?:\d{4}-\d{2}-\d{2}\s+)? |
658 |
|
|
\d{1,2}:\d{2}(?::\d{2})? |
659 |
|
|
| \d{4}-\d{2}-\d{2} |
660 |
|
|
) |
661 |
|
|
| # YYMMDD, etc |
662 |
|
|
(?:\d\d){3,7} |
663 |
|
|
)$/x; |
664 |
|
|
sub check_rawdatetime { |
665 |
|
|
croak "invalid SQL rawdatetime dude" |
666 |
|
|
unless (${$_[0]} =~ m/$rawdatetime_re/o); |
667 |
cvsjoko |
1.1 |
} |
668 |
|
|
|
669 |
joko |
1.3 |
=item check_dmdatetime |
670 |
cvsjoko |
1.1 |
|
671 |
joko |
1.3 |
checks that $value is of the form YYYYMMDDHH:MM:SS, or those allowed |
672 |
|
|
for rawdatetime. |
673 |
cvsjoko |
1.1 |
|
674 |
|
|
=cut |
675 |
|
|
|
676 |
joko |
1.3 |
sub check_dmdatetime { |
677 |
|
|
croak "invalid SQL rawdatetime dude" |
678 |
|
|
unless (${$_[0]} =~ m/^\d{10}:\d\d:\d\d$|$rawdatetime_re/o); |
679 |
cvsjoko |
1.1 |
} |
680 |
|
|
|
681 |
joko |
1.3 |
=item check_flat_hash |
682 |
cvsjoko |
1.1 |
|
683 |
joko |
1.3 |
checks that $value is a ref HASH and all values are scalars. Does NOT |
684 |
|
|
currently check that all values are of the correct type (int vs real |
685 |
|
|
vs string, etc) |
686 |
cvsjoko |
1.1 |
|
687 |
|
|
=cut |
688 |
|
|
|
689 |
|
|
sub check_flat_hash { |
690 |
|
|
croak "not a hash" |
691 |
|
|
unless (ref ${$_[0]} eq "HASH"); |
692 |
|
|
while (my ($k, $v) = each %${$_[0]}) { |
693 |
|
|
croak "hash not flat" |
694 |
|
|
if (ref $k or ref $v); |
695 |
|
|
} |
696 |
|
|
} |
697 |
|
|
|
698 |
joko |
1.3 |
=item check_hash |
699 |
cvsjoko |
1.1 |
|
700 |
joko |
1.3 |
checks that $value is a ref HASH, that every key in the hash is a |
701 |
|
|
scalar, and that every value is a blessed object. |
702 |
cvsjoko |
1.1 |
|
703 |
|
|
=cut |
704 |
|
|
|
705 |
|
|
sub check_hash { |
706 |
|
|
croak "not a hash" |
707 |
|
|
unless (ref ${$_[0]} eq "HASH"); |
708 |
|
|
while (my ($k, $v) = each %${$_[0]}) { |
709 |
|
|
croak "hash key not flat" |
710 |
|
|
if (ref $k); |
711 |
|
|
croak "hash value not an object" |
712 |
|
|
if (ref $v !~ m/$obj_re/); |
713 |
|
|
} |
714 |
|
|
} |
715 |
|
|
|
716 |
joko |
1.3 |
=item check_nothing |
717 |
cvsjoko |
1.1 |
|
718 |
joko |
1.3 |
checks whether Australians like sport |
719 |
cvsjoko |
1.1 |
|
720 |
|
|
=cut |
721 |
|
|
|
722 |
|
|
sub check_nothing { } |
723 |
|
|
|
724 |
joko |
1.3 |
=back |
725 |
|
|
|
726 |
cvsjoko |
1.1 |
=item destroy_X ($instance, $attr) |
727 |
|
|
|
728 |
|
|
Similar story with the check_X series of functions, these are called |
729 |
|
|
during object destruction on every attribute that has a reference that |
730 |
|
|
might need breaking. Note: B<these functions all assume that |
731 |
|
|
attributes belonging to an object that is being destroyed may be |
732 |
|
|
destroyed also>. In other words, do not allow distinct objects to |
733 |
|
|
share Set::Object containers or hash references in their attributes, |
734 |
|
|
otherwise when one gets destroyed the others will lose their data. |
735 |
|
|
|
736 |
joko |
1.3 |
Available functions: |
737 |
|
|
|
738 |
|
|
=over |
739 |
cvsjoko |
1.1 |
|
740 |
joko |
1.3 |
=item destroy_array |
741 |
|
|
|
742 |
|
|
empties an array |
743 |
cvsjoko |
1.1 |
|
744 |
|
|
=cut |
745 |
|
|
|
746 |
|
|
sub destroy_array { |
747 |
joko |
1.3 |
my $self = shift; |
748 |
|
|
my $attr = shift; |
749 |
cvsjoko |
1.1 |
my $t = tied $self->{$attr}; |
750 |
joko |
1.3 |
@{$self->{$attr}} = () |
751 |
|
|
unless (defined $t and $t =~ m,Tangram::CollOnDemand,); |
752 |
cvsjoko |
1.1 |
delete $self->{$attr}; |
753 |
|
|
} |
754 |
|
|
|
755 |
joko |
1.3 |
=item destroy_set |
756 |
cvsjoko |
1.1 |
|
757 |
joko |
1.3 |
Calls Set::Object::clear to clear the set |
758 |
cvsjoko |
1.1 |
|
759 |
|
|
=cut |
760 |
|
|
|
761 |
|
|
sub destroy_set { |
762 |
joko |
1.3 |
my $self = shift; |
763 |
|
|
my $attr = shift; |
764 |
cvsjoko |
1.1 |
|
765 |
|
|
my $t = tied $self->{$attr}; |
766 |
joko |
1.3 |
return if (defined $t and $t =~ m,Tangram::CollOnDemand,); |
767 |
cvsjoko |
1.1 |
if (ref $self->{$attr} eq "Set::Object") { |
768 |
|
|
$self->{$attr}->clear; |
769 |
|
|
} |
770 |
|
|
delete $self->{$attr}; |
771 |
|
|
} |
772 |
|
|
|
773 |
joko |
1.3 |
=item destroy_hash |
774 |
cvsjoko |
1.1 |
|
775 |
joko |
1.3 |
empties a hash |
776 |
cvsjoko |
1.1 |
|
777 |
|
|
=cut |
778 |
|
|
|
779 |
|
|
sub destroy_hash { |
780 |
joko |
1.3 |
my $self = shift; |
781 |
|
|
my $attr = shift; |
782 |
cvsjoko |
1.1 |
my $t = tied $self->{$attr}; |
783 |
joko |
1.3 |
%{$self->{$attr}} = () |
784 |
|
|
unless (defined $t and $t =~ m,Tangram::CollOnDemand,); |
785 |
cvsjoko |
1.1 |
delete $self->{$attr}; |
786 |
|
|
} |
787 |
|
|
|
788 |
joko |
1.3 |
=item destroy_ref |
789 |
cvsjoko |
1.1 |
|
790 |
joko |
1.3 |
destroys a reference. |
791 |
cvsjoko |
1.1 |
|
792 |
|
|
=cut |
793 |
|
|
|
794 |
|
|
sub destroy_ref { |
795 |
joko |
1.3 |
my $self = shift; |
796 |
|
|
delete $self->{shift}; |
797 |
|
|
} |
798 |
cvsjoko |
1.1 |
|
799 |
joko |
1.3 |
=back |
800 |
cvsjoko |
1.1 |
|
801 |
|
|
=item parse_X ($attribute, { schema option }) |
802 |
|
|
|
803 |
|
|
Parses the schema option field, and returns one or two closures that |
804 |
|
|
act as a check_X and a destroy_X function for the attribute. |
805 |
|
|
|
806 |
|
|
This is currently a very ugly hack, parsing the SQL type definition of |
807 |
|
|
an object. But it was bloody handy in my case for hacking this in |
808 |
joko |
1.3 |
quickly. This is probably unmanagably unportable across databases; |
809 |
|
|
but send me bug reports on it anyway, and I'll try and make the |
810 |
|
|
parsers work for as many databases as possible. |
811 |
|
|
|
812 |
|
|
This perhaps should be replaced by primitives that go the other way, |
813 |
|
|
building the SQL type definition from a more abstract definition of |
814 |
|
|
the type. |
815 |
cvsjoko |
1.1 |
|
816 |
|
|
Available functions: |
817 |
|
|
|
818 |
joko |
1.3 |
=over |
819 |
|
|
|
820 |
|
|
=item parse_string |
821 |
|
|
|
822 |
|
|
parses SQL types of: |
823 |
|
|
|
824 |
|
|
=over |
825 |
cvsjoko |
1.1 |
|
826 |
|
|
=cut |
827 |
|
|
|
828 |
joko |
1.3 |
use vars qw($quoted_part $sql_list); |
829 |
|
|
|
830 |
|
|
$quoted_part = qr/(?: \"([^\"]+)\" | \'([^\']+)\' )/x; |
831 |
|
|
$sql_list = qr/\(\s* |
832 |
|
|
( |
833 |
|
|
$quoted_part |
834 |
|
|
(?:\s*,\s* $quoted_part )* |
835 |
|
|
) \s*\)/x; |
836 |
|
|
|
837 |
cvsjoko |
1.1 |
sub parse_string { |
838 |
|
|
|
839 |
joko |
1.3 |
my $attribute = shift; |
840 |
|
|
my $option = shift; |
841 |
cvsjoko |
1.1 |
|
842 |
|
|
# simple case; return the check_string function. We don't |
843 |
|
|
# need a destructor for a string so don't return one. |
844 |
|
|
if (!$option->{sql}) { |
845 |
|
|
return \&check_string; |
846 |
|
|
} |
847 |
|
|
|
848 |
joko |
1.3 |
=item CHAR(N), VARCHAR(N) |
849 |
cvsjoko |
1.1 |
|
850 |
joko |
1.3 |
closure checks length of string is less than N characters |
851 |
cvsjoko |
1.1 |
|
852 |
|
|
=cut |
853 |
|
|
|
854 |
|
|
if ($option->{sql} =~ m/^\s*(?:var)?char\s*\(\s*(\d+)\s*\)/ix) { |
855 |
|
|
my $max_length = $1; |
856 |
|
|
return sub { |
857 |
|
|
die "string too long for $attribute" |
858 |
|
|
if (length ${$_[0]} > $max_length); |
859 |
|
|
}; |
860 |
|
|
|
861 |
joko |
1.3 |
=item TINYBLOB, BLOB, LONGBLOB |
862 |
cvsjoko |
1.1 |
|
863 |
joko |
1.3 |
checks max. length of string to be 255, 65535 or 16777215 chars |
864 |
|
|
respectively. Also works with "TEXT" instead of "BLOB" |
865 |
cvsjoko |
1.1 |
|
866 |
|
|
=cut |
867 |
|
|
|
868 |
joko |
1.6 |
} elsif ($option->{sql} =~ m/^\s*(tiny|long|medium)? |
869 |
joko |
1.3 |
(?:blob|text)/ix) { |
870 |
cvsjoko |
1.1 |
my $max_length = ($1 ? ($1 eq "tiny"?255:2**24 - 1) |
871 |
|
|
: 2**16 - 1); |
872 |
|
|
return sub { |
873 |
|
|
die "string too long for $attribute" |
874 |
joko |
1.3 |
if (${$_[0]} and length ${$_[0]} > $max_length); |
875 |
cvsjoko |
1.1 |
}; |
876 |
|
|
|
877 |
joko |
1.3 |
=item SET("members", "of", "set") |
878 |
cvsjoko |
1.1 |
|
879 |
joko |
1.3 |
checks that the value passed is valid as a SQL set type, and that all |
880 |
|
|
of the passed values are allowed to be a member of that set. |
881 |
cvsjoko |
1.1 |
|
882 |
|
|
=cut |
883 |
|
|
|
884 |
joko |
1.3 |
} elsif (my ($members) = $option->{sql} =~ |
885 |
|
|
m/^\s*set\s*$sql_list/oi) { |
886 |
|
|
|
887 |
|
|
my %members; |
888 |
|
|
$members{lc($1 || $2)} = 1 |
889 |
|
|
while ( $members =~ m/\G[,\s]*$quoted_part/cog ); |
890 |
|
|
|
891 |
cvsjoko |
1.1 |
return sub { |
892 |
joko |
1.3 |
for my $x (split /\s*,\s*/, ${$_[0]}) { |
893 |
cvsjoko |
1.1 |
croak ("SQL set badly formed or invalid member $x " |
894 |
|
|
." (SET" . join(",", keys %members). ")") |
895 |
|
|
if (not exists $members{lc($x)}); |
896 |
|
|
} |
897 |
|
|
}; |
898 |
|
|
|
899 |
joko |
1.3 |
=item ENUM("possible", "values") |
900 |
cvsjoko |
1.1 |
|
901 |
joko |
1.3 |
checks that the value passed is one of the allowed values. |
902 |
cvsjoko |
1.1 |
|
903 |
|
|
=cut |
904 |
|
|
|
905 |
joko |
1.3 |
} elsif (my ($values) = $option->{sql} =~ |
906 |
|
|
m/^\s*enum\s*$sql_list/oi ) { |
907 |
|
|
|
908 |
|
|
my %values; |
909 |
|
|
$values{lc($1 || $2)} = 1 |
910 |
|
|
while ( $values =~ m/\G[,\s]*$quoted_part/gc); |
911 |
|
|
|
912 |
cvsjoko |
1.1 |
return sub { |
913 |
|
|
croak ("invalid enum value ${$_[0]} must be (" |
914 |
|
|
. join(",", keys %values). ")") |
915 |
|
|
if (not exists $values{lc(${$_[0]})}); |
916 |
|
|
} |
917 |
|
|
|
918 |
joko |
1.3 |
|
919 |
cvsjoko |
1.1 |
} else { |
920 |
|
|
die ("Please build support for your string SQL type in " |
921 |
|
|
."Class::Tangram (".$option->{sql}.")"); |
922 |
|
|
} |
923 |
|
|
} |
924 |
|
|
|
925 |
joko |
1.3 |
=back |
926 |
cvsjoko |
1.1 |
|
927 |
joko |
1.3 |
=back |
928 |
cvsjoko |
1.1 |
|
929 |
joko |
1.3 |
=back |
930 |
cvsjoko |
1.1 |
|
931 |
joko |
1.3 |
=head2 Quick Object Dumping and Destruction |
932 |
cvsjoko |
1.1 |
|
933 |
joko |
1.3 |
=over |
934 |
cvsjoko |
1.1 |
|
935 |
|
|
=item $instance->quickdump |
936 |
|
|
|
937 |
|
|
Quickly show the blessed hash of an object, without descending into |
938 |
|
|
it. Primarily useful when you have a large interconnected graph of |
939 |
|
|
objects so don't want to use the B<x> command within the debugger. |
940 |
|
|
It also doesn't have the side effect of auto-vivifying members. |
941 |
|
|
|
942 |
|
|
This function returns a string, suitable for print()ing. It does not |
943 |
|
|
currently escape unprintable characters. |
944 |
|
|
|
945 |
|
|
=cut |
946 |
|
|
|
947 |
|
|
sub quickdump($) { |
948 |
joko |
1.3 |
my $self = shift; |
949 |
cvsjoko |
1.1 |
|
950 |
|
|
my $r = "REF ". (ref $self). "\n"; |
951 |
|
|
for my $k (sort keys %$self) { |
952 |
|
|
$r .= (" $k => " |
953 |
|
|
. ( |
954 |
|
|
tied $self->{$k} |
955 |
|
|
|| ( ref $self->{$k} |
956 |
|
|
? $self->{$k} |
957 |
|
|
: "'".$self->{$k}."'" ) |
958 |
|
|
) |
959 |
|
|
. "\n"); |
960 |
|
|
} |
961 |
|
|
return $r; |
962 |
|
|
} |
963 |
|
|
|
964 |
|
|
|
965 |
|
|
=item $instance->DESTROY |
966 |
|
|
|
967 |
|
|
This function ensures that all of your attributes have their |
968 |
|
|
destructors called. It calls the destroy_X function for attributes |
969 |
|
|
that have it defined, if that attribute exists in the instance that we |
970 |
|
|
are destroying. It calls the destroy_X functions as destroy_X($self, |
971 |
|
|
$k) |
972 |
|
|
|
973 |
|
|
=cut |
974 |
|
|
|
975 |
|
|
sub DESTROY($) { |
976 |
joko |
1.3 |
my $self = shift; |
977 |
cvsjoko |
1.1 |
|
978 |
|
|
my $class = ref $self; |
979 |
|
|
|
980 |
|
|
# if no cleaners are known for this class, it hasn't been imported |
981 |
|
|
# yet. Don't call import_schema, that would be a bad idea in a |
982 |
|
|
# destructor. |
983 |
|
|
exists $cleaners{$class} or return; |
984 |
|
|
|
985 |
|
|
# for every attribute that is defined, and has a cleaner function, |
986 |
|
|
# call the cleaner function. |
987 |
|
|
for my $k (keys %$self) { |
988 |
|
|
if (defined $cleaners{$class}->{$k} and exists $self->{$k}) { |
989 |
|
|
$cleaners{$class}->{$k}->($self, $k); |
990 |
|
|
} |
991 |
|
|
} |
992 |
|
|
$self->{_DESTROYED} = 1; |
993 |
|
|
} |
994 |
|
|
|
995 |
|
|
=item $instance->clear_refs |
996 |
|
|
|
997 |
|
|
This clears all references from this object, ie exactly what DESTROY |
998 |
|
|
normally does, but calling an object's destructor method directly is |
999 |
|
|
bad form. Also, this function has no qualms with loading the class' |
1000 |
|
|
schema with import_schema() as needed. |
1001 |
|
|
|
1002 |
|
|
This is useful for breaking circular references, if you know you are |
1003 |
|
|
no longer going to be using an object then you can call this method, |
1004 |
|
|
which in many cases will end up cleaning up most of the objects you |
1005 |
|
|
want to get rid of. |
1006 |
|
|
|
1007 |
|
|
However, it still won't do anything about Tangram's internal reference |
1008 |
|
|
to the object, which must still be explicitly unlinked with the |
1009 |
|
|
Tangram::Storage->unload method. |
1010 |
|
|
|
1011 |
|
|
=cut |
1012 |
|
|
|
1013 |
|
|
sub clear_refs($) { |
1014 |
joko |
1.3 |
my $self = shift; |
1015 |
cvsjoko |
1.1 |
my $class = ref $self; |
1016 |
|
|
|
1017 |
|
|
exists $cleaners{$class} or import_schema($class); |
1018 |
|
|
|
1019 |
|
|
# break all ref's, sets, arrays |
1020 |
|
|
for my $k (keys %$self) { |
1021 |
|
|
if (defined $cleaners{$class}->{$k} and exists $self->{$k}) { |
1022 |
|
|
$cleaners{$class}->{$k}->($self, $k); |
1023 |
|
|
} |
1024 |
|
|
} |
1025 |
|
|
$self->{_NOREFS} = 1; |
1026 |
|
|
} |
1027 |
|
|
|
1028 |
joko |
1.3 |
=back |
1029 |
|
|
|
1030 |
|
|
=head1 FUNCTIONS |
1031 |
|
|
|
1032 |
|
|
The following functions are not intended to be called as object |
1033 |
|
|
methods. |
1034 |
|
|
|
1035 |
|
|
=head2 Schema Import |
1036 |
|
|
|
1037 |
|
|
our $fields = { int => [ qw(foo bar) ], |
1038 |
|
|
string => [ qw(baz quux) ] }; |
1039 |
|
|
|
1040 |
|
|
# Version 1.115 and below compatibility: |
1041 |
|
|
our $schema = { |
1042 |
|
|
fields => { int => [ qw(foo bar) ], |
1043 |
|
|
string => [ qw(baz quux) ] } |
1044 |
|
|
}; |
1045 |
|
|
|
1046 |
|
|
=over |
1047 |
|
|
|
1048 |
|
|
=item Class::Tangram::import_schema($class) |
1049 |
|
|
|
1050 |
|
|
Parses a tangram object field list, in C<${"${class}::fields"}> (or |
1051 |
|
|
C<${"${class}::schema"}-E<gt>{fields}> to the internal type information |
1052 |
|
|
hashes. It will also define all of the attribute accessor and update |
1053 |
|
|
methods in the C<$class> package. |
1054 |
|
|
|
1055 |
|
|
Note that calling this function twice for the same class is not |
1056 |
|
|
tested and may produce arbitrary results. Patches welcome. |
1057 |
|
|
|
1058 |
|
|
=cut |
1059 |
|
|
|
1060 |
|
|
sub import_schema($) { # Damn this function is long |
1061 |
|
|
my $class = shift; |
1062 |
|
|
|
1063 |
|
|
eval { |
1064 |
|
|
my ($fields, $bases, $abstract); |
1065 |
|
|
{ |
1066 |
|
|
|
1067 |
|
|
# Here, we go hunting around for their defined schema and |
1068 |
|
|
# options |
1069 |
|
|
no strict 'refs'; |
1070 |
|
|
local $^W=0; |
1071 |
|
|
eval { |
1072 |
|
|
$fields = (${"${class}::fields"} || |
1073 |
|
|
${"${class}::schema"}->{fields}); |
1074 |
|
|
$abstract = (${"${class}::abstract"} || |
1075 |
|
|
${"${class}::schema"}->{abstract}); |
1076 |
|
|
$bases = ${"${class}::schema"}->{bases}; |
1077 |
|
|
}; |
1078 |
|
|
if ( my @stack = @{"${class}::ISA"}) { |
1079 |
|
|
# clean "bases" information from @ISA |
1080 |
|
|
my %seen = map { $_ => 1 } $class, __PACKAGE__; |
1081 |
joko |
1.4 |
#$bases = []; # will anything break without this? it's needed for recording inheritances later on |
1082 |
joko |
1.3 |
while ( my $super = pop @stack ) { |
1083 |
|
|
if ( defined ${"${super}::schema"} |
1084 |
|
|
or defined ${"${super}::fields"} ) { |
1085 |
|
|
push @$bases, $super; |
1086 |
|
|
} else { |
1087 |
|
|
push @stack, grep { !$seen{$_}++ } |
1088 |
|
|
@{"${super}::ISA"}; |
1089 |
|
|
} |
1090 |
|
|
} |
1091 |
|
|
if ( !$fields and !@$bases ) { |
1092 |
|
|
die ("No schema and no Class::Tangram " |
1093 |
|
|
."superclass for $class; define " |
1094 |
|
|
."${class}::fields!"); |
1095 |
|
|
} |
1096 |
|
|
} |
1097 |
|
|
} |
1098 |
|
|
|
1099 |
|
|
# if this is an abstract type, do not allow it to be |
1100 |
|
|
# instantiated |
1101 |
|
|
if ($abstract) { |
1102 |
|
|
$abstract{$class} = 1; |
1103 |
|
|
} |
1104 |
|
|
|
1105 |
|
|
# If there are any base classes, import them first so that the |
1106 |
|
|
# check, cleaners and init_defaults can be inherited |
1107 |
|
|
if (defined $bases) { |
1108 |
|
|
(ref $bases eq "ARRAY") |
1109 |
|
|
or die "bases not an array ref for $class"; |
1110 |
|
|
|
1111 |
joko |
1.4 |
# record bases of current class for later retrieval via Run-time type information |
1112 |
|
|
$bases{$class} = $bases; |
1113 |
|
|
|
1114 |
joko |
1.3 |
# Note that the order of your bases is significant, that |
1115 |
|
|
# is if you are using multiple iheritance then the later |
1116 |
|
|
# classes override the earlier ones. |
1117 |
|
|
for my $super ( @$bases ) { |
1118 |
|
|
import_schema $super unless (exists $check{$super}); |
1119 |
|
|
|
1120 |
|
|
# copy each of the per-class configuration hashes to |
1121 |
|
|
# this class as defaults. |
1122 |
|
|
my ($k, $v); |
1123 |
|
|
|
1124 |
|
|
# FIXME - this repetition of code is getting silly :) |
1125 |
|
|
$types{$class}->{$k} = $v |
1126 |
|
|
while (($k, $v) = each %{ $types{$super} } ); |
1127 |
|
|
$check{$class}->{$k} = $v |
1128 |
|
|
while (($k, $v) = each %{ $check{$super} } ); |
1129 |
|
|
$cleaners{$class}->{$k} = $v |
1130 |
|
|
while (($k, $v) = each %{ $cleaners{$super} } ); |
1131 |
|
|
$attribute_options{$class}->{$k} = $v |
1132 |
|
|
while (($k, $v) = each %{ $attribute_options{$super} } ); |
1133 |
|
|
$init_defaults{$class}->{$k} = $v |
1134 |
|
|
while (($k, $v) = each %{ $init_defaults{$super} } ); |
1135 |
|
|
$required_attributes{$class}->{$k} = $v |
1136 |
|
|
while (($k, $v) = each %{ $required_attributes{$super} } ); |
1137 |
|
|
} |
1138 |
|
|
} |
1139 |
|
|
|
1140 |
|
|
# iterate over each of the *types* of fields (string, int, ref, etc.) |
1141 |
|
|
while (my ($type, $v) = each %$fields) { |
1142 |
|
|
if (ref $v eq "ARRAY") { |
1143 |
|
|
$v = { map { $_, undef } @$v }; |
1144 |
|
|
} |
1145 |
|
|
my $def = $defaults{$type}; |
1146 |
|
|
|
1147 |
|
|
# iterate each of the *attributes* of a particular type |
1148 |
|
|
while (my ($attribute, $options) = each %$v) { |
1149 |
|
|
|
1150 |
|
|
# this is what we are finding out about each attribute |
1151 |
|
|
# $type is already set |
1152 |
|
|
my ($default, $check_func, $required, $cleaner); |
1153 |
|
|
# set defaults from what they give |
1154 |
|
|
$options ||= {}; |
1155 |
|
|
if (ref $options eq "HASH" or |
1156 |
|
|
UNIVERSAL::isa($options, 'Tangram::Type')) { |
1157 |
|
|
($check_func, $default, $required, $cleaner) |
1158 |
|
|
= @{$options}{qw(check_func init_default |
1159 |
|
|
required destroy_func)}; |
1160 |
|
|
} |
1161 |
|
|
|
1162 |
|
|
# Fill their settings with info from defaults |
1163 |
|
|
if (ref $def eq "HASH") { |
1164 |
|
|
|
1165 |
|
|
# try to magically parse their options |
1166 |
|
|
if ( $def->{parse} and !($check_func and $cleaner) ) { |
1167 |
|
|
my @a = $def->{parse}->($attribute, $options); |
1168 |
|
|
$check_func ||= $a[0]; |
1169 |
|
|
$cleaner ||= $a[1]; |
1170 |
|
|
} |
1171 |
|
|
|
1172 |
|
|
# fall back to defaults for this class |
1173 |
|
|
$check_func ||= $def->{check_func}; |
1174 |
|
|
$cleaner ||= $def->{destroy_func}; |
1175 |
|
|
$default = $def->{init_default} unless defined $default; |
1176 |
|
|
} |
1177 |
|
|
|
1178 |
|
|
# everything must be checked! |
1179 |
|
|
die "No check function for ${class}\->$attribute (type $type)" |
1180 |
|
|
unless (ref $check_func eq "CODE"); |
1181 |
|
|
|
1182 |
|
|
$types{$class}->{$attribute} = $type; |
1183 |
|
|
$check{$class}->{$attribute} = $check_func; |
1184 |
|
|
{ |
1185 |
|
|
no strict "refs"; |
1186 |
|
|
local ($^W) = 0; |
1187 |
|
|
|
1188 |
|
|
# build an appropriate "get_attribute" method, and |
1189 |
|
|
# define other per-type methods |
1190 |
|
|
my ($get_closure, $set_closure); |
1191 |
|
|
|
1192 |
|
|
# implement with closures for speed |
1193 |
|
|
if ( $type =~ m/i?set/ ) { |
1194 |
|
|
|
1195 |
|
|
# GET_$attribute (Set::Object) |
1196 |
|
|
$get_closure = sub { |
1197 |
|
|
my $self = shift; |
1198 |
|
|
if ( !defined $self->{$attribute} ) { |
1199 |
|
|
$self->{$attribute} = Set::Object->new(); |
1200 |
|
|
} |
1201 |
|
|
my $set = $self->{$attribute}; |
1202 |
|
|
( wantarray ? $set->members : $set ) |
1203 |
|
|
}; |
1204 |
|
|
|
1205 |
|
|
# and add a whole load of other functions too |
1206 |
|
|
for my $set_method (qw(includes insert size clear |
1207 |
|
|
remove)) { |
1208 |
|
|
|
1209 |
|
|
# ${attribute}_includes, etc |
1210 |
|
|
my $set_method_closure = sub { |
1211 |
|
|
my $self = shift; |
1212 |
|
|
$self->{$attribute} = Set::Object->new() |
1213 |
|
|
unless defined $self->{$attribute}; |
1214 |
|
|
return $self->{$attribute}->$set_method(@_); |
1215 |
|
|
}; |
1216 |
|
|
*{$class."::${attribute}_$set_method"} = |
1217 |
|
|
$set_method_closure unless |
1218 |
|
|
(defined &{$class."::${attribute}_$set_method"}); |
1219 |
|
|
} |
1220 |
|
|
|
1221 |
|
|
} elsif ( $type =~ m/i?array/ ) { |
1222 |
|
|
|
1223 |
|
|
# GET_$attribute (array) |
1224 |
|
|
# allow array slices, and return whole array |
1225 |
|
|
# in list context |
1226 |
|
|
$get_closure = sub { |
1227 |
|
|
my $array = ($_[0]->{$attribute} ||= []); |
1228 |
|
|
shift; |
1229 |
|
|
if ( @_ ) { |
1230 |
|
|
@{$array}[@_]; |
1231 |
|
|
} else { |
1232 |
|
|
( wantarray ? @{ $array } : $array ) |
1233 |
|
|
} |
1234 |
|
|
}; |
1235 |
|
|
|
1236 |
|
|
} elsif ( $type =~ m/i?hash/ ) { |
1237 |
|
|
# GET_$attribute (hash) |
1238 |
|
|
# allow hash slices, and return whole hash in |
1239 |
|
|
# list context |
1240 |
|
|
$get_closure = sub { |
1241 |
|
|
my $hash = ($_[0]->{$attribute} ||= {}); |
1242 |
|
|
shift; |
1243 |
|
|
if ( @_ ) { |
1244 |
|
|
@{$hash}{@_} |
1245 |
|
|
} else { |
1246 |
|
|
( wantarray ? %{ $hash } : $hash ); |
1247 |
|
|
} |
1248 |
|
|
}; |
1249 |
|
|
} else { |
1250 |
|
|
# GET_$attribute (scalar) |
1251 |
|
|
# return value only |
1252 |
|
|
$get_closure = sub { $_[0]->{$attribute}; }; |
1253 |
|
|
} |
1254 |
|
|
|
1255 |
|
|
*{$class."::get_$attribute"} = $get_closure |
1256 |
|
|
unless (defined &{$class."::get_$attribute"}); |
1257 |
|
|
|
1258 |
|
|
# SET_$attribute (all) |
1259 |
|
|
my $checkit = \$check{$class}->{$attribute}; |
1260 |
|
|
|
1261 |
|
|
# required hack for strings - duplicate the code |
1262 |
|
|
# to avoid the following string comparison for |
1263 |
|
|
# every set |
1264 |
|
|
if ( $type eq "string" ) { |
1265 |
|
|
$set_closure = sub { |
1266 |
|
|
my $self = shift; |
1267 |
|
|
my $value = shift; |
1268 |
|
|
eval { |
1269 |
|
|
if ( defined $value and length $value ) { |
1270 |
|
|
${$checkit}->(\$value); |
1271 |
|
|
} elsif ( $required ) { |
1272 |
|
|
die "value is required" |
1273 |
|
|
} elsif ( defined $required ) { |
1274 |
|
|
die "value must be defined" |
1275 |
|
|
unless defined $value; |
1276 |
|
|
} |
1277 |
|
|
}; |
1278 |
|
|
$@ && croak("value failed type check - ${class}->" |
1279 |
|
|
."set_$attribute('$value') ($@)"); |
1280 |
|
|
$self->{$attribute} = $value; |
1281 |
|
|
}; |
1282 |
|
|
} else { |
1283 |
|
|
$set_closure = sub { |
1284 |
|
|
my $self = shift; |
1285 |
|
|
my $value = shift; |
1286 |
|
|
eval { |
1287 |
|
|
if ( $value ) { |
1288 |
|
|
${$checkit}->(\$value); |
1289 |
|
|
} elsif ( $required ) { |
1290 |
|
|
die "value is required" |
1291 |
|
|
} elsif ( defined $required ) { |
1292 |
|
|
die "value must be defined" |
1293 |
|
|
unless defined $value; |
1294 |
|
|
} |
1295 |
|
|
}; |
1296 |
|
|
$@ && croak("value failed type check - ${class}->" |
1297 |
|
|
."set_$attribute('$value') ($@)"); |
1298 |
|
|
$self->{$attribute} = $value; |
1299 |
|
|
}; |
1300 |
|
|
} |
1301 |
|
|
|
1302 |
|
|
# now export them into the caller's namespace |
1303 |
|
|
my ($getter, $setter) |
1304 |
|
|
= ("get_$attribute", "set_$attribute"); |
1305 |
|
|
*{$class."::$getter"} = $get_closure |
1306 |
|
|
unless defined &{$class."::$getter"}; |
1307 |
|
|
*{$class."::$setter"} = $set_closure |
1308 |
|
|
unless defined &{$class."::$setter"}; |
1309 |
|
|
|
1310 |
|
|
*{$class."::$attribute"} = sub { |
1311 |
|
|
my $self = shift; |
1312 |
|
|
if ( @_ ) { |
1313 |
|
|
warn("The OO Police say change your call " |
1314 |
|
|
."to ->set_$attribute") if ($^W); |
1315 |
|
|
#goto $set_closure; # NO! BAD!! :-) |
1316 |
|
|
return $self->$setter(@_); |
1317 |
|
|
} else { |
1318 |
|
|
return $self->$getter(@_); |
1319 |
|
|
#goto $get_closure; |
1320 |
|
|
} |
1321 |
|
|
} unless defined &{$class."::$attribute"}; |
1322 |
|
|
} |
1323 |
|
|
|
1324 |
|
|
$cleaners{$class}->{$attribute} = $cleaner |
1325 |
|
|
if (defined $cleaner); |
1326 |
|
|
$init_defaults{$class}->{$attribute} = $default |
1327 |
|
|
if (defined $default); |
1328 |
|
|
$required_attributes{$class}->{$attribute} = $required |
1329 |
|
|
if (defined $required); |
1330 |
|
|
$attribute_options{$class}->{$attribute} = |
1331 |
|
|
( $options || {} ); |
1332 |
|
|
} |
1333 |
|
|
} |
1334 |
|
|
}; |
1335 |
|
|
|
1336 |
|
|
$@ && die "$@ while trying to import schema for $class"; |
1337 |
|
|
} |
1338 |
|
|
|
1339 |
|
|
=back |
1340 |
|
|
|
1341 |
|
|
=head2 Run-time type information |
1342 |
|
|
|
1343 |
|
|
It is possible to access the data structures that Class::Tangram uses |
1344 |
|
|
internally to verify attributes, create objects and so on. |
1345 |
|
|
|
1346 |
|
|
This should be considered a B<HIGHLY EXPERIMENTAL> interface to |
1347 |
|
|
B<INTERNALS> of Class::Tangram. |
1348 |
|
|
|
1349 |
|
|
Class::Tangram keeps seven internal hashes: |
1350 |
|
|
|
1351 |
|
|
=over |
1352 |
|
|
|
1353 |
|
|
=item C<%types> |
1354 |
|
|
|
1355 |
|
|
C<$types{$class}-E<gt>{$attribute}> is the tangram type of each attribute, |
1356 |
|
|
ie "ref", "iset", etc. See L<Tangram::Type>. |
1357 |
|
|
|
1358 |
|
|
=item C<%attribute_options> |
1359 |
|
|
|
1360 |
|
|
C<$attribute_options{$class}-E<gt>{$attribute}> is the options hash |
1361 |
|
|
for a given attribute. |
1362 |
|
|
|
1363 |
|
|
=item C<%required_attributes> |
1364 |
|
|
|
1365 |
|
|
C<$required_attributes{$class}-E<gt>{$attribute}> is the 'required' |
1366 |
|
|
option setting for a given attribute. |
1367 |
|
|
|
1368 |
|
|
=item C<%check> |
1369 |
|
|
|
1370 |
|
|
C<$check{$class}-E<gt>{$attribute}> is a function that will be passed |
1371 |
|
|
a reference to the value to be checked and either throw an exception |
1372 |
|
|
(die) or return true. |
1373 |
|
|
|
1374 |
|
|
=item C<%cleaners> |
1375 |
|
|
|
1376 |
|
|
C<$attribute_options{$class}-E<gt>{$attribute}> is a reference to a |
1377 |
|
|
destructor function for that attribute. It is called as an object |
1378 |
|
|
method on the object being destroyed, and should ensure that any |
1379 |
|
|
circular references that this object is involved in get cleared. |
1380 |
|
|
|
1381 |
|
|
=item C<%abstract> |
1382 |
|
|
|
1383 |
|
|
C<$abstract-E<gt>{$class}> is set if the class is abstract |
1384 |
|
|
|
1385 |
|
|
=item C<%init_defaults> |
1386 |
|
|
|
1387 |
|
|
C<$init_defaults{$class}-E<gt>{$attribute}> represents what an |
1388 |
|
|
attribute is set to automatically if it is not specified when an |
1389 |
|
|
object is created. If this is a scalar value, the attribute is set to |
1390 |
|
|
the value. If it is a function, then that function is called (as a |
1391 |
|
|
method) and should return the value to be placed into that attribute. |
1392 |
|
|
If it is a hash ref or an array ref, then that structure is COPIED in |
1393 |
|
|
to the new object. If you don't want that, you can do something like |
1394 |
|
|
this: |
1395 |
|
|
|
1396 |
|
|
[...] |
1397 |
|
|
flat_hash => { |
1398 |
|
|
attribute => { |
1399 |
|
|
init_default => sub { { key => "value" } }, |
1400 |
|
|
}, |
1401 |
|
|
}, |
1402 |
|
|
[...] |
1403 |
|
|
|
1404 |
|
|
Now, every new object will share the same hash for that attribute. |
1405 |
cvsjoko |
1.1 |
|
1406 |
|
|
=back |
1407 |
|
|
|
1408 |
joko |
1.3 |
There are currently four functions that allow you to access parts of |
1409 |
|
|
this information. |
1410 |
|
|
|
1411 |
|
|
=over |
1412 |
|
|
|
1413 |
|
|
=item Class::Tangram::attribute_options($class) |
1414 |
|
|
|
1415 |
|
|
Returns a hash ref to a data structure from attribute names to the |
1416 |
|
|
option hash for that attribute. |
1417 |
|
|
|
1418 |
|
|
=cut |
1419 |
|
|
|
1420 |
|
|
sub attribute_options($) { |
1421 |
|
|
my $class = shift; |
1422 |
|
|
return $attribute_options{$class}; |
1423 |
|
|
} |
1424 |
|
|
|
1425 |
|
|
=item Class::Tangram::attribute_types($class) |
1426 |
|
|
|
1427 |
|
|
Returns a hash ref from attribute names to the tangram type for that |
1428 |
|
|
attribute. |
1429 |
|
|
|
1430 |
|
|
=cut |
1431 |
|
|
|
1432 |
|
|
sub attribute_types($) { |
1433 |
|
|
my $class = shift; |
1434 |
|
|
return $types{$class}; |
1435 |
|
|
} |
1436 |
|
|
|
1437 |
|
|
=item Class::Tangram::required_attributes($class) |
1438 |
|
|
|
1439 |
|
|
Returns a hash ref from attribute names to the 'required' option setting for |
1440 |
|
|
that attribute. May also be called as a method, as in |
1441 |
|
|
C<$instance-E<gt>required_attributes>. |
1442 |
|
|
|
1443 |
|
|
=cut |
1444 |
|
|
|
1445 |
|
|
sub required_attributes($) { |
1446 |
|
|
my $class = ref $_[0] || $_[0]; |
1447 |
|
|
return $required_attributes{$class}; |
1448 |
|
|
} |
1449 |
|
|
|
1450 |
|
|
=item Class::Tangram::init_defaults($class) |
1451 |
|
|
|
1452 |
|
|
Returns a hash ref from attribute names to the default intial values for |
1453 |
|
|
that attribute. May also be called as a method, as in |
1454 |
|
|
C<$instance-E<gt>init_defaults>. |
1455 |
|
|
|
1456 |
|
|
=cut |
1457 |
|
|
|
1458 |
|
|
sub init_defaults($) { |
1459 |
|
|
my $class = ref $_[0] || $_[0]; |
1460 |
|
|
return $init_defaults{$class}; |
1461 |
|
|
} |
1462 |
|
|
|
1463 |
|
|
=item Class::Tangram::known_classes |
1464 |
|
|
|
1465 |
|
|
This function returns a list of all the classes that have had their |
1466 |
|
|
object schema imported by Class::Tangram. |
1467 |
|
|
|
1468 |
|
|
=cut |
1469 |
|
|
|
1470 |
|
|
sub known_classes { |
1471 |
|
|
return keys %types; |
1472 |
|
|
} |
1473 |
|
|
|
1474 |
|
|
=item Class::Tangram::is_abstract($class) |
1475 |
|
|
|
1476 |
|
|
This function returns true if the supplied class is abstract. |
1477 |
|
|
|
1478 |
|
|
=cut |
1479 |
|
|
|
1480 |
|
|
sub is_abstract { |
1481 |
|
|
my $class = shift; |
1482 |
|
|
$class eq "Class::Tangram" && ($class = shift); |
1483 |
|
|
|
1484 |
|
|
exists $cleaners{$class} or import_schema($class); |
1485 |
|
|
} |
1486 |
|
|
|
1487 |
joko |
1.4 |
=item Class::Tangram::class_bases($class) |
1488 |
|
|
|
1489 |
|
|
Returns an array ref of class names the given class inherits from. |
1490 |
|
|
|
1491 |
|
|
=cut |
1492 |
|
|
|
1493 |
|
|
sub class_bases($) { |
1494 |
|
|
my $class = shift; |
1495 |
|
|
return $bases{$class}; |
1496 |
|
|
} |
1497 |
|
|
|
1498 |
jonen |
1.5 |
=item Class::Tangram::class_is_abstract($class) |
1499 |
|
|
|
1500 |
|
|
Returns a bool value if the given class is abstract. |
1501 |
|
|
|
1502 |
|
|
=cut |
1503 |
|
|
|
1504 |
|
|
sub class_is_abstract($) { |
1505 |
|
|
my $class = shift; |
1506 |
|
|
return exists $abstract{$class}; |
1507 |
|
|
} |
1508 |
|
|
|
1509 |
joko |
1.3 |
=item Class->set_init_default(attribute => $value); |
1510 |
|
|
|
1511 |
|
|
Sets the default value on an attribute for newly created "Class" |
1512 |
|
|
objects, as if it had been declared with init_default. Can be called |
1513 |
|
|
as a class or an instance method. |
1514 |
|
|
|
1515 |
|
|
=cut |
1516 |
|
|
|
1517 |
|
|
sub set_init_default { |
1518 |
|
|
my $invocant = shift; |
1519 |
|
|
my $class = ref $invocant || $invocant; |
1520 |
|
|
|
1521 |
|
|
exists $init_defaults{$class} or import_schema($class); |
1522 |
|
|
|
1523 |
|
|
while ( my ($attribute, $value) = splice @_, 0, 2) { |
1524 |
|
|
$init_defaults{$class}->{$attribute} = $value; |
1525 |
|
|
} |
1526 |
|
|
} |
1527 |
|
|
|
1528 |
|
|
=back |
1529 |
|
|
|
1530 |
|
|
=cut |
1531 |
|
|
|
1532 |
|
|
# a little embedded package |
1533 |
|
|
|
1534 |
|
|
package Tangram::Transient; |
1535 |
|
|
|
1536 |
|
|
BEGIN { |
1537 |
|
|
eval "use base qw(Tangram::Type)"; |
1538 |
|
|
if ( $@ ) { |
1539 |
|
|
# no tangram |
1540 |
|
|
} else { |
1541 |
|
|
$Tangram::Schema::TYPES{transient} = bless {}, __PACKAGE__; |
1542 |
|
|
} |
1543 |
|
|
} |
1544 |
|
|
|
1545 |
|
|
sub coldefs { } |
1546 |
|
|
|
1547 |
|
|
sub get_exporter { } |
1548 |
|
|
sub get_importer { } |
1549 |
|
|
|
1550 |
|
|
sub get_import_cols { |
1551 |
|
|
# print "Get_import_cols:" , Dumper \@_; |
1552 |
|
|
return (); |
1553 |
|
|
} |
1554 |
|
|
|
1555 |
cvsjoko |
1.1 |
=head1 SEE ALSO |
1556 |
|
|
|
1557 |
|
|
L<Tangram::Schema> |
1558 |
|
|
|
1559 |
|
|
B<A guided tour of Tangram, by Sound Object Logic.> |
1560 |
|
|
|
1561 |
|
|
http://www.soundobjectlogic.com/tangram/guided_tour/fs.html |
1562 |
|
|
|
1563 |
joko |
1.3 |
=head1 DEPENDENCIES |
1564 |
|
|
|
1565 |
|
|
The following modules are required to be installed to use |
1566 |
|
|
Class::Tangram: |
1567 |
|
|
|
1568 |
|
|
Set::Object => 1.02 |
1569 |
|
|
Pod::Constants => 0.11 |
1570 |
|
|
Test::Simple => 0.18 |
1571 |
|
|
Date::Manip => 5.21 |
1572 |
|
|
|
1573 |
|
|
Test::Simple and Date::Manip are only required to run the test suite. |
1574 |
|
|
|
1575 |
|
|
If you find Class::Tangram passes the test suite with earlier versions |
1576 |
|
|
of the above modules, please send me an e-mail. |
1577 |
|
|
|
1578 |
|
|
=head2 MODULE RELEASE |
1579 |
|
|
|
1580 |
joko |
1.4 |
This is Class::Tangram version 1.13. |
1581 |
joko |
1.3 |
|
1582 |
cvsjoko |
1.1 |
=head1 BUGS/TODO |
1583 |
|
|
|
1584 |
joko |
1.3 |
There should be more functions for breaking loops; in particular, a |
1585 |
|
|
standard function called C<drop_refs($obj)>, which replaces references |
1586 |
|
|
to $obj with the appropriate C<Tangram::RefOnDemand> object so that an |
1587 |
|
|
object can be unloaded via C<Tangram::Storage->unload()> and actually |
1588 |
|
|
have a hope of being reclaimed. Another function that would be handy |
1589 |
|
|
would be a deep "mark" operation for manual mark & sweep garbage |
1590 |
|
|
collection. |
1591 |
|
|
|
1592 |
|
|
Need to think about writing some functions using C<Inline> for speed. |
1593 |
|
|
One of these days... |
1594 |
cvsjoko |
1.1 |
|
1595 |
joko |
1.3 |
Allow C<init_default> values to be set in a default import function? |
1596 |
cvsjoko |
1.1 |
|
1597 |
joko |
1.3 |
ie |
1598 |
cvsjoko |
1.1 |
|
1599 |
joko |
1.3 |
use MyClassTangramObject -defaults => { foo => "bar" }; |
1600 |
cvsjoko |
1.1 |
|
1601 |
|
|
=head1 AUTHOR |
1602 |
|
|
|
1603 |
|
|
Sam Vilain, <sam@vilain.net> |
1604 |
|
|
|
1605 |
joko |
1.3 |
=head2 CREDITS |
1606 |
|
|
|
1607 |
|
|
# Some modifications |
1608 |
|
|
# Copyright © 2001 Micro Sharp Technologies, Inc., Vancouver, WA, USA |
1609 |
|
|
# Author: Karl M. Hegbloom <karlheg@microsharp.com> |
1610 |
|
|
# Perl Artistic Licence. |
1611 |
|
|
|
1612 |
|
|
Many thanks to Charles Owens and David Wheeler for their feedback, |
1613 |
|
|
ideas, patches and bug testing. |
1614 |
|
|
|
1615 |
cvsjoko |
1.1 |
=cut |
1616 |
|
|
|
1617 |
|
|
69; |
1618 |
joko |
1.3 |
|
1619 |
|
|
__END__ |
1620 |
|
|
|
1621 |
|
|
# From old SYNOPSIS, I decided it was too long. A lot of |
1622 |
|
|
# the information here needs to be re-integrated into the |
1623 |
|
|
# POD. |
1624 |
|
|
|
1625 |
|
|
package Project; |
1626 |
|
|
|
1627 |
|
|
# here's where we build the individual object schemas into |
1628 |
|
|
# a Tangram::Schema object, which the Tangram::Storage |
1629 |
|
|
# class uses to know which tables and columns to find |
1630 |
|
|
# objects. |
1631 |
|
|
use Tangram::Schema; |
1632 |
|
|
|
1633 |
|
|
# TIMTOWTDI - this is the condensed manpage version :) |
1634 |
|
|
my $dbschema = Tangram::Schema->new |
1635 |
|
|
({ classes => |
1636 |
|
|
[ 'Orange' => { fields => $Orange::fields }, |
1637 |
|
|
'MyObject' => { fields => $MyObject::schema }, ]}); |
1638 |
|
|
|
1639 |
|
|
sub schema { $dbschema }; |
1640 |
|
|
|
1641 |
|
|
package main; |
1642 |
|
|
|
1643 |
|
|
# See Tangram::Relational for instructions on using |
1644 |
|
|
# "deploy" to create the database this connects to. You |
1645 |
|
|
# only have to do this if you want to write the objects to |
1646 |
|
|
# a database. |
1647 |
|
|
use Tangram::Relational; |
1648 |
|
|
my ($dsn, $u, $p); |
1649 |
|
|
my $storage = Tangram::Relational->connect |
1650 |
|
|
(Project->schema, $dsn, $u, $p); |
1651 |
|
|
|
1652 |
|
|
# Create an orange |
1653 |
|
|
my $orange = Orange->new( |
1654 |
|
|
juiciness => 8, |
1655 |
|
|
type => 'Florida', |
1656 |
|
|
tag => '', # required |
1657 |
|
|
); |
1658 |
|
|
|
1659 |
|
|
# Store it |
1660 |
|
|
$storage->insert($orange); |
1661 |
|
|
|
1662 |
|
|
# This is how you get values out of the objects |
1663 |
|
|
my $juiciness = $orange->juiciness; |
1664 |
|
|
|
1665 |
|
|
# a "ref" must be set to a blessed object, any object |
1666 |
|
|
my $grower = bless { name => "Joe" }, "Farmer"; |
1667 |
|
|
$orange->set_grower ($grower); |
1668 |
|
|
|
1669 |
|
|
# these are all illegal - type checking is fairly strict |
1670 |
|
|
my $orange = eval { Orange->new; }; print $@; |
1671 |
|
|
eval { $orange->set_juiciness ("Yum"); }; print $@; |
1672 |
|
|
eval { $orange->set_segments (31); }; print $@; |
1673 |
|
|
eval { $orange->set_grower ("Mr. Nice"); }; print $@; |
1674 |
|
|
|
1675 |
|
|
# Demonstrate some "required" functionality |
1676 |
|
|
eval { $orange->set_type (''); }; print $@; |
1677 |
|
|
eval { $orange->set_type (undef); }; print $@; |
1678 |
|
|
eval { $orange->set_tag (undef); }; print $@; |
1679 |
|
|
|
1680 |
|
|
# this works too, but is slower |
1681 |
|
|
$orange->get( "juiciness" ); |
1682 |
|
|
$orange->set( juiciness => 123, |
1683 |
|
|
segments => 17 ); |
1684 |
|
|
|
1685 |
|
|
# Re-configure init_default - make each new orange have a |
1686 |
|
|
# random juiciness |
1687 |
|
|
$orange->set_init_default( juiciness => sub { int(rand(45)) } ); |