File Coverage

blib/lib/Class/GAPI.pm
Criterion Covered Total %
statement 3 98 3.0
branch 0 40 0.0
condition n/a
subroutine 1 10 10.0
pod 0 8 0.0
total 4 156 2.5


line stmt bran cond sub pod time code
1             package Class::GAPI ;
2             $VERSION = '1.1' ;
3 1     1   43955 use strict ;
  1         2  
  1         1427  
4            
5             #
6             # GAPI, Generic API. This is a foundation class with loads
7             # of automation built in. It is probably slow, but you get
8             # lots of handy tricks with it.
9             #
10            
11             sub new {
12 0     0 0   my $class = shift ;
13            
14 0           my %self ; # This has to be 2 lines.
15 0 0         %self = @_ if scalar(@_) ; # Don't Change it.
16            
17 0           my $obj = bless(\%self, $class) ; # I gotta be me
18            
19 0           while(my ($key, $val) = each %self) {
20 0           delete($self{$key}) ;
21 0           my $block = join "", ('$obj->', "$key", '($val) ;') ; # Autoload recieved properties
22 0           eval($block) ;
23             }
24            
25 0           foreach(eval(join "", ('@', $class, '::Default_Properties'))) {
26 0 0         unless (defined $obj->{$_}) {
27 0           my $block = join "", ('$obj->', "$_", '() ;') ; # Autoload Default Properties
28 0           eval($block) ;
29             }
30             }
31            
32 0           foreach(eval(join "", ('@', $class, '::Children'))) {
33            
34 0           my $namespace = $_ ;
35            
36 0 0         if ($namespace =~ /^Class\:\:GAPI\:\:/) {
    0          
37 0           $_ = 'Class::GAPI' ; # Stub Class
38 0           $namespace =~ s/^Class\:\:GAPI\:\:// ;
39             } elsif ($namespace =~ /^Class\:\:List\:\:/) {
40 0           $_ = 'Class::List' ; # Stub List
41 0           $namespace =~ s/^Class\:\:List\:\:// ;
42             } else {
43 0           $namespace =~ s/^.*\:\:// ; # Named Class
44             }
45 0 0         unless (defined $obj->{$namespace}) {
46 0           my $block = join "", ($_, '->new();') ; # Named Class Constructor
47 0           $obj->{$namespace} = eval($block) ;
48             }
49             }
50            
51 0           eval('$obj->_init() ;') ; # sub _init is reserved against autoloading
52 0           return $obj ;
53             }
54            
55             #######################################################################
56             #######################################################################
57            
58             #######################################################################
59             #######################################################################
60            
61             sub AUTOLOAD {
62 0     0     my $self = shift ;
63 0           my $argument = undef ; # Whatever is passed to the function
64 0           my @tree ; # FQ namespace to parse
65             my $functioname ; # Name of function we are replacing.
66 0           my $namespace ; # The local namespace of the function
67 0           our $AUTOLOAD ; # Gift from Perl
68            
69 0 0         $argument = shift @_ if scalar(@_) ;
70 0           @tree = split(/\:\:/, $AUTOLOAD) ;
71 0           $functioname = pop @tree ;
72 0           $namespace = pop @tree ;
73            
74             # warn("AUTOLOAD\,$functioname\,$namespace\,$argument") ;
75            
76 0 0         return if $AUTOLOAD =~ /::DESTROY$/ ; # Destruction requires no action
77 0 0         return undef if $functioname eq $namespace ; # Avoid recursion
78 0 0         return undef if $functioname eq '_init' ; # _init is reserved for user defined init.
79            
80 0 0         if (defined $argument) { # Set the property and return a pointer
81 0           $self->{$functioname} = $argument ;
82 0           return (as_ptr($self->{$functioname})) ; # Yep even scalars are returned as pointers
83             }
84             else { # Initialize property and Return the value
85 0 0         unless (exists $self->{$functioname}) { $self->{$functioname} = undef ; }
  0            
86 0           return $self->{$functioname} ;
87             }
88             }
89            
90 0 0   0 0   sub as_ptr { unless(ref $_[0]) { return \$_[0] ; } else { return $_[0] ; } } #
  0            
  0            
91            
92             sub sprout {
93 0     0 0   my $self = shift ;
94 0           my $newclass = shift ;
95 0           $self->{$newclass} = Class::GAPI->new() ;
96 0           return $self->{$newclass} ;
97             }
98            
99             sub clone { # Make a recursive copy of self, provided that subordinates also have "clone" functions
100 0     0 0   my $self = shift ;
101 0           my $class = ref($self) ;
102 0           my $twin = $class->new() ;
103 0           while(my ($key, $val) = each %$self) {
104 0 0         if (! ref($self->{$key})) {
    0          
105 0           $twin->{$key} = $val ;
106             } elsif(is_blessed($self->{$key})) {
107 0           my $block = ('$twin->{$key} = $val->clone();') ;
108 0           eval($block) ;
109             } else {
110 0           $twin->{$key} = $val ; # try and pass unblessed references
111             }
112             }
113 0           return $twin ;
114             }
115            
116             sub load { # Broadcast namespace down the tree.
117 0     0 0   my $self = shift ;
118 0           my @libs = @_ ;
119 0           foreach (@libs) {
120 0           my $block = join '', ('use ', $_, ';') ;
121 0           eval($block) ;
122             }
123 0           while(my ($key, $val) = each %$self) {
124 0 0         if (is_blessed($val)) {
125 0           my $block = '$val->load(@libs);' ;
126 0           eval($block) ;
127             }
128             }
129             }
130            
131             sub is_blessed { # Object detection.
132 0     0 0   my $val = shift ;
133 0 0         if (ref($val)) {
134 0           foreach('SCALAR','ARRAY','HASH','CODE','GLOB','REF','LVALUE','IO::Handle') {
135 0 0         if ($val =~ $_ ) { return 0 ; }
  0            
136             }
137 0           return 1 ;
138             }
139 0           return 0 ;
140             }
141            
142             sub overlay { # Convert a hash into a series of function calls.
143 0     0 0   my $self = shift ;
144 0 0         return undef if scalar(@_) % 2 ;
145 0           my %pairs = @_ ;
146            
147 0           while(my ($k, $v) = each %pairs) {
148 0           my $block = join "", ( '$self->', $k, '(', '$v', ');' ) ;
149 0           eval($block) ;
150 0 0         if ($@) {
151 0           my $class = ref($self) ;
152 0           warn ("$class is executing $block and throwing:\n $@\n XXXXXXXXXXXXXXXXXXX") ;
153             }
154             }
155 0           return $self ;
156             }
157            
158             sub warn_self { # pass ($self $string) to warn ($self $string 1) to intercept warn data.
159 0     0 0   my $self = shift ;
160 0           my $id = shift ;
161 0           my $class = ref($self) ;
162 0           my $cstring = "\n$id object $self in $class" ; # Class info
163 0           while(my ($k, $v) = each %$self) { $cstring .= "\n $k\-\>$v" ; }
  0            
164 0 0         unless (scalar(@_)) { warn $cstring ; }
  0            
165 0           else { return $cstring ; }
166             }
167            
168             1 ;
169            
170             ############### CODE ENDS HERE ##############################
171            
172             =head1 NAME
173            
174             Class::GAPI - Generic API, Base class with autoloaded methods, stub objects, cloning etc.
175            
176             =head1 SYNOPSIS
177            
178             package Guppy ;
179            
180             use Class::GAPI ; # All of its cool stuff
181             our @ISA = qw(Class::GAPI) ; # is now in our namespace
182            
183             our @Children = qw(Class::GAPI::Fin Class::List::Eyeballs CGI) ; # Autoconstruct Subordinates
184             our @Default_Properties = qw(scaly small sushi) ; # Call at constructor time
185            
186             use strict ;
187            
188             sub _init { # Last stage of initialization
189             my $self = shift ;
190             $self->fillet(1) if defined $self->{'sushi'}; # sushi exists but is undefined
191             return 1;
192             }
193             1 ;
194            
195             package Petstore ;
196             use Guppy ;
197             my $pet = Guppy->new(color => 'orange', price => '.50', small => 1, -sushi => 1) ; # envoke these functions
198             $pet->Eyeballs->[0] = "left" ; # Access a special list subclass
199             $pet->Eyeballs->[1] = "right" ; #
200             $pet->Fin->dorsal("polkadot") ; # Access a subordinate Class::GAPI object
201             $pet->Fin->tail("orange") ; #
202            
203             =head1 DESCRIPTION
204            
205             This is a foundation class. It is intended to be inhertied and used as a framework for other
206             objects. This module features autoloaded methods (set+get as one method), three styles of
207             initialization, tools for handling stub objects, and cloning. It is particularly well suited
208             to handling record list type structures, deeply nested trees and those on-the-fly data structures
209             that give Perl a reputation as being a language of line noise. GAPI breaks a few rules and
210             create a few others. Overall it just makes coding complex nested data structures a heck of a lot
211             easier.
212            
213             =head1 AUTOLOADED METHODS
214            
215             Probably the most used part of this module is the autoloaded methods. One can access them from
216             a few places. First by constructing the widget with a hash
217            
218             my $pet = Guppy->new(foo => "bar") ;
219            
220             This is the same as saying:
221            
222             my $pet = Guppy->new() ;
223             $pet->foo("bar") ;
224            
225             which is the same thing as saying:
226            
227             my $pet = Guppy->new() ;
228             $pet->{'foo'} = 'bar' ;
229            
230             So all methods are autoloaded. A side effect is that typo'd function calls
231             generally will not cause a crash, but rather quitely create an additional
232             property. This can also be viewed as a feature, in that you can call nonexistant
233             functions in GAPI objects, thereby allowing you to write you code a bit more top-down
234             and it will be more tolerable of things you haven't added yet.
235            
236             All autoloaded methods add properties, never deleting them. To undefine something
237             call it as a hash. (the variable "_init" is reserved and does not autoload,
238             you'll see why later)
239            
240             undef $pet->{'foo'} ; # no foo for you
241             delete $pet->{'foo'} ; # de-exist foo.
242            
243             Passing a hash or array to a function returns a reference to the respective type, as does just
244             calling an empty function on a property that contains a hash or array. And they may be constructed
245             on the fly. So you can:
246            
247             my $hashref = $pet->magician(tophat => 'bunny') ;
248            
249             But don't do this. Forget I mentioned it. Instead use the sprout() function
250             which is GAPI for creating GAPI based subclasses. sprout()ed classes will
251             then also support autoloaded methods and other GAPI functions.
252            
253             $pet->sprout('magician', tophat => 'bunny') ; # $pet->{'magician'} is now a Class::GAPI object
254            
255             my $wascallywabit = $pet->magician->tophat() ; # get the rabbit
256             $pet->magician->tophat('dove') ; # replace it with a dove
257            
258             Now, back to the constructor:
259            
260             my $pet = Guppy->new(foo => "bar") ;
261            
262             This does not just set $pet->{'foo'} to "bar", it invoke the function 'foo' on "bar", and
263             the autoloaded function is what does the set/get. So it is important to note that one can preempt
264             this behavior simply by defining a function as follows:
265            
266             sub foo {
267             my $self = shift ;
268             my $bar = shift ;
269             print "a guppy walks into a $bar and says: Ouch.\n" ;
270             }
271            
272             =head1 OBJECT INITIALIZATION
273            
274             Class::GAPI has three stages of initialization at constructor time. The first which we just
275             discussed is by calling passed arguments as functions. The second is by evaluating two class
276             wide predefined arrays. They are:
277            
278             our @Default_Properties = qw(scaly small sushi) ; # execute some functions during new()
279             our @Children = qw(Class::GAPI::Fin Class::List::Eyeballs) ; # make some branches on our tree
280            
281             @Default_Properties is easy. Anything named here is called just as if it was passed as an
282             option with an undefined value. So the example above is the same as:
283            
284             my $pet = Guppy->new(scaly => undef, small => undef, sushi => undef) ;
285            
286             @Default_Properties is not used that often, in that the other Initialization stages can
287             do more than @Default_Properties. It is handy from time to time when you want to add
288             something complicated to the objects initialization and don't need to pass any special
289             arguments. (Like I said, rarely used) It is also trumped by any same-named passed option
290             pair from stage 1. So you you can define this as a hail marry for any function that should
291             be run at constructor time, even if the caller doesn't send an option pair.
292            
293             @Children is a list of subordinate objects to call ->new() on at constructor time. This allows
294             Class::GAPI based objects to include other classes in a sem-codeless fashion. Just "use" something
295             and stick it in Children, and you will get one built. (No options will be passed, but it will
296             built.) So for example you can do this:
297            
298             package Guppy ;
299            
300             use CGI ;
301             use Class::GAPI ;
302             our @ISA = qw(Class::GAPI) ;
303             our @Children = qw(CGI) ;
304             1 ;
305            
306             Which will then allow you to do this:
307            
308             my $pet = Guppy->new() ;
309             my $SwimTowardstheLight = $pet->CGI->param("fishhook") ; # Extract CGI parameter "fishhook"
310            
311             Class::GAPI will always use the right-most namespace fragment as the option in the option => value pair. (This may
312             cause a namespace conflict from time to time, in those cases just use the third stage _init instead.) So for example:
313            
314             package SpyGuppy ;
315             use Crypt::CBC ; # block handler
316             use Crypt::DES ; # Encryption Algorythm.
317             use Class::GAPI ;
318             our @ISA = qw(Class::GAPI) ;
319             our @Children = qw(Crypt::CBC Crypt::DES) ;
320             1 ;
321            
322             and then do:
323            
324             my $pet = SpyGuppy->new() ;
325             $pet->CBC->something() ;
326             $pet->DES->somethingelse() ;
327            
328             @Children also conveiniently has 2 special class names. Class::GAPI::Foo, and Class::List::Foo. In
329             this case "Foo" can be anything you like, and will correspondingly be used to create a
330             sprout()ed object. Note that Class::GAPI::Foo is a a sprouted hash, while Class::List::Foo
331             is a sprouted array. This is very convenient for making lists of objects. The technique below can be used
332             to quickly create a variety of styles of record manager classes.
333            
334            
335             package Guppy::School ;
336             use Guppy ;
337             our @ISA = qw(Guppy) ; # We are derived from a Guppy, which is derived from a GAPI
338             our @Children = qw(Class::List::School) ; # $self->{'School'} is now an array
339            
340             sub doSpawn { # Add a new Guppy Object
341             my $self = shift ;
342             my $fish = Guppy->new() ;
343             push @{$self->School()}, $fish ;
344             }
345            
346             sub fishNet { # Get a specific Guppy object
347             my $self = shift ;
348             my $n = shift ;
349             my $fish = $self->School->[$n] ;
350             return($fish) ;
351             }
352             1 ;
353            
354             The third stage of initialization is by defining a local &_init subroutine. This gets called after everything else. So if one desires to
355             do something with passed variables after the class is blessed, this is where to do it. If you call an autoloaded function here, it takes place
356             after autoloaded functions from ->new(), and Default_Properties. So you do have access to data passed or processed during invokation.
357            
358             passed at invokation:
359            
360             package Guppy ;
361             use Class::GAPI ;
362             our @ISA = (Class::GAPI);
363             use strict ;
364            
365             sub _init {
366             my $self = shift ;
367             $self->chopchopchop() if $self->sushi() && $self->filet() ;
368             }
369             1 ;
370            
371             package PetShop ;
372             use Guppy ;
373            
374             my $pet = Guppy->new(-sushi => 0, -filet => undef) ;
375             my $lunch = Guppy->new(-sushi => 1, -filet => 1) ;
376            
377            
378             In this case the execution of method chopchopchop would occur
379             in the case of lunch but not in the case of pet.
380            
381             =head1 OTHER FUNCTIONS
382            
383             Cloning is supported for Class::GAPI objects and any subordinate objects based on Class::GAPI
384             or that Inherit Class::GAPI. This includes Class::List objects. This is function is eval()d, so it
385             will not crash if you have other stuff in their, just don't expect that other stuff copy.
386            
387             my $twin = $pet->clone(); # Make the FDA nervous
388            
389             The overlay() function allows one to execute a block of functions by passing hash. This is equivilant
390             to what happens when constructed with new(). This is typically usefull when you want to copy a hash
391             into several objects as you might in a record table:
392            
393             package Guppy::School ;
394             use Guppy ;
395             our @ISA = qw(Guppy) ; # We are derived from a Guppy, which is derived from a GAPI
396             our @Children = qw(Class::List::School) ; # $self->{'School'} is now an array
397            
398             sub doSpawn { # Add a new Guppy Object
399             my $self = shift ;
400             my $fish = Guppy->new(@_) ; # Pass options pairs to the new fish
401             push @{$self->School()}, $fish ;
402             }
403            
404             sub fishGrow { # Add a block of options like so: fishGrow(2, foo => 'bar') ;
405             my $self = shift ;
406             my $n = shift ;
407             $self->School->[$n]->overlay(@_);
408             return($fish) ;
409             }
410             1 ;
411            
412             The warn_self() function is pretty much what it sounds like. You can call it at any level with
413             a tree of nested GAPI and it will produce a table of the object as a warning. Obviously this
414             handy for debugging:
415            
416             $self->warn_self() ;
417             $self->Foo->Bar->warn_self() ;
418            
419             =head1 NOTES
420            
421             It is worth noting that GAPI uses a lot of eval() calls. So it is fairly slow. Also special
422             care should be given to using this module in CGI because of that. You should probably
423             read the code and understand how the constructor works before even considering using this
424             thing in cgi code. Consider yourself warned.
425            
426             This was written on an Win32 box running cygwin and Activestate, and it works on both with Perl 5.8.
427             I expect it should work with anything later than 5.6.1, but It hasn't been tested.
428            
429             Autoloaded methods tend to cause silent failure modes. Essentially typos that would have
430             normally crashed perl will often just end up creating a dangling property somewhere.
431             Use $self->warn_self() to take snapshots of objects if something is not getting properly
432             populated. If you see two similarly named properties, you've found the culprit.
433            
434             No animals were harmed in the development of this module.
435            
436             =head1 AUTHOR
437            
438             Matthew Sibley
439             matt@itoperators.com
440            
441             =head1 COPYRIGHT AND LICENCE
442            
443             Copyright (C) 2005 Crosswire Industries Inc. (http://www.itoperators.com)
444            
445             This library is free software; you can redistribute it and/or modify
446             it under the same terms as Perl itself, either Perl version 5.8.6 or,
447             at your option, any later version of Perl 5 you may have available.
448            
449             =cut
450