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
|
|
|
|
|
|
|
|