File Coverage

blib/lib/Class/MethodVars.pm
Criterion Covered Total %
statement 99 360 27.5
branch 18 202 8.9
condition 6 94 6.3
subroutine 20 25 80.0
pod 0 4 0.0
total 143 685 20.8


line stmt bran cond sub pod time code
1             package Class::MethodVars;
2 1     1   1145 use warnings;
  1         3  
  1         52  
3 1     1   8 use strict;
  1         2  
  1         43  
4              
5 1     1   14204 use NEXT;
  1         3785  
  1         1895  
6              
7             our $VERSION = '1.'.qw $Rev: 133 $[1];
8              
9             our %Configs; # Needs to be accessible to Class::Framework
10             my %OptionsMap = (
11             '^args'=>'hatargs',
12             'hatargs'=>'hatargs',
13             'varargs'=>'varargs',
14             '^fields'=>'hatfields',
15             'hatfields'=>'hatfields',
16             'varfields'=>'varfields',
17             '^this'=>'hatthis',
18             'hatthis'=>'hatthis',
19             'varthis'=>'varthis',
20             'subthis'=>'subthis',
21             '^class'=>'hatclass',
22             'hatclass'=>'hatclass',
23             'varclass'=>'varclass',
24             'subclass'=>'subclass',
25              
26             'debug'=>'debug',
27             );
28             my %DefaultOptions = (
29             hatargs=>1,
30             hatfields=>1,
31             subthis=>1,
32             subclass=>1,
33             # No varthis and varclass because that causes an implicit use vars which is bad for a default.
34             );
35              
36             sub __DefaultConfigs() {
37             return {
38 1     1   16 fieldhatprefix=>"",
39             fieldvarprefix=>"",
40             class=>"__CLASS__",
41             this=>"this",
42             fields=>[],
43             rwfields=>[],
44             rofields=>[],
45             wofields=>[],
46             hiddenfields=>[],
47             options=>{ %DefaultOptions }
48             };
49             }
50              
51             sub import {
52 1     1   1 shift; # You should NEVER be @ISA = "Class::MethodVars"
53 1         3 my $package = caller;
54 1 50       6 if ($Configs{$package}) {
55 0         0 require Carp;
56 0         0 Carp::croak "Double import into this package!";
57             }
58 1   33     7 my $Config = $Configs{$package}||=__DefaultConfigs;
59 1         3 my $cpos = 0;
60 1         6 while (@_) {
61 0         0 my $cmd = shift;
62 0         0 $cpos++;
63 0 0 0     0 if ($cpos == 1 and ref($cmd)) {
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
64 0         0 push(@{$Config->{rwfields}},@$cmd);
  0         0  
65             } elsif ($cmd eq '-this') {
66 0         0 $Config->{this} = shift;
67             } elsif ($cmd eq '-class') {
68 0         0 $Config->{class} = shift;
69             } elsif ($cpos == 1 and not $cmd=~/^[\+-]/) {
70 0         0 push(@{$Config->{rwfields}},$cmd);
  0         0  
71 0   0     0 while (@_ and not $_[0]=~/^-/) {
72 0         0 push(@{$Config->{rwfields}},shift);
  0         0  
73             }
74             } elsif ($cmd eq '-fields' or $cmd eq '-field') {
75 0 0 0     0 if (@_ and ref($_[0])) {
76 0         0 push(@{$Config->{rwfields}},@{shift()});
  0         0  
  0         0  
77             } else {
78 0   0     0 while (@_ and not $_[0]=~/^-/) {
79 0         0 push(@{$Config->{rwfields}},shift);
  0         0  
80             }
81             }
82             } elsif ($cmd =~ /^-(r[wo]|wo|hidden)fields?$/) {
83 0         0 my $fieldtype = lc $1."fields";
84 0 0 0     0 if (@_ and ref($_[0])) {
85 0         0 push(@{$Config->{$fieldtype}},@{shift()});
  0         0  
  0         0  
86             } else {
87 0   0     0 while (@_ and not $_[0]=~/^-/) {
88 0         0 push(@{$Config->{$fieldtype}},shift);
  0         0  
89             }
90             }
91             } elsif ($cmd eq '-fieldvarprefix') {
92 0         0 $Config->{fieldvarprefix} = shift;
93             } elsif ($cmd eq '-fieldhatprefix') {
94 0         0 $Config->{fieldhatprefix} = shift;
95             } elsif ($cmd=~/^([+-])(.*)$/ and $OptionsMap{$2}) {
96 0         0 my ($toggle,$option) = ($1,$2);
97 0 0 0     0 if ($toggle eq '-' and @_ and $_[0]=~/^(?:[10]|ON|OFF|TRUE|FALSE)$/i) { # 'it's okay - you would have -fields if expecting field names...
      0        
98 0         0 $toggle = shift;
99             }
100 0         0 $toggle = grep { lc($toggle) eq lc($_) } qw( - 1 ON TRUE );
  0         0  
101 0         0 $Config->{options}->{$option} = $toggle;
102             } else {
103 0         0 require Carp;
104 0         0 local $Carp::CarpLevel = 1;
105 0         0 Carp::croak "I don't know what to do with \"$cmd\"";
106             }
107             }
108 1         2 $Config->{fields} = [ Class::MethodVars::_Private::unique(@{$Config->{rwfields}},@{$Config->{rofields}},@{$Config->{wofields}},@{$Config->{hiddenfields}}) ];
  1         3  
  1         2  
  1         2  
  1         6  
109             #FIXME: I should probably whine if people use the same field name for a -rofield as a -wofield (for example).
110 1         3 my @bad_field_names = grep { not /\A\w+\z/ } @{$Config->{fields}};
  0         0  
  1         4  
111 1 50       4 if (@bad_field_names) {
112 0         0 require Carp;
113 0 0       0 if (eval { require Lingua::EN::Inflect; 1 }) {
  0         0  
  0         0  
114 0         0 Carp::croak Lingua::EN::Inflect::inflect("Invalid field PL(name,".@bad_field_names."): ").join(", ",@bad_field_names);
115             } else {
116 0         0 local $" = ", ";
117 0         0 Carp::croak "Invalid field name(s): @bad_field_names";
118             }
119             }
120 1         4 $Config->{allfields} = [ Class::MethodVars::_Private::findBaseFields($package) ]; # This will pull in self and base fields.
121 1 50       6 $Config->{options}->{subthis} = 0 if $Config->{this}=~/^\$?_$/;
122 1 50       15 $Config->{options}->{subclass} = 0 if $Config->{class}=~/^\$?_$/;
123 1         67 eval 'unshift @'.$package.'::ISA,q('.__PACKAGE__.'::_ATTRS)';
124 1 50       41 eval 'package '.$package.'; sub '.$Config->{this}.'();' if $Config->{options}->{subthis};
125 1 50       37 eval 'package '.$package.'; sub '.$Config->{class}.'();' if $Config->{options}->{subclass};
126 1         2 my @varnames;
127 1 50       5 push(@varnames,$Config->{this}) if $Config->{options}->{varthis};
128 1 50       5 push(@varnames,$Config->{class}) if $Config->{options}->{varclass};
129 1 50       4 push(@varnames,map { $Config->{fieldvarprefix}.$_ } @{$Config->{fields}}) if $Config->{options}->{varfields};
  0         0  
  0         0  
130             # Can't do varargs, because I don't know what the args will be in advance!
131 1 50       3 eval 'package '.$package.'; use vars qw('.join(" ",map { s{^(?![\%\$\@])}{\$}; $_ } @varnames).');' if @varnames;
  0         0  
  0         0  
132 1         153 1;
133             }
134              
135             sub Method {
136 0     0 0 0 my ($package, $symbol, $referent, $attr, $data, $stage) = @_;
137 1     1   9 no warnings 'redefine';
  1         3  
  1         55  
138 1     1   5 no strict 'refs';
  1         2  
  1         1717  
139 0         0 if (0 and defined($symbol) and *{$symbol}{NAME} eq 'ANON') {
140             # ^--- DISABLED!
141             # Darn, need to find a better symbol. (Probably :Method is not on the first prototype...)
142             #XXX: This doesn't work. Even $referent points to the wrong thing :(
143             # Can't figure out true name, can't figure out true reference = can't put Humpty Dumpty back together again :(
144             ($symbol) = eval 'grep { *{$_}{NAME} ne "ANON" } grep { *{$_}{CODE} and *{$_}{CODE} eq $referent } values %'.$package.'::';
145             }
146 0 0 0     0 if (not defined($symbol) or *{$symbol}{NAME} eq 'ANON') {
  0         0  
147 0         0 require Carp;
148 0         0 local $Carp::CarpLevel = 3;
149 0 0       0 if ($^S) {
150 0         0 Carp::croak "Unable to identify the name of subroutine at $referent. You appear to be calling this inside an eval. This is a known bug - please \"use\" this module at the start of your application.\n";
151             } else {
152 0         0 Carp::croak "Unable to identify the name of subroutine at $referent. Please ensure :Method is on the first prototype.\n";
153             }
154             # Don't try and apply the magic to a closure, this attribute is not set up for it.
155             }
156 0         0 my ($self,@args);
157 0   0     0 my $Config = $Configs{$package}||__DefaultConfigs;
158 0 0       0 $data = "." unless defined $data;
159 0 0       0 if (ref $data) {
160 0         0 ($self,@args) = @$data;
161             } else {
162 0         0 ($self,@args) = split(/[,\s]+/,$data);
163             }
164 0 0       0 $self = $Config->{this} if $self eq '.';
165 0         0 $self=~s{^\$}{};
166 0 0       0 if ($self eq "_") {
167 0         0 $self = "";
168 0 0       0 $self.= 'local $_ = (ref($_[0]) and UNIVERSAL::isa($_[0],q('.$package.')))?shift:$_; my $me = $_;' if $Config->{options}->{varthis};
169 0 0       0 $self.= 'my $me = shift;' unless $Config->{options}->{varthis};
170 0 0       0 $self.= 'local ${^__} = $me;' if $Config->{options}->{hatthis};
171 0 0       0 $self.= 'local $_ = (ref($_[0]) and UNIVERSAL::isa($_[0],q('.$package.')))?shift:$_; my $me = $_;' unless $self;
172             } else {
173 0         0 my $selfname = $self;
174 0         0 $self = 'my $me = shift;';
175 0 0       0 $self.='local *'.$package.'::'.$selfname.' = sub { $me };' if $Config->{options}->{subthis};
176 0 0       0 $self.='local ${^_'.$selfname.'} = $me;' if $Config->{options}->{hatthis};
177 0 0       0 $self.='local *'.$package.'::'.$selfname.' = \$me;' if $Config->{options}->{varthis};
178             #$self = 'my $me = $_[0]; local *'.$package.'::'.$self.' = sub { $me }; local ${^_'.$self.'} = $_[0]; local *'.$package.'::'.$self.' = \shift;'
179             }
180 0         0 if (0) { # This is all too late. Needed to do it in BEGIN if I was going to do it at all.
181             my @varargs = @args;
182             eval 'package '.$package.'; use vars qw('.join(" ",grep { $_ ne "_" } map { s{^(?![\%\$\@])}{\$}; $_ } @varargs).');' if @varargs;
183             }
184 0         0 for (@args) {
185 0         0 my $aname = $_;
186 0 0 0     0 if ($aname=~/^\@(\w+)$/ and $aname eq $args[-1]) {
187 0         0 $aname = $1;
188 0         0 $_ = ''; # This alters @args!
189 0 0       0 $_.= 'local @{^_'.$aname.'} = @_;' if $Config->{options}->{hatargs};
190 0 0       0 $_.= 'my @args = @_; local *'.$package.'::'.$aname.' = \@args;' if $Config->{options}->{varargs};
191 0         0 next;
192             }
193 0 0 0     0 if ($aname=~/^\%(\w+)$/ and $aname eq $args[-1]) {
194 0         0 $aname = $1;
195 0         0 $_ = ''; # This alters @args!
196 0 0       0 $_.= 'local %{^_'.$aname.'} = @_;' if $Config->{options}->{hatargs};
197 0 0       0 $_.= 'my %args = @_; local *'.$package.'::'.$aname.' = \%args;' if $Config->{options}->{varargs};
198 0         0 next;
199             }
200 0         0 $aname=~s{^\$}{}; # Allow, but ignore leading dollar sigal.
201 0 0       0 unless ($aname=~/^\w+$/) {
202 0         0 require Carp;
203 0         0 local $Carp::CarpLevel = 1;
204 0         0 Carp::croak "Bad argument name: $aname (@args)";
205             }
206 0         0 $_ = ''; # This alters @args!
207 0 0       0 if ($aname eq '_') {
208 0         0 $_.='local $_ = $_[0];';
209             } else {
210 0 0       0 $_.='local ${^_'.$aname.'} = $_[0];' if $Config->{options}->{hatargs};
211 0 0       0 $_.='local *'.$package.'::'.$aname.' = \$_[0];' if $Config->{options}->{varargs};
212             }
213 0         0 $_.='shift;';
214             }
215 0         0 my @fields = @{$Config->{allfields}};
  0         0  
216 0         0 for (@fields) {
217 0         0 my $fname = $_;
218 0         0 $_ = "";
219 0 0       0 $_.='local *{^_'.$Config->{fieldhatprefix}.$fname.'} = \$me->{q('.$fname.')};' if $Config->{options}->{hatfields};
220 0 0       0 $_.='local *'.$package.'::'.$Config->{fieldvarprefix}.$fname.' = \$me->{q('.$fname.')};' if $Config->{options}->{varfields};
221             }
222 0         0 my $proto = prototype $referent;
223 0 0       0 if (defined $proto) {
224 0         0 $proto = "($proto)";
225             } else {
226 0         0 $proto = "";
227             }
228 0 0       0 if ($Config->{options}->{debug}) {
229 0         0 my $subdecl = "sub ".$package."::".(*{$symbol}{NAME}).$proto." {\n\t$self\n\t@fields\n\t@args\n\t\$referent->(\@_)\n};\n";
  0         0  
230 0         0 $subdecl=~s{;(?!\n)\s*}{;\n\t}gs;
231 0         0 warn $subdecl;
232             }
233 0         0 local $@;
234 0         0 my $subdecl = "package $package; sub $proto {
235             $self
236             @fields
237             @args
238             \$referent->(\@_)
239             };";
240             #*{$symbol} = eval $subdecl;
241             #die $@."\n$subdecl" if $@;
242 0         0 my $subref = eval $subdecl;
243 0 0       0 die "Failure to create sub: ".$@."\n$subdecl" if $@;
244 0         0 my ($sympkg,$symname) = (*{$symbol}{PACKAGE},*{$symbol}{NAME});
  0         0  
  0         0  
245             # eval '*{$symbol} = $subref; 1' or warn "Assigning symbol: $@"; # I don't know why this doesn't work any more.
246 0 0       0 eval '$'.$sympkg.'::{$symname} = $subref' or die "Failed to assign symbol *{$symbol}{PACKAGE}::*{$symbol}{NAME}: $@";
247             }
248              
249             sub ClassMethod {
250 0     0 0 0 my ($package, $symbol, $referent, $attr, $data, $stage) = @_;
251 1     1   7 no warnings 'redefine';
  1         2  
  1         38  
252 1     1   13 no strict 'refs';
  1         2  
  1         1755  
253 0         0 if (0 and defined($symbol) and *{$symbol}{NAME} eq 'ANON') {
254             # ^--- DISABLED!
255             # Darn, need to find a better symbol. (Probably :Method is not on the first prototype...)
256             #XXX: This doesn't work. Even $referent points to the wrong thing :(
257             # Can't figure out true name, can't figure out true reference = can't put Humpty Dumpty back together again :(
258             ($symbol) = eval 'grep { *{$_}{NAME} ne "ANON" } grep { *{$_}{CODE} and *{$_}{CODE} eq $referent } values %'.$package.'::';
259             }
260 0 0 0     0 if (not defined($symbol) or *{$symbol}{NAME} eq 'ANON') {
  0         0  
261 0         0 require Carp;
262 0         0 local $Carp::CarpLevel = 3;
263 0 0       0 if ($^S) {
264 0         0 Carp::croak "Unable to identify the name of subroutine at $referent. You appear to be calling this inside an eval. This is a known bug - please \"use\" this module at the start of your application.\n";
265             } else {
266 0         0 Carp::croak "Unable to identify the name of subroutine at $referent. Please ensure :Method is on the first prototype.\n";
267             }
268             # Don't try and apply the magic to a closure, this attribute is not set up for it.
269             }
270 0         0 my ($class,@args);
271 0   0     0 my $Config = $Configs{$package}||__DefaultConfigs;
272 0 0       0 $data = "." unless defined $data;
273 0 0       0 if (ref $data) {
274 0         0 ($class,@args) = @$data;
275             } else {
276 0         0 ($class,@args) = split(/[,\s]+/,$data);
277             }
278 0 0       0 $class = $Config->{class} if $class eq '.';
279 0         0 my $self = $Config->{this};
280 0         0 $class=~s{^\$}{};
281 0         0 $self=~s{^\$}{};
282 0 0       0 if ($self eq "_") {
283 0         0 $self = "";
284 0 0       0 $self.= 'local $_ = (ref($_[0]) and UNIVERSAL::isa($_[0],q('.$package.')))?shift:$_; my $me = $_;' if $Config->{options}->{varthis};
285 0 0       0 $self.= 'my $me = shift;' unless $Config->{options}->{varthis};
286 0 0       0 $self.= 'local ${^__} = $me;' if $Config->{options}->{hatthis};
287 0 0       0 $self.= 'local $_ = (ref($_[0]) and UNIVERSAL::isa($_[0],q('.$package.')))?shift:$_; my $me = $_;' unless $self;
288             } else {
289 0         0 my $selfname = $self;
290 0         0 $self = 'my $me = shift;';
291 0 0       0 $self.='local *'.$package.'::'.$selfname.' = sub { $me };' if $Config->{options}->{subthis};
292 0 0       0 $self.='local ${^_'.$selfname.'} = $me;' if $Config->{options}->{hatthis};
293 0 0       0 $self.='local *'.$package.'::'.$selfname.' = \$me;' if $Config->{options}->{varthis};
294             #$self = 'my $me = $_[0]; local *'.$package.'::'.$self.' = sub { $me }; local ${^_'.$self.'} = $_[0]; local *'.$package.'::'.$self.' = \shift;'
295             }
296 0 0       0 if ($class eq "_") {
297 0         0 $class = "";
298 0 0       0 $class.= 'local $_ = ref($me)||$me;' if $Config->{options}->{varthis};
299 0 0       0 $class.= 'local ${^__} = $me;' if $Config->{options}->{hatthis};
300 0 0       0 $class.= 'local $_ = ref($me)||$me;' unless $class;
301             } else {
302 0         0 my $classname = $class;
303 0         0 $class = 'my $class = ref($me)||$me;';
304 0 0       0 $class.='local *'.$package.'::'.$classname.' = sub { $class };' if $Config->{options}->{subclass};
305 0 0       0 $class.='local ${^_'.$classname.'} = $class;' if $Config->{options}->{hatclass};
306 0 0       0 $class.='local *'.$package.'::'.$classname.' = \$class;' if $Config->{options}->{varclass};
307             }
308 0         0 for (@args) {
309 0         0 my $aname = $_;
310 0 0 0     0 if ($aname=~/^\@(\w+)$/ and $aname eq $args[-1]) {
311 0         0 $aname = $1;
312 0         0 $_ = '';
313 0 0       0 $_.= 'local @{^_'.$aname.'} = @_;' if $Config->{options}->{hatargs};
314 0 0       0 $_.= 'my @args = @_; local *'.$package.'::'.$aname.' = \@args;' if $Config->{options}->{varargs};
315 0         0 next;
316             }
317 0 0 0     0 if ($aname=~/^\%(\w+)$/ and $aname eq $args[-1]) {
318 0         0 $aname = $1;
319 0         0 $_ = '';
320 0 0       0 $_.= 'local %{^_'.$aname.'} = @_;' if $Config->{options}->{hatargs};
321 0 0       0 $_.= 'my %args = @_; local *'.$package.'::'.$aname.' = \%args;' if $Config->{options}->{varargs};
322 0         0 next;
323             }
324 0         0 $aname=~s{^\$}{}; # Allow, but ignore leading dollar sigal.
325 0 0       0 unless ($aname=~/^\w+$/) {
326 0         0 require Carp;
327 0         0 local $Carp::CarpLevel = 1;
328 0         0 Carp::croak "Bad argument name: $aname (@args)";
329             }
330 0         0 $_ = ''; # This alters @args!
331 0 0       0 if ($aname eq '_') {
332 0         0 $_.='local $_ = $_[0];';
333             } else {
334 0 0       0 $_.='local ${^_'.$aname.'} = $_[0];' if $Config->{options}->{hatargs};
335 0 0       0 $_.='local *'.$package.'::'.$aname.' = \$_[0];' if $Config->{options}->{varargs};
336             }
337 0         0 $_.='shift;';
338             }
339             # Class methods don't get fields (you have the {this} variable).
340             # my @fields = @{$Config->{fields}};
341             # for (@fields) {
342             # my $fname = $_;
343             # $_ = "";
344             # $_.='local ${^_'.$fname.'} = $me->{q('.$fname.')};' if $Config->{options}->{hatfields};
345             # $_.='local *'.$package.'::'.$fname.' = \$me->{q('.$fname.')};' if $Config->{options}->{varfields};
346             # }
347 0         0 my $proto = prototype $referent;
348 0 0       0 if (defined $proto) {
349 0         0 $proto = "($proto)";
350             } else {
351 0         0 $proto = "";
352             }
353 0 0       0 if ($Config->{options}->{debug}) {
354 0         0 my $subdecl = "sub ".$package."::".(*{$symbol}{NAME}).$proto." {\n\t$self\n\t$class\n\t@args\n\t\$referent->(\@_)\n};\n";
  0         0  
355 0         0 $subdecl=~s{;(?!\n)\s*}{;\n\t}gs;
356 0         0 warn $subdecl;
357             }
358 0         0 local $@;
359 0         0 my $subdecl = "package $package; sub $proto {
360             $self
361             $class
362             @args
363             \$referent->(\@_)
364             };";
365 0         0 my $subref = eval $subdecl;
366 0 0       0 die "Failure to create sub: ".$@."\n$subdecl" if $@;
367 0         0 my ($sympkg,$symname) = (*{$symbol}{PACKAGE},*{$symbol}{NAME});
  0         0  
  0         0  
368             # eval '*{$symbol} = $subref; 1' or warn "Assigning symbol: $@"; # I don't know why this doesn't work any more.
369 0 0       0 eval '$'.$sympkg.'::{$symname} = $subref' or die "Failed to assign symbol *{$symbol}{PACKAGE}::*{$symbol}{NAME}: $@";
370             # *{$symbol} = eval "package $package; sub $proto {
371             # $self
372             # $class
373             # @args
374             # \$referent->(\@_)
375             # };";
376             }
377              
378             our %Methods;
379             our %ClassMethods;
380             our %symcache;
381              
382             sub findsym($$) {
383 0     0 0 0 my ($pkg,$ref) = @_;
384 0 0 0     0 return $symcache{$pkg,$ref} if $symcache{$pkg,$ref} and *{$symcache{$pkg,$ref}}{CODE} eq $ref;
  0         0  
385 1     1   6 no strict 'refs';
  1         2  
  1         537  
386 0         0 for (values %{$pkg."::"}) {
  0         0  
387 0 0 0     0 return $symcache{$pkg,$ref} = $_ if *{$_}{CODE} and *{$_}{CODE} eq $ref;
  0         0  
  0         0  
388             }
389 0         0 return undef; # Don't cache incase there is a better way to get it later.
390             }
391              
392             sub make_methods($);
393             sub make_methods($) {
394 0     0 0 0 my $pkg = shift;
395             # My call line looks like this to maintain Attribute::Handlers compatibility.
396             # unfortunately I cannot use Attribute::Handlers because it fails to trigger
397             # if you do "eval 'use mypkg;'" and it can't find the symbols.
398             #my ($package, $symbol, $referent, $attr, $data, $stage);
399 0         0 my @oa = $pkg->NEXT::ELSEWHERE::ancestors;
400 0   0     0 1 while @oa and shift(@oa) ne $pkg;
401 0   0     0 1 while @oa and shift(@oa) ne "Class::MethodVars::_ATTRS";
402 0         0 my $next;
403 0 0       0 if (@oa) {
404 0         0 make_methods(shift(@oa));
405             }
406 0 0       0 for my $data (@{$ClassMethods{$pkg}||[]}) {
  0         0  
407 0         0 my ($package,$ref,$args) = @$data;
408 0         0 my $sym = findsym($package,$ref);
409 0         0 ClassMethod($package,$sym,$ref,"ClassMethod",$args,"import");
410             }
411 0 0       0 for my $data (@{$Methods{$pkg}||[]}) {
  0         0  
412 0         0 my ($package,$ref,$args) = @$data;
413 0         0 my $sym = findsym($package,$ref);
414 0         0 Method($package,$sym,$ref,"ClassMethod",$args,"import");
415             }
416 0         0 delete $ClassMethods{$pkg};
417 0         0 delete $Methods{$pkg};
418             }
419              
420             {
421 1     1   5 no warnings 'void'; # "Too late to run INIT block"
  1         3  
  1         118  
422             INIT {
423             make_methods($_) for keys %ClassMethods;
424             make_methods($_) for keys %Methods;
425             };
426             }
427              
428             package Class::MethodVars::_ATTRS;
429 1     1   6 use warnings;
  1         2  
  1         36  
430 1     1   4 use strict;
  1         3  
  1         27  
431              
432 1     1   5 use NEXT; # Need this incase they used to inherit from someone else.
  1         1  
  1         745  
433              
434             sub import {
435 0     0   0 my ($spkg,@args) = @_;
436 0         0 my $tpkg = caller;
437 0 0       0 if ($spkg eq __PACKAGE__) {
438 0         0 require Carp;
439 0         0 Carp::croak "Don't do that.";
440             }
441 0         0 Class::MethodVars::make_methods($spkg);
442 0         0 my @oa = $_[0]->NEXT::ELSEWHERE::ancestors;
443 0   0     0 1 while @oa and shift(@oa) ne $tpkg;
444 0   0     0 1 while @oa and shift(@oa) ne __PACKAGE__;
445 0         0 my $next;
446 0 0 0     0 if (@oa and $next = $oa[0]->can("import")) {
447 0         0 goto &$next;
448             }
449             }
450              
451             sub MODIFY_CODE_ATTRIBUTES {
452 1     1   42 my ($pkg,$ref,@attrs) = @_;
453              
454             # I want to write this:
455             # my @oldattrs = @attrs;
456             # unless (eval q{ @attrs = $pkg->NEXT::DISTINCT::ACTUAL::MODIFY_CODE_ATTRIBUTES($ref,@attrs); 1 }) {
457             # @attrs = @oldattrs;
458             # }
459             # But "(eval)" can't call NEXT...MODIFY_CODE_ATTRIBUTES! So:
460 1         11 my @oa = $_[0]->NEXT::ELSEWHERE::ancestors;
461 1   66     43 1 while @oa and shift(@oa) ne caller;
462 1   33     5 1 while @oa and shift(@oa) ne __PACKAGE__;
463 1         2 my $next;
464 1 50 33     6 if (@oa and $next = $oa[0]->can("import")) {
465 0         0 @attrs = $pkg->$next($ref,@attrs);
466             }
467             # End whinge
468              
469 1         2 my @bad_attrs;
470             my @good_attrs;
471 1         3 for (@attrs) {
472 1 50       10 if (/\A(?:Class)?Method(?:\(.*)?\z/) {
473 1         4 push(@good_attrs,$_);
474             } else {
475 0         0 push(@bad_attrs,$_);
476             }
477             }
478 1 50       8 if (@good_attrs > 1) {
    50          
479 0         0 require Carp;
480 0         0 Carp::croak q{Please only use one of :Method or :ClassMethod for each method};
481             } elsif (@good_attrs) {
482 1         2 my $args;
483 1 50       4 $args = [split(/[\s,]/,$1)] if $good_attrs[0]=~/\((.*)\)\s*\z/;
484 1 50       7 if ($good_attrs[0]=~/\AClassMethod/) {
485 1   50     9 $ClassMethods{$pkg}||=[];
486 1         2 push(@{$ClassMethods{$pkg}},[ $pkg,$ref,$args ]);
  1         4  
487             } else {
488 0   0     0 $Methods{$pkg}||=[];
489 0         0 push(@{$Methods{$pkg}},[ $pkg,$ref,$args ]);
  0         0  
490             }
491             }
492 1         4 return @bad_attrs;
493             }
494              
495             package Class::MethodVars::_Private;
496 1     1   6 use warnings;
  1         3  
  1         25  
497 1     1   11 use strict;
  1         2  
  1         384  
498              
499             sub unique(@) {
500 2     2   6 my %u = map { $_=>$_ } @_;
  0         0  
501 2         7 return values %u;
502             }
503              
504             sub retrFields($) {
505 1     1   2 my $pkg = shift;
506 1 50       5 return () unless $Class::MethodVars::Configs{$pkg};
507 1 50       4 return () unless $Class::MethodVars::Configs{$pkg}->{fields};
508 1         2 return @{$Class::MethodVars::Configs{$pkg}->{fields}};
  1         3  
509             }
510              
511             sub findBaseFields($);
512             sub findBaseFields($) {
513 1     1   2 my $pkg = shift;
514 1         79 my @isa = eval '@'.$pkg.'::ISA';
515 1         3 my @fields;
516 1         3 for my $bpkg (@isa) {
517 0         0 push(@fields,findBaseFields($bpkg));
518             }
519 1         3 return unique @fields,retrFields $pkg;
520             }
521              
522             1;
523              
524             __END__