File Coverage

blib/lib/Class/Tangram.pm
Criterion Covered Total %
statement 642 950 67.5
branch 252 478 52.7
condition 140 274 51.0
subroutine 77 124 62.1
pod 33 35 94.2
total 1144 1861 61.4


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