File Coverage

blib/lib/Class/Maker.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # Author: Murat Uenalan (muenalan@cpan.org)
2             #
3             # Copyright (c) 2001 Murat Uenalan. All rights reserved.
4             #
5             # Note: This program is free software; you can redistribute
6             #
7             # it and/or modify it under the same terms as Perl itself.
8              
9             package Class::Maker;
10              
11 8     8   158084 require 5.005_62; use strict; use warnings;
  8     8   20  
  8         280  
  8         42  
  8         17  
  8         257  
12            
13 8     8   40 no warnings 'once';
  8         20  
  8         382  
14              
15             our $VERSION = "0.06";
16            
17 8     8   6126 use Class::Maker::Basic::Handler::Attributes;
  0            
  0            
18            
19             use Class::Maker::Basic::Fields;
20              
21             use Carp qw(cluck);
22            
23             use Exporter;
24            
25             use subs qw(class);
26            
27             our $DEBUG = 0;
28            
29             our $TRACE = ( \*STDOUT, \*STDERR )[ ($ENV{CLASSMAKER_TRACE}||2) - 1 ];
30            
31             our %EXPORT_TAGS = ( 'all' => [ qw(class) ] );
32            
33             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
34            
35             our @EXPORT = ();
36            
37             our @ISA = qw( Exporter );
38            
39             our $pkg = '<undefined class>';
40            
41             our $cpkg = $pkg;
42            
43             our $explicit = 0;
44            
45             # Preloaded methods go here.
46            
47             sub import
48             {
49             Class::Maker->export_to_level( 1, @_ );
50             }
51            
52             sub class
53             {
54             class_import( scalar caller, @_ );
55             }
56            
57             sub class_import
58             {
59             # $class is the caller package
60            
61             my ( $class, @args ) = @_;
62            
63             return unless @args;
64            
65             # construct the destination package for the classes:
66             #
67             # - we create the class within the current package (default)
68             # - or create it in the current package
69             # - or when starting with 'main::' or '::' we create it with the main package
70            
71             unless( ref $args[0] )
72             {
73             if( $args[0] =~ /^[\.\*]/ )
74             {
75             $args[0] =~ s/^[\.\*]//;
76              
77             $pkg = $class.'::'.$args[0];
78              
79             print $Class::Maker::TRACE "Class::Maker *DEBUG*: DETECTED '.' IN CLASS NAME - CREATING CLASS IN SUBPACKAGE: $pkg" if $DEBUG;
80             }
81             else
82             {
83             $pkg = ( $args[0] =~ s/^(?:main)?::// ) ? $args[0] : $class.'::'.$args[0];
84             }
85             }
86             else
87             {
88             # We had no explicit destination package, so create the class in the current package
89            
90             $pkg = $class;
91             }
92            
93             #remember caller package
94            
95             $cpkg = $class;
96            
97             # init class 'cause somebody could give an empty parameter
98             # list for abstract classes
99            
100             Class::Maker::Basic::Fields::isa( [] );
101            
102             Class::Maker::Basic::Fields::configure( { ctor => 'new', dtor => 'delete' } );
103            
104             foreach my $arg ( @args )
105             {
106             if( ref($arg) eq 'HASH' )
107             {
108             no strict 'refs';
109            
110             Class::Maker::Reflection::install( $arg );
111            
112             foreach my $func ( sort { $b cmp $a } keys %$arg )
113             {
114             # fields for the class attributes/isa/configure/..
115            
116             "Class::Maker::Basic::Fields::${func}"->( $arg->{$func}, $arg );
117             }
118             }
119             }
120             }
121            
122             sub _make_method
123             {
124             no strict 'refs';
125            
126             my $type = shift;
127            
128             my $name = shift;
129            
130             $Class::Maker::Basic::Handler::Attributes::name = $explicit ? "${pkg}::$name" : $name;
131            
132             no strict 'refs';
133            
134             if( *{ "Class::Maker::Basic::Handler::Attributes::${type}" }{CODE} )
135             {
136             return *{ "${pkg}::$name" } = Class::Maker::Basic::Handler::Attributes->$type;
137             }
138            
139             return *{ "${pkg}::$name" } = Class::Maker::Basic::Handler::Attributes->default;
140             }
141              
142             #
143             # Reflection
144             #
145              
146             package Class::Maker::Reflex; # returned by Class::Maker::Reflection::reflect
147              
148             no warnings 'once';
149              
150             sub definition : method
151             {
152             my $this = shift;
153              
154             return $this->{def};
155             }
156            
157             sub parents : method
158             {
159             my $this = shift;
160              
161             return unless exists $this->{isa};
162            
163             return Class::Maker::Reflection::inheritance_isa( @{ $this->{isa} } );
164             }
165              
166             package Class::Maker::Reflection;
167              
168             no warnings 'once';
169              
170             our $DEBUG = $Class::Maker::DEBUG;
171              
172             use Data::Dump qw(dump);
173              
174             sub DEBUG : lvalue { $DEBUG }
175            
176            
177             # DEEP : Whether reflect should traverse the @ISA tree and return all parent reflex's
178            
179             our $DEEP = 0;
180            
181             our $DEFINITION = 'CLASS';
182            
183             sub _get_definition
184             {
185             my $class = shift;
186            
187             no warnings;
188            
189             no strict 'refs';
190            
191             return \${ "${class}::".$Class::Maker::Reflection::DEFINITION };
192             }
193            
194             sub _get_isa
195             {
196             no strict 'refs';
197            
198             return @{ $_[0].'::ISA'};
199             }
200            
201             sub install
202             {
203             ${ Class::Maker::Reflection::_get_definition( $pkg ) } = $_[0];
204             }
205            
206             sub reflect
207             {
208             my $class = ref( $_[0] ) || $_[0] || die;
209            
210             my $rfx = bless { name => $class }, 'Class::Maker::Reflex';
211            
212             # - First get the "${$DEFINITION}" href containing the class definition
213             # - find the functions of that class declerated with ': method'
214             # - catch up the parent class reflection if DEEP is activated
215             # - update "${$DEFINITION}"->{isa} with its real @ISA
216            
217             $rfx->{def} = ${ Class::Maker::Reflection::_get_definition( $class ) };
218            
219             $rfx->{methods} = find_methods( $rfx->{name} );
220            
221             no strict 'refs';
222            
223             if( $DEEP && defined *{ "${class}::ISA" }{ARRAY} )
224             {
225             $rfx->{isa} = \@{ *{ "${class}::ISA" }{ARRAY} };
226            
227             $rfx->{parents}->{$_} = reflect( $_ ) for @{ $rfx->{isa} };
228             }
229            
230             return $rfx;
231             }
232            
233             sub classes
234             {
235             no strict 'refs';
236            
237             my @found;
238            
239             my $path = shift if @_ > 1;
240            
241             foreach my $pkg ( @_ )
242             {
243             next unless $pkg =~ /::$/;
244            
245             $path .= $pkg;
246            
247             if( $path =~ /(.*)::$/ )
248             {
249             my $clean_path = $1;
250            
251             if( $path ne 'main::' )
252             {
253             if( my $href_cls = reflect( $clean_path ) )
254             {
255             push @found, { $clean_path => $href_cls };
256             }
257             }
258            
259             foreach my $symbol ( sort keys %{$path} )
260             {
261             if( $symbol =~ /::$/ && $symbol ne 'main::' )
262             {
263             push @found, classes( $path, $symbol );
264             }
265             }
266             }
267             }
268            
269             return @found;
270             }
271            
272             use attributes;
273            
274             sub find_methods
275             {
276             my $class = shift;
277            
278             my $methods = [];
279            
280             no strict 'refs';
281            
282             foreach my $pkg ( $class.'::' )
283             {
284             foreach ( sort keys %{$pkg} )
285             {
286             unless( /::$/ )
287             {
288             if( defined *{ "$pkg$_" }{CODE} )
289             {
290             if( my $type = attributes::get( \&{ "$pkg$_" } ) )
291             {
292             push @$methods, "$_" if $type =~ /method/i;
293             }
294             }
295             }
296             }
297             }
298            
299             return $methods;
300             }
301              
302             # my @obj = @{ Class::Maker::Reflection::find_object_in_namespace_that_isa( main => [qw( NotExisting ) ], 'Parse::Grammar::POQL::TestShopping' => [ 'Person', 'Shopping::Cart' ] )->{objects} };
303            
304             sub find
305             {
306             my %request = @_;
307              
308             my $result_report = {};
309              
310             my @result;
311            
312             # parsing all references in a package (via symbol table)
313            
314             while( my ( $where, $what ) = each %request )
315             {
316             no strict 'refs';
317            
318             foreach my $pkg ( $where.'::' )
319             {
320             printf $Class::Maker::TRACE "Searching in package '$where' for '%s' instances\n", Data::Dump::dump($what) if DEBUG;
321            
322             foreach ( sort keys %{$pkg} )
323             {
324             unless( /::$/ )
325             {
326             print $Class::Maker::TRACE defined *{ "$pkg$_" } ? "PKG: " : "" if DEBUG;
327             print $Class::Maker::TRACE "$pkg$_\n" if DEBUG;
328              
329             if( defined *{ "$pkg$_" } )
330             {
331             my $sref = \${ "$pkg$_" };
332            
333             if( ref( $sref ) eq 'REF' )
334             {
335             print $Class::Maker::TRACE "\tREF: $pkg$_\n" if DEBUG;
336              
337             my $type = ref( $$sref );
338              
339             if( Class::Maker::Reflection::inheritance_isa( $type ) )
340             {
341             print $Class::Maker::TRACE "\tISA: ", Data::Dump::dump( Class::Maker::Reflection::inheritance_isa( $type ) ), "\n" if DEBUG;
342              
343             print $Class::Maker::TRACE "\tDUMP: ", Data::Dump::dump( $$sref ), "\n" if DEBUG;
344             }
345              
346             print $Class::Maker::TRACE "\n" if DEBUG;
347              
348             for my $isa_maybe ( @$what )
349             {
350             printf $Class::Maker::TRACE "** GRABBED ABOVE OBJECT **\n\n\n" if $$sref->isa( $isa_maybe ) && DEBUG;
351            
352             if( $$sref->isa( $isa_maybe ) )
353             {
354             push @{ $result_report->{alpha} }, "$pkg$_";
355              
356             push @{ $result_report->{objects} }, $$sref;
357             }
358             }
359             }
360             }
361             }
362             }
363             }
364             }
365            
366             return $result_report;
367             }
368              
369              
370              
371              
372            
373             # helpers
374            
375             sub _isa_tree
376             {
377             my $list = shift;
378            
379             my $level = shift;
380            
381             for my $child ( @_ )
382             {
383             my @parents = Class::Maker::Reflection::_get_isa( $child );
384            
385             $level++;
386            
387             push @{ $list->{$level} }, $child;
388            
389             warn sprintf "\@%s::ISA = qw(%s);",$child , join( ' ', @parents ) if $Class::Maker::DEBUG;
390            
391             _isa_tree( $list, $level, @parents );
392            
393             $level--;
394             }
395             }
396            
397             # returns the isa tree sorted by level of recursion
398            
399             # 5 -> Exporter
400             # 4 -> Object::Debugable
401             # 3 -> Person, Exporter
402             # 2 -> Employee, Exporter, Object::Debugable
403             # 1 -> Doctor
404            
405             sub isa_tree
406             {
407             my $list = {};
408            
409             _isa_tree( $list, 0, @_ );
410            
411             return $list;
412             }
413            
414             # returns the isa tree in a planar list (for con-/destructor queue's)
415            
416             sub inheritance_isa
417             {
418             warn sprintf "SCANNING ISA FOR (%s);", join( ', ', @_ ) if $Class::Maker::DEBUG;
419            
420             my $construct_list = isa_tree( @_ );
421            
422             my @ALL;
423            
424             foreach my $level ( sort { $b <=> $a } keys %$construct_list )
425             {
426             push @ALL, @{ $construct_list->{$level} };
427             }
428            
429             return \@ALL;
430             }
431              
432             1;
433              
434             __END__
435              
436             =head1 NAME
437              
438             Class::Maker - classes, reflection, schemas, serialization, attribute- and multiple inheritance
439              
440             =head1 SYNOPSIS
441              
442             use Class::Maker qw(:all);
443              
444             class Something;
445              
446             class Person,
447             {
448             isa => [ 'Something' ],
449              
450             public =>
451             {
452             scalar => [qw( name age internal )],
453             },
454              
455             private
456             {
457             int => [qw( internal )],
458             },
459             };
460              
461             sub Person::hello
462             {
463             my $this = shift;
464              
465             $this->_internal( 2123 ); # the private one
466              
467             printf "Here is %s and i am %d years old.\n", $this->name, $this->age;
468             };
469              
470             my $p = Person->new( name => Murat, age => 27 );
471              
472             $p->hello;
473              
474              
475             =head1 DESCRIPTION
476              
477             This package is for everybody who wants to program oo-perl and does not really feel comfortable with the common way. Class::Maker introduces the concept of classes via a "class" function. It automatically creates packages, ISA, new and attribute-handlers. The classes can inherit from common perl-classes and class-maker classes. Single and multiple inheritance is supported.
478              
479             Reflection is transparently implemented and allows one to inspect the class properties and methods during runtime. This is helpfull for implementing persistance and serialization. A Tangram (see cpan) schema generator is included to the package, so one can use Tangram object-persistance on the fly as long as he uses Class::Maker classes.
480              
481             =head1 INTRODUCTION
482              
483             When you want to program oo-perl, mostly you suffer under the flexibility of perl. It is so flexibel, you have to do alot by hand. Here an example (slightly modified) from perltoot perl documentation for demonstration:
484              
485             package Person;
486              
487             @ISA = qw(Something);
488              
489             sub new {
490             my $self = {};
491             $self->{NAME} = undef;
492             $self->{AGE} = undef;
493             bless($self); # but see below
494             return $self;
495             }
496              
497             sub name {
498             my $self = shift;
499             if (@_) { $self->{NAME} = shift }
500             return $self->{NAME};
501             }
502              
503             sub age {
504             my $self = shift;
505             if (@_) { $self->{AGE} = shift }
506             return $self->{AGE};
507             }
508              
509             C++ has really straightforward class decleration style. It looks really beautiful. At that time many cpan modules tried to compensate with perl idiom, i still rather missed something. This package though has a "class" function which transparetly decleares perl classes with some rememberance to other languages. It smoothly integrates into perl code and handles may issues a beginner would immediately stumble (such as package issues etc). So the above example could be now written as:
510              
511             use Class::Maker qw(class);
512              
513             class 'Person',
514             {
515             isa => [ 'SomeBaseClass' ],
516              
517             public =>
518             {
519             scalar => [qw( name age )],
520             },
521             };
522              
523             When using "class", you do not explictly need "package". The function does all symbol creation for you. It is more a class decleration (like in java/cpp/..). So here we now leap into the documentation.
524              
525             =head1 FUNCTIONS
526              
527             =head2 class()
528              
529             The 'class' function is very central to Class::Maker.
530              
531             class 'Class',
532             {
533             ..FIELDS..
534             };
535              
536             [Note] The parantheses for the class() function are optional.
537              
538             Here 'Class' is the Name for the class. It is also the name for the package where the symbols for the class are created. Examples: 'Animal', 'Animal::Spider', 'Histology::Structures::Epithelia'.
539              
540             Normally the class is created related to the main package:
541              
542             package Far::Far::Away;
543              
544             class 'Galaxy',
545             {
546             };
547              
548             Like with B<package> 'Galaxy' would become to 'main::Galaxy' (and not Far::Far::Away::Galaxy).
549              
550             =head2 FIELDS
551              
552             Fields are the keys in the hashref given as the second (or first if the first argument (classname) is omitted) argument to "class". Here are the basic fields (for adding new fields read the Class::Maker::Basic::Fields).
553              
554             =head3 isa => aref
555              
556             Same as the @ISA array in an package (see perltoot).
557              
558             Some short-cut syntax is available:
559              
560             a) when the name is started with an '.' or '*' the package name is extrapolated to that name:
561              
562             package Far::Far::Away;
563              
564             class Galaxy,
565             {
566             isa => [qw( .AnotherGalaxy )],
567             };
568              
569             Then '.AnotherGalaxy' becomes expanded to 'Far::Far::Away::AnotherGalaxy'.
570              
571             =head3 public => href
572            
573             public =>
574             {
575             int => [qw(id)],
576             },
577              
578             leads to a attribute-handler which can be used like:
579              
580             $obj->id( 123 );
581              
582             my $value = $obj->id;
583              
584             Because the default handler is an lvalue function, the following call is also valid:
585              
586             $obj->id = 5678;
587              
588              
589             These keys are 'type-identifiers' (no fear, its simple), which help you to sort things. In general these are used to create handlers for the type. It is somehow like the get/set like method functions to access class-properties, but its more generalized and not so restrictive. By default, every non-known type-identifier is a simple scalar handler. Class::Maker will not warn you at any point, if you use a unknown type-identifier. So that
590              
591             public =>
592             {
593             scalar => ...
594             array => ...
595             hash => ...
596              
597             _anthing_here_ => ..
598             }
599              
600             Because b<array> and b<hash> are internally decleared and creating special mutators/handlers they will be not create scalar handlers,
601             but 'scalar' and '_anything_here' will create scalara mutators, as they are forwarded to the default scalar handlers; both are internally not explicitly defined.
602             The mechanism is extendable, see L<Class::Maker::Basic::Fields>.
603              
604             =head3 private => href
605              
606             All properties in the 'private' section, get a '_' prepended to their names.
607              
608             private =>
609             {
610             int => [qw(uid gid)],
611             },
612              
613             So you must access 'uid' with $obj->_uid();
614              
615             public =>
616             {
617             int => [qw(uid gid)],
618              
619             string => [qw(name lastname)],
620              
621             ref => [qw(father mother)],
622              
623             array => [qw(friends)],
624              
625             custom => [qw(anything)],
626             },
627              
628             Nothing more, nothing less. The significant part is that no encapsulation as such is present (as in cpp). The only encapsulation is the "secret" that
629             you have to prepend and '_' in front of the name.
630              
631             =head3 configure => href
632              
633             This Field is for general options. Basicly following options are supported:
634              
635             a) new: The name of the default constructor is 'new'. With this option you can change the name to something of your choice. For instance:
636              
637             configure =>
638             {
639             new => 'connect'
640             },
641              
642             Could be used for database objects. So you would use
643              
644             my $obj AnyClass->connect( );
645              
646             to create an AnyClass object.
647              
648             PS. Class::Maker provides a very sophisticated default constructor that does a lot (including the inhertance issues) and is explained somewhere else.
649              
650             c) I<private>: Prefix string (default '_') for private functions can be changed with this.
651              
652             private =>
653             {
654             int => [qw(dummy1)],
655             },
656            
657             configure =>
658             {
659             private => { prefix => '__' },
660             },
661              
662             would force to access 'dummy1' via ->__dummy1().
663              
664             =head3 automethod
665              
666             Reserved.
667              
668             =head3 has
669              
670             Reserved. Is planned to be used for 'has a' relationships.
671              
672             =head3 default => href
673              
674             Give default values for class attributes. It is the same as the handler was called with the value within the L<_postinit> function.
675              
676             default =>
677             {
678             name => 'John',
679              
680             friends => [qw(Petra Jenna)],
681             },
682              
683             So after construction the CLASS->name method would return 'John' etc.
684              
685             =head3 version => scalar
686              
687             Give the class/objects a version number. Internally the $VERSION is set to that value.
688              
689             =head3 persistance => href
690              
691             Here you can set options and add information for the reflect-function. You can also add custom information, you may want to process when you reflect objects.
692              
693             For example the tangram-schema generator looks for an 'abstract' key, to handle this class as an abstract class:
694              
695             persistance =>
696             {
697             abstract => 1,
698             },
699              
700             You can read more about Persistance under the L<Class::Maker::Extension::Persistance> manpage.
701              
702             =head1 Global flags
703              
704             =head2 $Class::Maker::explicit
705              
706             Internally an instance of a class holds all properties/attributes in an hash (The object is blessed with a hash-ref). The keys are normally
707             exactly the same as you declare in the descriptors. In special cases you want inheritance per se, but still might be interested to call parent methods explicitly. Put another way,
708             when you use 'soft' inheritance, you may have name clashes if a parent object uses the same name for a property as its child.
709             To compensate that problem, set this global (very early in your program, best is BEGIN block) explicit to something true (i.e. 1). This will lead to internal prepending of the classname to the key name:
710              
711             BEGIN
712             {
713             $Class::Maker::explicit = 1;
714             }
715              
716             'A' inherits 'B'. Both have a 'name' property. With explicit internally the fields are distinct:
717              
718             A::name
719             B::name
720              
721             [Note] This does not collide with attribute-overloading/inheritance ! Because the first attribute-handler in the isa-tree is always called. You do not have to care for this. Only use this feature, if you have fear that name clashes could appear, beside overloading. Per default it is turned off, because i suppose that most class designers care for name clashes themselfs.
722              
723             =head1 INTERNALS
724              
725             For this example:
726              
727             class 'Person',
728             {
729             isa => [ 'SomeBaseClass' ],
730              
731             public =>
732             {
733             scalar => [qw( name age )],
734             },
735             };
736              
737             Following happens in the background, when using 'class':
738              
739             =over 4
740              
741             =item 1.
742             creates a package "Person".
743              
744             =item 2.
745             sets @Person::ISA to the [ 'SomeBaseClass' ].
746              
747             =item 3.
748             creates method handlers for the attributes (including lvalue methods).
749             While "hash" and "array" keys are really functional keywords, any other
750             key will simply result in a scalar get/set method.
751              
752             =back
753              
754             =item 4.
755             exports a default constructor (i.e."Person::new()") which handles argument initialization.
756             It has also a mechanism for initializing the parent objects (including MI).
757              
758             =item 5.
759             creates $Person::CLASS holding a hashref to the unmodified second argument to 'class' (or the first, if the package name is omitted). This is essential for reflection: i.e. you can get runtime information about the class. See below.
760              
761             =back
762              
763             =back
764              
765             =head1 USING AN CLASS/OBJECT
766              
767             =head2 CONSTRUCTION
768              
769             Once a class is created it is shipped with a versatile C<new()> constructor. It is central to L<Class::Maker> because it deploy the object correctly, including constructing the multiple-inheritance chain and presetting class fields. To have fine grained control over the construction process following special methods are available for modification during construction. See the L<Class::Maker::Basic::Constructor> deeper explanation of the construction process.
770              
771             =head2 METHODS
772              
773             =head2 DESTRUCTION
774              
775             =head1 RESERVED SYMBOLS
776              
777             =head2 %CLASS
778              
779             As said, once a class is created L<Class::Maker> creates a C<CLASS> hash into the package. It is required for the process of runtime introspection (reflection). In general it is mostly similar to the L<FIELDS> hash during decleration, but one shouldnt count on that, because it is surely modified in future. Refer to the L<Class::Maker::Basic::Reflection> for correctly access the introspective freatures. Although today it has the function to:
780              
781             =over 4
782              
783             =item -
784             have dependency/class walking (see the contrib/ directory of the distribution for an example script).
785              
786             =item -
787             creating on-the-fly persistance => (for an example with Tangram see below)
788              
789             =item -
790             it creates the complete tangram schema tree (Tangram users know how hard it is
791              
792             =head1 PERFORMANCE
793              
794             I never seriously benchmarked Class::Maker. Because the internal representation is just the same as for standard perl-classes, only a minimal delay in the constructor (during scan through the class hirarchy for _init() routines) should be apparent. Beware that the accessors for any member of course delay the processsing (wildly guessed to be 3x slower). There is a hack-ish way to circumvent this, and may, increase speed when required:
795             - directly going into the object gut with $this->{member}. Beware that the member can be hidden as ->{SUPERCLASS::member}.
796              
797             =head1 EXAMPLES
798              
799             All test files (test.pl and t/) are verbose enough for a good overview. Visit the Class::Maker::Examples manpage for examples how to write basic data-type-like classes and basic classes used for i.e. e-commerce applications.
800              
801             =head1 EXPORT
802              
803             facultative: qw(reflect schema)
804             obligate: qw(class)
805              
806             class by default.
807              
808             [Note] If you care about ns pollution, just use Class::Maker::class directly.
809              
810             Class::Maker::class 'Person',
811             {
812             ...
813             };
814              
815             =head1 KNOWN BUGS/PROBLEMS
816              
817             isa => [qw( )] isnt in sync with @ISA. When @ISA (or isa) is modified after initation, the $reflex->{isa} will only represent the state during object initiation.
818              
819             <& /maslib/signatures.mas:author_as_pod, &>
820              
821             Contributions (Ideas or Code):
822              
823             - Terrence Brannon
824              
825             =head1 COPYRIGHT
826              
827             (c) 2001 by Murat Uenalan. All rights reserved.
828             Note: This program is free software; you can redistribute
829             it and/or modify it under the same terms as Perl itself.
830              
831             =head1 SEE ALSO
832              
833             L<Class::Maker::Exception>, L<Class::Maker::Basic::Fields>, L<Class::Maker::Basic::Reflection>, L<Class::Maker::Basic::Handler::Attributes>,L<Class::Maker::Basic::Types>,L<Class::Maker::Examples>, L<Class::Maker::Generator>, L<Class::Maker::Extension::Schema::Tangram>.
834              
835             =head1 Search for Class::Maker::* at CPAN
836              
837             Also at CPAN: Class::*, Tangram
838              
839             =head1 LITERATURE
840              
841             [1] Object-oriented Perl, Damian Conway
842             [2] Perl Cookbook, Nathan Torkington et al.
843              
844             =cut