File Coverage

blib/lib/Tree/Numbered.pm
Criterion Covered Total %
statement 124 189 65.6
branch 32 68 47.0
condition 1 5 20.0
subroutine 23 30 76.6
pod 24 27 88.8
total 204 319 63.9


line stmt bran cond sub pod time code
1             package Tree::Numbered;
2              
3 3     3   84228 use strict;
  3         7  
  3         8942  
4              
5             our $VERSION = '1.00';
6              
7             our @counters = (); # For getting new serial numbers.
8              
9             sub getNewSerial {
10 13     13 0 18 my $lucky_number = shift;
11 13 100       41 $counters[$lucky_number] = 1 unless (exists $counters[$lucky_number]);
12 13         41 return ($counters[$lucky_number]++);
13             }
14              
15             sub getNewLucky {
16 5     5 0 22 $#counters++;
17 5         13 return $#counters;
18             }
19              
20             # predeclare subs used in .
21             sub addField;
22              
23             # constructs a new tree or node.
24             # Arguments: $value - the default value to be stored in the node.
25             # %extra - extra fields as name => value.
26             # Returns: The tree object.
27              
28             sub new {
29 13     13 1 69 my ($parent, $value, $second, %extra) = @_;
30 13         16 my $class;
31            
32 13         49 my $properties = {
33             Items => [],
34             Cursor => -1,
35             };
36              
37            
38 13 100       45 if ($class = ref($parent)) {
39             # Give it the same number as its parent.
40 10         30 $properties->{_LuckyNumber} = $parent->{_LuckyNumber};
41 10         19 $properties->{_ParentRef} = $parent;
42 10         25 bless $properties, $class;
43              
44             # Inherit fields from parent (unless assigned in argument list).
45             $properties->addField($_, $parent->{$_})
46 10         36 foreach ($parent->getFieldNames);
47             } else {
48 3         7 $class = $parent;
49 3         14 $properties->{_LuckyNumber} = getNewLucky;
50 3         10 bless $properties, $class
51             }
52 13         57 $properties->{_Serial} = getNewSerial($properties->{_LuckyNumber});
53              
54             # Detect parameter passing style for backward compatibility.
55 13 100       50 if (scalar @_ > 2) {
    100          
56 3         12 $extra{$value} = $second;
57             } elsif (scalar @_ == 2) {
58 9 50       39 $extra{Value} = $value if defined $value;
59             }
60              
61             # Add requested fields, default to 'Value'.
62 13         36 foreach (keys %extra) {
63             # Calls are separated to override inherited settings.
64 14         39 $properties->addField($_);
65 14         42 $properties->setField($_, $extra{$_});
66             }
67 13         46 return $properties;
68             }
69              
70             # moves the cursor forward by one.
71             # Arguments: None.
72             # Returns: Whatever is pointed by the cursor, undef on overflow, first item
73             # on subsequent overflow.
74              
75             sub nextNode {
76 38     38 1 2811 my $self = shift;
77              
78 38         58 my $cursor = $self->{Cursor};
79 38         76 my $length = $self->childCount;
80 38         46 $cursor++;
81              
82             # return undef when end of iterations. On next call - reset counter.
83 38 100       93 if ($cursor > $length) {
84 1 50       4 $cursor = ($length) ? 0 : -1;
85             }
86 38         48 $self->{Cursor} = $cursor;
87              
88 38 100       92 if (exists $self->{Items}->[$cursor]) {
89 25         90 return $self->{Items}->[$cursor];
90             }
91 13         44 return undef;
92             }
93              
94             # returns the counter to the beginning of the list.
95             # Arguments: None.
96             # Returns: Nothing.
97              
98             sub reset {
99 12     12 1 16 my $self = shift;
100 12         26 $self->{Cursor} = -1;
101             }
102              
103             # deletes the item pointed to by the cursor.
104             # The curser is not changed, which means it effectively moves to the next item.
105             # However it does change to be just after the end if it is already there,
106             # so you won't get an overflow.
107             # Arguments: None.
108             # Returns: The deleted item or undef if none was deleted.
109              
110             sub delete {
111 0     0 1 0 my $self = shift;
112 0         0 my $cursor = $self->{Cursor};
113            
114 0 0       0 if (exists $self->{Items}->[$cursor]) {
115 0         0 my $deleted = splice(@{$self->{Items}}, $cursor, 1);
  0         0  
116              
117             # Make sure the cursor doesn't overflow:
118 0 0       0 if ($cursor > $self->childCount) {
119 0         0 $self->{Cursor} = $self->childCount;
120             }
121 0         0 return $deleted;
122             }
123              
124 0         0 return undef;
125             }
126              
127             # adds a node at the end of the list.
128             # Arguments: parameters for new.
129             # Returns: The added node or undef on error.
130              
131             sub append {
132 10     10 1 1811 my $self = shift;
133 10         31 my $newNode = $self->new(@_);
134 10 50       31 return undef unless $newNode;
135              
136 10         13 push @{$self->{Items}}, $newNode;
  10         21  
137 10         38 return $newNode;
138             }
139              
140             # saves the place of the cursor.
141             # Arguments: None.
142             # Returns: Nothing.
143              
144             sub savePlace {
145 12     12 1 19 my $self = shift;
146 12         56 $self->{Saved} = $self->{Cursor};
147             }
148              
149             # returns the cursor to its saved place if any. The place is
150             # still saved untill the save is changed.
151             # Arguments: None.
152             # Returns: 1 if restored, undef otherwise.
153              
154             sub restorePlace {
155 12     12 1 17 my $self = shift;
156 12 50 33     56 if (exists($self->{Saved}) && ($self->{Saved} <= $self->childCount)) {
157 12         20 $self->{Cursor} = $self->{Saved};
158 12         67 return 1;
159             }
160 0         0 return undef;
161             }
162              
163             # returns a new tree that is the same as the cloned one except for
164             # its lucky number.
165             # Arguments: None.
166             # Returns: None.
167              
168             sub clone {
169 8     8 1 12 my $self = shift;
170 8         12 my ($lucky_number, $parent) = @_;
171              
172 8 100       16 unless (defined $lucky_number) {
173 2         8 $lucky_number = getNewLucky;
174 2         14 $counters[$lucky_number] = $counters[$self->{_LuckyNumber}];
175             }
176              
177 8         14 my $cloned = {};
178 8         72 $cloned->{$_} = $self->{$_} foreach (keys %$self);
179 8         19 $cloned->{_ParentRef} = $parent;
180 8         90 $cloned->{_LuckyNumber} = $lucky_number;
181 6         39 $cloned->{Items} = [map {$_->clone($lucky_number, $cloned)}
  8         18  
182 8         10 @{ $self->{Items} }];
183            
184 8         32 return bless $cloned, ref($self);
185             }
186              
187             # runs a given subroutine on all descendants of a node.
188             # Arguments: $subref - the sub to be run.
189             # all remaining arguments will be passed to the subroutine,
190             # prepended by a ref to the node being processed.
191             # Returns: Nothing.
192              
193             sub deepProcess {
194 4     4 1 6 my $self = shift;
195 4         7 my ($subref, @args) = @_;
196              
197             # I do not use the savePlace + reset metods, because the subroutine
198             # passed by the user may mess it up.
199 4         4 foreach my $child (@{ $self->{Items} }) {
  4         13  
200 3         8 $subref->($child, @args);
201 3         24 $child->deepProcess($subref, @args);
202             }
203             }
204              
205             # does the same as deepProcess except that it also processes the
206             # root element.
207             # Arguments: see .
208             # Returns: Nothing.
209              
210             sub allProcess {
211 1     1 1 2 my $self = shift;
212 1         3 my ($subref, @args) = @_;
213            
214 1         4 $subref->($self, @args);
215 1         10 $self->deepProcess($subref, @args);
216             }
217              
218             #*******************************************************************
219             # Accessors:
220              
221             sub childCount {
222 70     70 1 80 my $self = shift;
223 70         67 return scalar @{$self->{Items}};
  70         221  
224             }
225              
226             # There is no setNumber because numbers are handled only by the object.
227             sub getNumber {
228 3     3 1 6 my $self = shift;
229 3         13 return $self->{_Serial};
230             }
231              
232             # same for LuckyNumber.
233             sub getLuckyNumber {
234 4     4 1 1524 my $self = shift;
235 4         20 return $self->{_LuckyNumber};
236             }
237              
238             sub getParentRef {
239 3     3 1 546 my $self = shift;
240 3         40 return $self->{_ParentRef};
241             }
242              
243             # returns a list of fields for an object, not including Value.
244             # Arguments: None.
245             # Returns: The list in list context, a ref to it in scalar context.
246              
247             sub getFieldNames {
248 116     116 1 127 my $self = shift;
249 116 50       247 return undef unless ($self->{Fields});
250            
251 116 50       210 if (wantarray) {
252 116         128 return @{ $self->{Fields} };
  116         397  
253             } else {
254 0         0 return $self->{Fields};
255             }
256             }
257              
258             # is a boolean function to determine if a field exists.
259             # Arguments: $name - field name.
260             # Returns: True if the field exists, undef if not.
261              
262             sub hasField {
263 105     105 0 1095 my $self = shift;
264 105         124 my $name = shift;
265              
266 105         168 for ($self->getFieldNames) {
267 159 100       720 return 1 if ($_ eq $name);
268             }
269 17         52 return undef;
270             }
271              
272             # accesses a field by name.
273             # Arguments: $name - field name.
274             # $value - field value.
275             # Returns: Field value on success, undef otherwise.
276              
277             sub getField {
278 52     52 1 1500 my $self = shift;
279 52         87 my $name = shift;
280              
281 52 100       85 return $self->{$name} if $self->hasField($name);
282 15         74 return undef;
283             }
284              
285             sub setField {
286 16     16 1 22 my $self = shift;
287 16         29 my ($name, $value) = @_;
288              
289 16 50       51 return $self->{$name} = $value if $self->hasField($name);
290 0         0 return undef;
291             }
292              
293             # returns a hash ref of field => value for each extra field.
294             # Arguments: None.
295             # Returns: See above.
296              
297             sub getFields {
298 0     0 1 0 my $self = shift;
299 0 0       0 return undef unless ($self->{Fields});
300              
301 0         0 my %fields = map { $_ => $self->getField($_) } $self->getFieldNames;
  0         0  
302 0         0 return \%fields;
303             }
304              
305             sub setFields {
306 0     0 1 0 my $self = shift;
307 0 0       0 return undef unless ($self->{Fields});
308              
309 0         0 my (%fields) = @_;
310 0         0 $self->setField($_, $fields{$_}) foreach (keys %fields);
311             }
312              
313             # adds an extra field to the object.
314             # Arguments: $name - field name.
315             # $value - field value.
316             # Returns: True on success, undef on failure (field exists).
317              
318             sub addField {
319 34     34 1 48 my $self = shift;
320 34         49 my ($name, $value) = @_;
321            
322             # Fail if field exists or is a predefined field.
323 34 100       87 return undef if (exists $self->{$name});
324            
325 24         29 push @{ $self->{Fields} }, $name;
  24         70  
326 24         50 $self->{$name} = $value;
327 24         79 return 1;
328             }
329              
330             # removes an extra field from the object.
331             # Arguments: $name - field name.
332             # Returns: True on success, undef on failure (field doesn't exist).
333              
334             sub removeField {
335 0     0 1 0 my $self = shift;
336 0         0 my $name = shift;
337              
338 0         0 my @fields = $self->getFieldNames;
339 0         0 for my $i (0..$#fields) {
340 0 0       0 if ($fields[$i] eq $name) {
341 0         0 delete $self->{$name};
342 0         0 splice @fields, $i, 1;
343 0         0 $self->{Fields} = \@fields;
344 0         0 return 1;
345             }
346             }
347 0         0 return undef;
348             }
349              
350             #***********************************************************************
351             # scary: AUTOLOAD captures access requests for fields.
352              
353             our $AUTOLOAD;
354              
355             sub AUTOLOAD {
356 34     34   107 my $self = shift;
357 34         132 $AUTOLOAD =~ s/(.*):://;
358 34         68 my $pkg = $1;
359              
360 34 50       130 if ($AUTOLOAD =~ /get(.*)$/) {
    0          
361 34 50       67 return $self->getField($1) if $self->hasField($1);
362 0         0 die ("No such field ($1) in tree");
363             } elsif ($AUTOLOAD =~ /set(.*)$/) {
364 0 0       0 return $self->setField($1, shift) if $self->hasField($1);
365 0         0 die ("No such field ($1) in tree");
366             }
367              
368 0         0 die "Can't call method $AUTOLOAD via package $pkg";
369             }
370              
371 0     0   0 sub DESTROY {}
372             #***************************************************************************
373             # Service methods.
374              
375             # returns the sub tree whose root element's serial number
376             # is requested.
377             # Arguments: $serial - the requested serial number.
378             # Returns - the matching object if it's there, undef otherwise.
379              
380             sub getSubTree {
381 0     0 1 0 my ($self, $serial) = @_;
382 0 0       0 return $self if ($serial == $self->getNumber);
383            
384 0         0 $self->savePlace;
385 0         0 $self->reset;
386              
387 0         0 while (my $branch = $self->nextNode) {
388 0 0       0 if ($branch->getNumber == $serial) {
    0          
389 0         0 $self->restorePlace;
390 0         0 return $branch;
391             } elsif (my $subtree = getSubTree($branch, $serial)) {
392 0         0 $self->restorePlace;
393 0         0 return $subtree;
394             }
395             }
396 0         0 $self->restorePlace;
397 0         0 return undef;
398             }
399              
400             # returns a list of serial numbers of all items under
401             # an item whose serial number is given as an argument.
402             # Arguments: $serial - denoting the item requested.
403              
404             sub listChildNumbers {
405 8     8 1 505 my $self = shift;
406 8         32 my $serial = shift;
407              
408 8         16 my @subSerials = ();
409 8 50       19 my $subtree = ($serial) ? getSubTree($self, $serial) : $self;
410              
411 8         29 $subtree->savePlace;
412 8         20 $subtree->reset;
413            
414 8         578 while (my $branch = $subtree->nextNode) {
415 12         22 push @subSerials, $branch->{_Serial};
416            
417 12 100       23 if ($branch->childCount > 0) {
418 4         18 push @subSerials, $branch->listChildNumbers;
419             }
420             }
421              
422 8         20 $subtree->restorePlace;
423 8         51 return @subSerials;
424             }
425              
426             # Will find an item in a tree by its serial number
427             # and return a list of all values for a requested field up to and including
428             # the requested one.
429             # Arguments: $serial - number of target node.
430             # $field - field name (defauld - Value)
431              
432             sub follow {
433 0     0 1   my $self = shift;
434 0           my ($serial, $field) = @_;
435 0   0       $field ||= 'Value'; # backward compatibility.
436              
437 0           $self->savePlace;
438 0           $self->reset;
439            
440 0           while (my $branch = $self->nextNode) {
441 0           my @patharray = ();
442 0 0         if ($branch->{_Serial} == $serial) {
    0          
443 0           $self->restorePlace;
444 0           return ($branch->getField($field));
445             } elsif ($branch->childCount) {
446 0           @patharray = $branch->follow($serial, $field);
447             }
448            
449 0 0         if ($#patharray >= 0) {
450             # Parent nodes go first:
451 0           unshift @patharray, $branch->getField($field);
452 0           $self->restorePlace;
453 0           return @patharray;
454             }
455             }
456              
457 0           $self->restorePlace;
458 0           return ();
459             }
460              
461             1;
462              
463             =head1 NAME
464              
465             Tree::Numbered - a thin N-ary tree structure with a unique number for each item.
466              
467             =head1 SYNOPSYS
468              
469             use Tree::Numbered;
470             my $tree = Tree::Numbered->new('John Doe');
471             $tree->append('John Doe Jr.');
472             $tree->append('Marry-Jane Doe');
473              
474             while (my $branch = $tree->nextNode) {
475             $branch->delete if ($branch->getValue eq 'Stuff I dont want');
476             }
477            
478             my $itemId = what_the_DB_says;
479             print join ' --- ', $tree->follow($itemId); # a list of items up to itemId.
480            
481             $tree->allProcess( sub {
482             my $self = shift;
483             $self->getValue =~ /^(\S*)/;
484             $self->addField('FirstName', $1);
485             } );
486              
487             etc.
488              
489             =head1 DESCRIPTION
490              
491             Tree::Numbered is a special N-ary tree with a number for each node. This is useful on many occasions. The first use I found for that (and wrote this for) was to store information about the selected item as a number instead of storing the whole value which is space-expensive.
492              
493             Every tree also has a lucky number of his own that distinguishes it from other trees created by the same module. This module is thin on purpose and is meant to be a base class for stuff that can make use of this behaveiour. For example, I wrote Tree::Numbered::DB which ties a tree to a table in a database, and Javascript::Menu which uses this tree to build menus for websites.
494              
495             One more feature that the module implements for the ease of subclassing it is an API for adding and removing fields from trees and nodes.
496              
497             =head1 BUILDING AND DESTROYING A TREE
498              
499             =over 4
500              
501             =item Tree::Numbered->new
502              
503             There is only one correct way to start an independent tree with its own lucky number from scratch: calling the class function I. Using new as a method is wrong because it will create a node for the same tree but the tree won't know of its existence. See below.
504              
505             There are two forms of calling I. The first is calling it with one argument only - the value for the node. If you do that, the tree / node will be created with one field, called 'Value', and this field will receive the value you supplied as an argument.
506              
507             The second form is calling I with a hash of => pairs. For each pair, a field by the name be created, and it's value will be . The Value field will not be created unless you specifically request it.
508              
509             Note: Calling I with no arguments is the same as calling it in the second form.
510              
511             =item $tree->clone
512              
513             Another way to obtain a new tree object is to clone an existing one. The clone method does that. The original object remains untouched.
514              
515             =item $tree->append
516              
517             This is the correct way to add an item to a tree or a branch thereof. Internally uses $tree->new but does other stuff. The arguments for I are the same as for I.
518              
519             =item $tree->delete
520              
521             Deletes the child pointed to by the cursor (see below) and returns the deleted item. Note that it becomes risky to use this item since its parent tree knows nothing about it from the moment it is deleted and you can cause collisions so use with caution.
522              
523             =back
524              
525             =head1 WORKING WITH FIELDS
526              
527             As this module is designed for subclassing, I added a mechanism to easily create fields in a tree. Fields are different from normal object attributes in that they are specifically registered as fields within their object, and can be added, removed, and querried for existence. For every field a set of accessors is auto created (actually, autoloaded). In the section 'Subclassing Issues' there's more on using fields vs. regular attributes.
528              
529             One more important thing to know about fields is that every node inherits all fields that his parent had at the moment of creation. The value of the field is also inherited unless the field was requested in the argument list for I, in which case it takes the value provided as an argument.
530              
531             Naming your fields: If you want to make use of the automatic accessors, you might want to name your fields with a capital first letter. This is because the accessors created for a field are nothing more then the name of the field prefixed with either 'get' or 'set'. Alternatively, use underscore-lower letter if that's your style.
532              
533             =head2 But what if I only need one field for storing some value?
534              
535             No problem - The module knows of a default field called 'Value' which is created if you only give one argument - the value - to I or I. When you build a tree like that, you'll have the methods getValue and setValue. Furthermore, the method I guesses that you want this field if you do not specify any other. So working with one field only is just a short-code version of working with many.
536              
537             =head2 Methods for working with fields:
538              
539             =over 4
540              
541             =item getFieldNames
542              
543             Returns a list of all registered fields within a node.
544              
545             =item addField(name, [value])
546              
547             Adds a field I to the node on which the method was invoked. if value is not specified, the field's value will default to undef. If the field does not exist, will do nothing - won't even set its value.
548              
549             If you need to add a field to all existing descendants of a node (future ones inherit the field automatically) use either I or I as described above in 'Synopsys'.
550              
551             Returns: True on success, undef on failure.
552              
553             =item removeField(name)
554              
555             Removes the field by that name, and deletes its value.
556              
557             Returns: True on success, undef on failure.
558              
559             =item getField(name)
560              
561             Returns the value of the field given in the name argument. if the field does not exist returns undef. If you want to check if the field exists, use I or try to call the automatic getter for that field, an attempt that will cause a painfull death if there's no such field.
562              
563             =item setField(name, [value])
564              
565             Sets the value of the requested field. If value is not specified, undef is assumed. Returns the new value on success, undef on failure (field does not exist).
566              
567             =item getFields
568              
569             Returns a reference to a hash of Field => Value pairs, for each field the node owns.
570              
571             =item setFields([Field => Value, Field => Value, ...])
572              
573             Sets the requested fields to the requested values. Keeps quiet if a field does not exist, so watch out.
574              
575             =back
576              
577             =head1 ITERATING OVER CHILD ITEMS
578              
579             =head2 The cursor
580              
581             Every node in the tree has its own cursor that points to the current item. When you start iterating, the cursor is placed just B the first child. When you are at the last item, trying to move beyond the last item will put the
582             cursor B the last item (which will result in an undef value, signalling the end) but the next attempt will cause the cursor to B from the first child.
583              
584             =head2 Methods for iteration
585              
586             =over 4
587              
588             =item nextNode
589              
590             Moves the cursor one child forward and returns the pointed child.
591              
592             =item reset
593              
594             Resets the cursor to point before the first child.
595              
596             =item savePlace
597              
598             Allows you to save the cursor's place, do stuff, then go back. There is only one save, though, so don't try to nest saves.
599              
600             =item restorePlace
601              
602             Tries to set the cursor to the item at the same place as it was when its place was saved and returns true on success. If the saved place doesn't exist anymore returns undef. Note: If you deleted items from the tree you might not get to the right place.
603              
604             =head1 ACCESSORS
605              
606             The following accessors are always available and deal with the node's properties, not with fields:
607              
608             =over 4
609              
610             =item getNumber
611              
612             Gets the number of the node within its tree. There is no setter since this number is special.
613              
614             =item getLuckyNumber
615              
616             Gets the number of the main tree that the node is a member of. Again, there is no setter since this is special.
617              
618             =item getParentRef
619              
620             Returns a reference to the parent of the node (of course the root will return undef).
621              
622             =item childCount
623              
624             Gets the number of childs for this node.
625              
626             =back
627              
628             Also, as described above, for each field you create, a getter and a setter automatically show up when needed. For each field *, you'll have the methods get* and set*.
629              
630             =head1 THINGS YOU CAN DO WITH NODE NUMBERS
631              
632             Well, I didn't include the node numbers just for fun, this is actually very needed sometimes. There are three basic service methods that use this (subclasses may add to this):
633              
634             =over 4
635              
636             =item getSubTree([number])
637              
638             If a node who's number is given is a member of the subtree that the method was invoked on, that node will be returned, undef otherwise. To be consistent with set theory, any tree is considered to be its own child, so giving the number of the root node will return that node.
639              
640             =item listChildNumbers([number])
641              
642             returns a list of all numbers of nodes that are decendants (any level) of the subtree whose number is given. Number is optional, the node's own number is used if no number is specifically requested.
643              
644             =item follow(number, [field])
645              
646             returns a list of all field values starting from the decendant node with the requested number, through every parent of the node and up to the node the method was invoked on. If no such node exists, returns an empty list. If no field is specified, defaults to the Value field.
647              
648             =back
649              
650             =head1 OTHER METHODS
651              
652             There are two methods that apply a certain subroutine to whole trees:
653              
654             =over 4
655              
656             =item deepProcess (SUBREF, ARG, ARG, ...)
657              
658             For each child of the node, runs the subroutine referenced by SUBREF with an argument list that starts with a reference to the child being processed and continues with the rest of the arguments passed to deepProcess. In short, your subroutine is called as a method of the child items, so you can shift $self out of the arguments list and use it.
659              
660             =item allProcess (SUBREF, ARG, ARG, ...)
661              
662             does the same as deepProcess but runs on the root node first.
663              
664             =back
665              
666             =head1 SUBCLASSING ISSUES
667              
668             =over 4
669              
670             =item Fields vs. normal attributes
671              
672             The usual implementation of a data field is as a hash key, either in one hash per object, or one hash per property (an inside-out object). The fields mechanism allows you to maintain a list of fields, but assumes that the fields are regular data fields. My subclasses of this modules use Fields for regular data, but do not register as fields anything with 'magical' or 'internal use only' data. The reason is that not using fields gives more control over the behaveiour of these fields.
673              
674             For example, if you implement a property using the fields mechanism, and you don't want the user to access it, you'll have to manually override the automatic accessors to die or do something unpleasant when they're used.
675              
676             Secondly, if you register a property as a field - it is easy to remove it by accident even if you count on it to always exist. That's bad, isn't it?
677              
678             And lastly, if you count on a value to be frequently used, you might not want the overhead of autoloading the accessors or you don't need it in the list of fields.
679              
680             The benefits of using fields, are in the easiness of managing a set of fields acording to changing demands and the simplicity of extending the behaveiour of a class. For example, check out my Javascript::Menu which creates the URL field if the user is just trying to build a navigational menu, but doesn't bother if the user uses the more complex, action based menus that I designed the module for.
681              
682             =item Setting attributes that have no setters
683              
684             You'll notice that none of the constant properties of this class (the number, lucky number and parent ref) have a setter. This is on purpose. These properties are determined automatically and are not supposed to be messed with.
685              
686             If you do want to change the behaveiour, I assume that you know what you're doing and that you have read some source. Then you'll have no problem with implementing your desired behaveiour.
687              
688             =item Extending field behaveiour
689              
690             The fields mechanism, however, is more object-oriented. Everything that has to do with fields tries to use field methods even internally. Even I calls addField to add fields. The main implication of this (that I found) is that if you want things to happen when a field is created, you must make sure that the data for this happening (if any is required) is already available at the time of adding the field. For an example of that, see how my Tree::Numbered::DB makes sure that a field mapping will always be available, before calling SUPER::new in its own constructor.
691              
692             =back
693              
694             =head1 BUGS AND PROBLEMS
695              
696             Works pretty well for me. If you found anything, please send a bug report:
697             Ehttp://rt.cpan.org/NoAuth/Bugs.html?Dist=Tree-NumberedE
698             or send mail to Ebug-Tree-Numbered#rt.cpan.orgE
699              
700             =head1 SEE ALSO
701              
702             Tree::Numbered::DB, Javascript::Menu
703              
704             =head1 AUTHOR
705              
706             Yosef Meller, E mellerf@netvision.net.il E
707              
708             =head1 COPYRIGHT AND LICENSE
709              
710             Copyright 2003 by Yosef Meller
711              
712             This library is free software; you can redistribute it and/or modify
713             it under the same terms as Perl itself.
714              
715             =cut