line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#======================================================================== |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Badger::Class::Methods |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# DESCRIPTION |
6
|
|
|
|
|
|
|
# Class mixin module for adding methods to a class. |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# AUTHOR |
9
|
|
|
|
|
|
|
# Andy Wardley |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
#======================================================================== |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
package Badger::Class::Methods; |
14
|
|
|
|
|
|
|
|
15
|
70
|
|
|
70
|
|
510
|
use Carp; |
|
70
|
|
|
|
|
133
|
|
|
70
|
|
|
|
|
9732
|
|
16
|
|
|
|
|
|
|
use Badger::Class |
17
|
|
|
|
|
|
|
version => 0.01, |
18
|
|
|
|
|
|
|
debug => 0, |
19
|
|
|
|
|
|
|
base => 'Badger::Base', |
20
|
|
|
|
|
|
|
import => 'class BCLASS', |
21
|
|
|
|
|
|
|
constants => 'DELIMITER ARRAY HASH PKG CODE', |
22
|
|
|
|
|
|
|
utils => 'is_object', |
23
|
|
|
|
|
|
|
exports => { |
24
|
|
|
|
|
|
|
hooks => { |
25
|
|
|
|
|
|
|
init => \&initialiser, |
26
|
70
|
|
|
|
|
317
|
map { $_ => [\&generate, 1] } |
|
490
|
|
|
|
|
2577
|
|
27
|
|
|
|
|
|
|
qw( accessors mutators get set slots hash auto_can ) |
28
|
|
|
|
|
|
|
}, |
29
|
|
|
|
|
|
|
}, |
30
|
|
|
|
|
|
|
messages => { |
31
|
|
|
|
|
|
|
no_target => 'No target class specified to generate methods for', |
32
|
|
|
|
|
|
|
no_type => 'No method type specified to generate', |
33
|
|
|
|
|
|
|
no_methods => 'No %s specified to generate', |
34
|
|
|
|
|
|
|
bad_method => 'Invalid %s method: %s', |
35
|
|
|
|
|
|
|
bad_type => 'Invalid method generator specified: %s', |
36
|
70
|
|
|
70
|
|
528
|
}; |
|
70
|
|
|
|
|
162
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# method aliases |
39
|
|
|
|
|
|
|
*get = \&accessors; |
40
|
|
|
|
|
|
|
*set = \&mutators; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
our $AUTOLOAD; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub generate { |
45
|
8
|
|
|
8
|
1
|
18
|
my $class = shift; |
46
|
8
|
|
50
|
|
|
15
|
my $target = shift |
47
|
|
|
|
|
|
|
|| return $class->error_msg('no_target'); |
48
|
8
|
|
50
|
|
|
17
|
my $type = shift |
49
|
|
|
|
|
|
|
|| return $class->error_msg('no_type'); |
50
|
8
|
|
50
|
|
|
14
|
my $methods = shift |
51
|
|
|
|
|
|
|
|| return $class->error_msg( no_methods => $type ); |
52
|
8
|
|
50
|
|
|
40
|
my $code = $class->can($type) |
53
|
|
|
|
|
|
|
|| return $class->error_msg( bad_type => $type ); |
54
|
|
|
|
|
|
|
|
55
|
8
|
|
|
|
|
10
|
$class->debug("generate($target, $type, $methods)") if DEBUG; |
56
|
|
|
|
|
|
|
|
57
|
8
|
|
|
|
|
21
|
$code->($class, $target, $methods); |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub accessors { |
61
|
258
|
|
|
258
|
1
|
804
|
my ($class, $target, $methods) = shift->args(@_); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
$target->import_symbol( |
64
|
|
|
|
|
|
|
$_ => $class->accessor($_) |
65
|
258
|
|
|
|
|
1027
|
) for @$methods; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub accessor { |
69
|
516
|
|
|
516
|
1
|
989
|
my ($self, $name) = @_; |
70
|
|
|
|
|
|
|
return sub { |
71
|
684
|
|
|
684
|
|
3258
|
$_[0]->{ $name }; |
72
|
516
|
|
|
|
|
2560
|
}; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub mutators { |
76
|
87
|
|
|
87
|
1
|
339
|
my ($class, $target, $methods) = shift->args(@_); |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
$target->import_symbol( |
79
|
|
|
|
|
|
|
$_ => $class->mutator($_) |
80
|
87
|
|
|
|
|
329
|
) for @$methods; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub mutator { |
84
|
92
|
|
|
92
|
1
|
220
|
my ($self, $name) = @_; |
85
|
|
|
|
|
|
|
return sub { |
86
|
|
|
|
|
|
|
# You wouldn't ever want to write a real subroutine like this. |
87
|
|
|
|
|
|
|
# But that's OK, because we're here to do it for you. You get |
88
|
|
|
|
|
|
|
# the efficiency without having to ever look at code like this: |
89
|
|
|
|
|
|
|
@_ == 2 |
90
|
|
|
|
|
|
|
? ($_[0]->{ $name } = $_[1]) |
91
|
55
|
100
|
|
55
|
|
367
|
: $_[0]->{ $name }; |
92
|
92
|
|
|
|
|
545
|
}; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub hash { |
96
|
1
|
|
|
1
|
1
|
2
|
my ($class, $target, $methods) = shift->args(@_); |
97
|
|
|
|
|
|
|
|
98
|
1
|
|
|
|
|
3
|
foreach (@$methods) { |
99
|
1
|
|
|
|
|
2
|
my $name = $_; # new lexical var for closure |
100
|
|
|
|
|
|
|
$target->import_symbol( |
101
|
|
|
|
|
|
|
$name => sub { |
102
|
|
|
|
|
|
|
# return hash ref when called without args |
103
|
8
|
100
|
|
8
|
|
23
|
return $_[0]->{ $name } if @_ == 1; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# return hash item when called with one non-ref arg |
106
|
7
|
100
|
100
|
|
|
36
|
return $_[0]->{ $name }->{ $_[1] } if @_ == 2 && ! ref $_[1]; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# add items to hash when called with hash ref or multiple args |
109
|
2
|
|
|
|
|
4
|
my $self = shift; |
110
|
2
|
100
|
66
|
|
|
13
|
my $items = @_ == 1 && ref $_[0] eq HASH ? shift : { @_ }; |
111
|
2
|
|
|
|
|
6
|
my $hash = $self->{ $name }; |
112
|
2
|
|
|
|
|
11
|
@$hash{ keys %$items } = values %$items; |
113
|
2
|
|
|
|
|
4
|
return $hash; |
114
|
|
|
|
|
|
|
} |
115
|
1
|
|
|
|
|
5
|
); |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub initialiser { |
120
|
3
|
|
|
3
|
1
|
17
|
my ($class, $target, $methods) = shift->args(@_); |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
$target->import_symbol( |
123
|
|
|
|
|
|
|
init => sub { |
124
|
3
|
|
|
3
|
|
8
|
my ($self, $config) = @_; |
125
|
3
|
|
|
|
|
14
|
$self->{ config } = $config; |
126
|
3
|
|
|
|
|
9
|
foreach my $name (@$methods) { |
127
|
3
|
|
|
|
|
13
|
$self->$name($config); |
128
|
|
|
|
|
|
|
} |
129
|
3
|
|
|
|
|
5
|
return $self; |
130
|
|
|
|
|
|
|
} |
131
|
3
|
|
|
|
|
21
|
); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub slots { |
135
|
3
|
|
|
3
|
1
|
13
|
my ($class, $target, $methods) = shift->args(@_); |
136
|
3
|
|
|
|
|
6
|
my $index = 0; |
137
|
|
|
|
|
|
|
|
138
|
3
|
|
|
|
|
9
|
foreach my $method (@$methods) { |
139
|
9
|
|
|
|
|
13
|
my $i = $index++; # new lexical var for closure |
140
|
|
|
|
|
|
|
$target->import_symbol( |
141
|
|
|
|
|
|
|
$method => sub { |
142
|
9
|
50
|
|
9
|
|
56
|
return @_ > 1 |
143
|
|
|
|
|
|
|
? ($_[0]->[$i] = $_[1]) |
144
|
|
|
|
|
|
|
: $_[0]->[$i]; |
145
|
|
|
|
|
|
|
} |
146
|
9
|
|
|
|
|
32
|
); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub auto_can { |
151
|
143
|
|
|
143
|
1
|
448
|
my ($class, $target, $methods) = shift->args(@_); |
152
|
|
|
|
|
|
|
|
153
|
143
|
50
|
|
|
|
456
|
die "auto_can only support a single method at this time\n" |
154
|
|
|
|
|
|
|
if @$methods != 1; |
155
|
|
|
|
|
|
|
|
156
|
143
|
|
|
|
|
259
|
my $method = shift @$methods; |
157
|
|
|
|
|
|
|
|
158
|
143
|
50
|
|
|
|
347
|
croak "Invalid auto_can method specified: $method\n" |
159
|
|
|
|
|
|
|
if ref $method eq CODE; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# avoid runaways |
162
|
143
|
|
|
|
|
261
|
my $seen = { }; |
163
|
|
|
|
|
|
|
|
164
|
143
|
|
|
|
|
183
|
$class->debug("installing AUTOLOAD and can() in $target") if DEBUG; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
$target->import_symbol( |
167
|
|
|
|
|
|
|
can => sub { |
168
|
18
|
|
|
18
|
|
42
|
my ($this, $name, @args) = @_; |
|
|
|
|
18
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
|
0
|
|
|
|
169
|
18
|
|
|
|
|
21
|
$class->debug("looking to see if $this can $name()") if DEBUG; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# This avoids runaways where can() calls itself repeatedly, but |
172
|
|
|
|
|
|
|
# doesn't prevent can() from being called several times for the |
173
|
|
|
|
|
|
|
# same item. |
174
|
18
|
50
|
|
|
|
45
|
return if $seen->{ $name }; |
175
|
18
|
|
|
|
|
39
|
local $seen->{ $name } = 1; |
176
|
|
|
|
|
|
|
|
177
|
18
|
|
100
|
|
|
190
|
return $this->SUPER::can($name) |
178
|
|
|
|
|
|
|
|| $this->$method($name, @args); |
179
|
|
|
|
|
|
|
} |
180
|
143
|
|
|
|
|
970
|
); |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
$target->import_symbol( |
183
|
|
|
|
|
|
|
AUTOLOAD => sub { |
184
|
20
|
|
|
20
|
|
101
|
my ($this, @args) = @_; |
185
|
20
|
|
|
|
|
145
|
my ($name) = ($AUTOLOAD =~ /([^:]+)$/ ); |
186
|
20
|
100
|
|
|
|
620
|
return if $name eq 'DESTROY'; |
187
|
10
|
100
|
|
|
|
32
|
if (my $method = $this->can($name, @args)) { |
188
|
7
|
|
|
|
|
36
|
my $that = class($this); |
189
|
7
|
|
|
|
|
13
|
$class->debug("$class installing $name method in $that") if DEBUG; |
190
|
7
|
|
|
|
|
24
|
$that->method( $name => $method ); |
191
|
7
|
|
|
|
|
17
|
return $method->($this, @args); |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Hmmm... what if $this isn't a subclass of Badger::Base? |
195
|
3
|
|
|
|
|
46
|
return $this->error_msg( bad_method => $name, ref $this, (caller())[1,2] ); |
196
|
|
|
|
|
|
|
} |
197
|
143
|
|
|
|
|
867
|
); |
198
|
|
|
|
|
|
|
|
199
|
143
|
|
|
|
|
315
|
$class->debug("installed AUTOLOAD and can() in $target") if DEBUG; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub args { |
203
|
495
|
|
|
495
|
1
|
736
|
my $class = shift; |
204
|
495
|
|
|
|
|
642
|
my $target = shift; |
205
|
495
|
50
|
|
|
|
1048
|
my $methods = @_ == 1 ? shift : [ @_ ]; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# update $target to a Badger::Class object if not already one |
208
|
495
|
50
|
|
|
|
1205
|
$target = class($target) |
209
|
|
|
|
|
|
|
unless is_object(BCLASS, $target); |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# split text string into list ref of method names |
212
|
495
|
100
|
|
|
|
3424
|
$methods = [ split(DELIMITER, $methods) ] |
213
|
|
|
|
|
|
|
unless ref $methods eq ARRAY; |
214
|
|
|
|
|
|
|
|
215
|
495
|
|
|
|
|
1770
|
return ($class, $target, $methods); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
1; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
__END__ |