line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Basset::Object; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
#Basset::Object Copyright and (c) 1999, 2000, 2002-2006 James A Thomason III |
4
|
|
|
|
|
|
|
#Basset::Object is distributed under the terms of the Perl Artistic License. |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=pod |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
Basset::Object - used to create objects |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 AUTHOR |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Jim Thomason, jim@jimandkoka.com |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 DESCRIPTION |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
This is my ultimate object creation toolset to date. It has roots in Mail::Bulkmail, Text::Flowchart, and the |
19
|
|
|
|
|
|
|
unreleased abstract object constructors that I've tooled around with in the past. |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
If you want an object to be compatible with anything else I've written, then subclass it off of here. |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
Of course, you don't have to use this to create subclasses, but you'll run the risk of making something with an inconsistent |
24
|
|
|
|
|
|
|
interface vs. the rest of the system. That'll confuse people and make them unhappy. So I recommend subclassing off of here |
25
|
|
|
|
|
|
|
to be consistent. Of course, you may not like these objects, but they do work well and are consistent. Consistency is |
26
|
|
|
|
|
|
|
very important in interface design, IMHO. |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
Please read the tutorials at L. |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=cut |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
$VERSION = '1.03'; |
33
|
|
|
|
|
|
|
|
34
|
714
|
|
|
714
|
|
6336
|
sub _conf_class {return 'Basset::Object::Conf'}; |
35
|
10
|
|
|
10
|
|
38282
|
BEGIN {eval 'use ' . _conf_class()}; |
|
9
|
|
|
9
|
|
5289
|
|
|
9
|
|
|
|
|
52
|
|
|
9
|
|
|
|
|
739
|
|
36
|
|
|
|
|
|
|
|
37
|
8
|
|
|
9
|
|
15717
|
use Data::Dumper (); |
|
8
|
|
|
|
|
111702
|
|
|
9
|
|
|
|
|
1350
|
|
38
|
8
|
|
|
8
|
|
79
|
use Carp; |
|
8
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
841
|
|
39
|
|
|
|
|
|
|
|
40
|
8
|
|
|
8
|
|
10563
|
use Basset::Container::Hash; |
|
8
|
|
|
|
|
20
|
|
|
8
|
|
|
|
|
260
|
|
41
|
|
|
|
|
|
|
|
42
|
8
|
|
|
8
|
|
50
|
use strict; |
|
8
|
|
|
|
|
17
|
|
|
8
|
|
|
|
|
234
|
|
43
|
8
|
|
|
8
|
|
39
|
use warnings; |
|
8
|
|
|
|
|
18
|
|
|
8
|
|
|
|
|
918
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=pod |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head1 METHODS |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=over |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=item add_attr |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
add_attr adds object attributes to the class. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Okay, now we're going to get into some philosophy. First of all, let me state that I *love* Perl's OO implementation. |
56
|
|
|
|
|
|
|
I usually get smacked upside the head when I say that, but I find it really easy to use, work with, manipulate, and so |
57
|
|
|
|
|
|
|
on. And there are things that you can do in Perl's OO that you can't in Java or C++ or the like. Perl, for example, can |
58
|
|
|
|
|
|
|
have *totally* private values that are completely inaccessible (lexicals, natch). private vars in the other languages |
59
|
|
|
|
|
|
|
can be redefined or tweaked or subclassed or otherwise gotten around in some form. Not Perl. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
And I obviously just adore Perl anyway. I get funny looks when I tell people that I like perl so much because it works |
62
|
|
|
|
|
|
|
the way I think. That bothers people for some reason. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
Anyway, as much as I like how it works, I don't like the fact that there's no consistent object type. An object is, |
65
|
|
|
|
|
|
|
of course, a blessed ((thingie)) (scalar, array, code, hash, etc) reference. And there are merits to using any of those |
66
|
|
|
|
|
|
|
things, depending upon the situation. Hashes are easy to work with and most similar to traditional objects. |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
$object->{$attribute} = $value; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
And whatnot. Arrays are much faster (typically 33% in tests I've done), but they suck to work with. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
$object->[15] = $value; #the hell is '15'? |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
( |
75
|
|
|
|
|
|
|
by the way, you can make this easier with variables defined to return the value, i.e. |
76
|
|
|
|
|
|
|
$object->[$attribute] = $value; #assuming $attribute == 15 |
77
|
|
|
|
|
|
|
) |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Scalars are speciality and coderefs are left to the magicians. Don't get me wrong, coderefs as objects are nifty, but |
80
|
|
|
|
|
|
|
they can be tricky to work with. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
So, I wanted a consistent interface. I'm not going to claim credit for this idea, since I think I originally read it |
83
|
|
|
|
|
|
|
in Object Oriented Programming in Perl (Damien's book). In fact, I think the error reporting method I use was also |
84
|
|
|
|
|
|
|
originally detailed in there. Anyway, I liked it a lot and decided I'd implement my own version of it. Besides, it's |
85
|
|
|
|
|
|
|
not like I'm the first guy to say that all attributes should be hidden behind mutators and accessors. |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Basically, attributes are accessed and mutated via methods. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
$object->attribute($value); |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
For all attributes. This way, the internal object can be whatever you'd like. I used to use mainly arrays for the speed |
92
|
|
|
|
|
|
|
boost, but lately I use hashes a lot because of the ease of dumping and reading the structure for debugging purposes. |
93
|
|
|
|
|
|
|
But, with this consistent interface of using methods to wrapper the attributes, I can change the implementation of |
94
|
|
|
|
|
|
|
the object (scalar, array, hash, code, whatever) up in this module and *nothing* else needs to change. |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
Say you implemented a giant system in OO perl. And you chose hashrefs as your "object". But then you needed a big |
97
|
|
|
|
|
|
|
speed boost later, which you could easily get by going to arrays. You'd have to go through your code and change all |
98
|
|
|
|
|
|
|
instances of $object->{$attribute} to $object->[15] or whatever. That's an awful lot of work. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
With everything wrappered up this way, changes can be made in the super object class and then automagically populate |
101
|
|
|
|
|
|
|
out everywhere with no code changes. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Enough with the philosophy, though. You need to know how this works. |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
It's easy enough: |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
package Some::Class; |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Some::Class->add_attr('foo'); |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Now your Some::Class objects have a foo attribute, which can be accessed as above. If called with a value, it's the mutator |
112
|
|
|
|
|
|
|
which sets the attribute to the new value and returns the new value. If called without one, it's the accessor which |
113
|
|
|
|
|
|
|
returns the value. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
my $obj = Some::Class->new(); |
116
|
|
|
|
|
|
|
$obj->foo('bar'); |
117
|
|
|
|
|
|
|
print $obj->foo(); #prints bar |
118
|
|
|
|
|
|
|
print $obj->foo('boo'); #prints boo |
119
|
|
|
|
|
|
|
print $obj->foo(); #prints boo |
120
|
|
|
|
|
|
|
print $obj->foo('bang'); #prints bang |
121
|
|
|
|
|
|
|
print $obj->foo; #prings bang |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
add_attr calls should only be in your module. B. And they really should be defined up at the top. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Internally, an add_attr call creates a function inside your package of the name of the attribute which reflects through |
126
|
|
|
|
|
|
|
to the internal _isa_accessor method which handles the mutating and accessing. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
You may alternatively pass in a list of attributes, if you don't want to do so much typing. |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
__PACKAGE__->add_attr( qw( foo bar baz ) ); |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
Gives you foo, bar, and baz attributes. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
There is another syntax for add_attr, to define a different internal accessor: |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Some::Class->add_attr(['foo', 'accessor_creator']); |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
This creates method called 'foo' which talks to a separate accessor, in this case the closure returned by "accessor_creator" instead of a closure |
139
|
|
|
|
|
|
|
returned by _isa_accessor. This is useful if you want to create a validating method on your attribute. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
Additionally, it creates a normal method going to _isa_accessor called '__b_foo', which is assumed to be the internal attribute |
142
|
|
|
|
|
|
|
slot your other accessor with use. In general, for a given "attribute", "__b_attribute" will be created for internal use. Also please |
143
|
|
|
|
|
|
|
note that you shouldn't ever create a method that starts with '__b_' (double underscore) since Basset reserves the right to automatically |
144
|
|
|
|
|
|
|
create methods named in that fashion. You've been warned. |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
"other_accessor" will get the object as the first arg (as always) and the name of the internal method as the second. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
A sample accessor_creator could look like this: |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Some::Class->add_attr(['foo', 'accessor_creator']); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub accessor_creator { |
153
|
|
|
|
|
|
|
my $self = shift; |
154
|
|
|
|
|
|
|
my $attribute = shift; #the external method name |
155
|
|
|
|
|
|
|
my $prop = shift; #the internal "slot" that is a normal attribute |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
#now we make our closure: |
158
|
|
|
|
|
|
|
return sub { |
159
|
|
|
|
|
|
|
my $self = shift; |
160
|
|
|
|
|
|
|
if (@_) { |
161
|
|
|
|
|
|
|
my $val = shift; |
162
|
|
|
|
|
|
|
if ($val == 7) { |
163
|
|
|
|
|
|
|
return $self->$prop($val); |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
else { |
166
|
|
|
|
|
|
|
return $self->error("Cannot store value...must be 7!", "not_7"); |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
else { |
170
|
|
|
|
|
|
|
return $self->$prop(); |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
And, finally, you can also pass in additional arguments as static args if desired. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Some::Class->add_attr(['foo', 'accessor_creator'], 'bar'); |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
$obj->foo('bee'); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub accessor_creator { |
182
|
|
|
|
|
|
|
my $self = shift; |
183
|
|
|
|
|
|
|
my $method = shift; |
184
|
|
|
|
|
|
|
my $static = shift; #'bar' in our example |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
return sub { |
187
|
|
|
|
|
|
|
#do something with static argument |
188
|
|
|
|
|
|
|
. |
189
|
|
|
|
|
|
|
. |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
}; |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
All easy enough. Refer to any subclasses of this class for further examples. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
Basset::Object includes two other alternate accessors for you - regex and private. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
Some::Class->add_attr(['user_id', '_isa_regex_accessor', qr{^\d+$}, "Error - user_id must be a number", "NaN"]); |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
The arguments to it are, respectively, the name of the attribute, the internal accessor used, the regex used to validate, the error message to return, and the error code to return. |
200
|
|
|
|
|
|
|
If you try to mutate with a value that doesn't match the regex, it'll fail. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
Some::Class->add_attr(['secret', '_isa_private_accessor']); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
private accessors add a slight degree of security. All they do is simply restrict access to the attribute unless you are within the class of the object. Note, that this causes |
205
|
|
|
|
|
|
|
access to automatically trickle down into subclasses. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=cut |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
sub add_attr { |
210
|
239
|
|
|
239
|
1
|
6890
|
my $pkg = shift; |
211
|
|
|
|
|
|
|
|
212
|
8
|
|
|
8
|
|
45
|
no strict 'refs'; |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
5754
|
|
213
|
|
|
|
|
|
|
|
214
|
239
|
|
|
|
|
508
|
foreach my $record (@_) { |
215
|
240
|
|
|
|
|
264
|
my ($attribute, $adding_method, $internal_attribute, @args); |
216
|
240
|
100
|
|
|
|
511
|
if (ref $record eq 'ARRAY') { |
217
|
43
|
|
|
|
|
102
|
($attribute, $adding_method, @args) = @$record; |
218
|
43
|
|
|
|
|
158
|
$internal_attribute = $pkg->privatize($attribute); |
219
|
42
|
|
|
|
|
168
|
*{$pkg . "::$internal_attribute"} = $pkg->_isa_accessor($internal_attribute, $attribute) |
|
43
|
|
|
|
|
1905
|
|
220
|
43
|
100
|
|
|
|
57
|
unless *{$pkg . "::$internal_attribute"}{'CODE'}; |
221
|
42
|
|
|
|
|
199
|
*{$pkg . "::$attribute"} = $pkg->$adding_method($attribute, $internal_attribute, @args) |
|
43
|
|
|
|
|
375
|
|
222
|
43
|
100
|
|
|
|
58
|
unless *{$pkg . "::$attribute"}{'CODE'}; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
else { |
225
|
197
|
|
|
|
|
264
|
$attribute = $record; |
226
|
197
|
100
|
|
|
|
216
|
*{$pkg . "::$record"} = $pkg->_isa_accessor($record) unless *{$pkg . "::$record"}{'CODE'}; |
|
193
|
|
|
|
|
788
|
|
|
197
|
|
|
|
|
1712
|
|
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
240
|
|
|
|
|
714
|
$pkg->_instance_attributes->{$attribute}++; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
239
|
|
|
|
|
602
|
return 1; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub _isa_accessor { |
238
|
235
|
|
|
235
|
|
333
|
my $pkg = shift; |
239
|
235
|
|
|
|
|
279
|
my $attribute = shift; |
240
|
235
|
|
66
|
|
|
814
|
my $prop = shift || $attribute; |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
return sub { |
243
|
1545
|
|
|
1545
|
|
42450
|
my $self = shift; |
244
|
|
|
|
|
|
|
|
245
|
1545
|
100
|
|
|
|
3565
|
return $self->error("Not a class attribute", "BO-08") unless ref $self; |
246
|
|
|
|
|
|
|
|
247
|
1507
|
100
|
|
|
|
4706
|
$self->{$prop} = shift if @_; |
248
|
|
|
|
|
|
|
|
249
|
1507
|
|
|
|
|
8362
|
$self->{$prop}; |
250
|
235
|
|
|
|
|
1214
|
}; |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# _accessor is the main accessor method used in the system. It defines the most simple behavior as to how objects are supposed |
254
|
|
|
|
|
|
|
# to work. If it's called with no arguments, it returns the value of that attribute. If it's called with arguments, |
255
|
|
|
|
|
|
|
# it sets the object attribute value to the FIRST argument passed and ignores the rest |
256
|
|
|
|
|
|
|
# |
257
|
|
|
|
|
|
|
# example: |
258
|
|
|
|
|
|
|
# my $object; |
259
|
|
|
|
|
|
|
# print $object->attribute7(); #prints out the value of attribute7 |
260
|
|
|
|
|
|
|
# print $object->attribute7('foo'); #sets the value of attribute7 to 'foo', and prints 'foo' |
261
|
|
|
|
|
|
|
# print $object->attribute7(); #prints out the value of attribute7, which is now known to be foo |
262
|
|
|
|
|
|
|
# |
263
|
|
|
|
|
|
|
# All internal accessor methods should behave similarly, read the documentation for add_attr for more information |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
#tested w/ add_attr, above |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub _isa_regex_accessor { |
268
|
2
|
|
|
2
|
|
3
|
my $pkg = shift; |
269
|
2
|
|
|
|
|
4
|
my $attribute = shift; |
270
|
2
|
|
|
|
|
4
|
my $prop = shift; |
271
|
2
|
|
|
|
|
3
|
my $regex = shift; |
272
|
2
|
|
|
|
|
5
|
my $error = shift; |
273
|
2
|
|
|
|
|
3
|
my $code = shift; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
return sub { |
276
|
23
|
|
|
23
|
|
41
|
my $self = shift; |
277
|
23
|
100
|
|
|
|
67
|
if (@_) { |
278
|
19
|
|
|
|
|
30
|
my $val = shift; |
279
|
19
|
100
|
100
|
|
|
194
|
return $self->error($error, $code) if defined $val && $val !~ /$regex/; |
280
|
|
|
|
|
|
|
|
281
|
9
|
|
|
|
|
29
|
return $self->$prop($val); |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
else { |
284
|
4
|
|
|
|
|
12
|
return $self->$prop(); |
285
|
|
|
|
|
|
|
} |
286
|
2
|
|
|
|
|
9
|
}; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub _isa_private_accessor { |
290
|
1
|
|
|
1
|
|
3
|
my $pkg = shift; |
291
|
1
|
|
|
|
|
1
|
my $attribute = shift; |
292
|
1
|
|
|
|
|
3
|
my $prop = shift; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
return sub { |
295
|
3
|
|
|
3
|
|
5
|
my $self = shift; |
296
|
3
|
|
|
|
|
12
|
my @caller = caller; |
297
|
3
|
100
|
|
|
|
8
|
return $self->error("Cannot access $prop : private method", "BO-27") unless $caller[0] eq $self->pkg; |
298
|
|
|
|
|
|
|
|
299
|
2
|
|
|
|
|
8
|
$self->$prop(@_); |
300
|
1
|
|
|
|
|
5
|
}; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=pod |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=begin btest(add_attr) |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub add_test_accessor { |
309
|
|
|
|
|
|
|
my $pkg = shift; |
310
|
|
|
|
|
|
|
my $attr = shift; |
311
|
|
|
|
|
|
|
my $prop = shift; |
312
|
|
|
|
|
|
|
my $extra = shift; |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
no strict 'refs'; |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
return sub { |
317
|
|
|
|
|
|
|
my $self = shift; |
318
|
|
|
|
|
|
|
return $self->error("Not a class attribute", "BO-08") unless ref $self; |
319
|
|
|
|
|
|
|
$extra; |
320
|
|
|
|
|
|
|
}; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
$test->ok(\&__PACKAGE__::test_accessor, "Added test accessor"); |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
326
|
|
|
|
|
|
|
$test->ok($o, "Object created"); |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_attr('test_attribute1'), "Added attribute for _accessor"); |
329
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_attr('test_attribute1'), "Re-added attribute for _accessor"); |
330
|
|
|
|
|
|
|
$test->ok($o->can('test_attribute1'), "Object sees attribute"); |
331
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->can('test_attribute1'), "Class sees attribute"); |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
$test->is($o->test_attribute1('testval1'), 'testval1', "Method test_attribute1 mutates"); |
334
|
|
|
|
|
|
|
$test->is($o->test_attribute1(), 'testval1', "Method test_attribute1 accesses"); |
335
|
|
|
|
|
|
|
$test->is($o->test_attribute1(undef), undef, "Method test_attribute1 deletes"); |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute1('testval17'), undef, "Class fails to mutate"); |
338
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute1(), undef, "Class fails to access"); |
339
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute1(undef), undef, "Class fails to delete"); |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_attr(['test_attribute2', 'add_test_accessor', 'excess']), "Added attribute for test_accessor"); |
342
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_attr(['test_attribute2', 'add_test_accessor', 'excess']), "Re-added attribute for test_accessor"); |
343
|
|
|
|
|
|
|
$test->ok($o->can('test_attribute2'), "Object sees attribute"); |
344
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->can('test_attribute2'), "Class sees attribute"); |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
$test->is($o->test_attribute2('testval2'), 'excess', "Method test_attribute2 mutates"); |
347
|
|
|
|
|
|
|
$test->is($o->test_attribute2(), 'excess', "Method test_attribute2 accesses"); |
348
|
|
|
|
|
|
|
$test->is($o->test_attribute2(undef), 'excess', "Method test_attribute2 deletes"); |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute2('testval18'), undef, "Class fails to mutate"); |
351
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute2(), undef, "Class fails to access"); |
352
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute2(undef), undef, "Class fails to delete"); |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_attr('test_attribute3', 'static'), "Added static attribute"); |
355
|
|
|
|
|
|
|
$test->ok($o->can('test_attribute3'), "Object sees attribute"); |
356
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->can('test_attribute3'), "Class sees attribute"); |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
$test->is($o->test_attribute3('status'), 'status', "Method test_attribute3 mutates"); |
359
|
|
|
|
|
|
|
$test->is($o->test_attribute3(), 'status', "Method test_attribute3 accesses"); |
360
|
|
|
|
|
|
|
$test->is($o->test_attribute3(undef), undef, "Method test_attribute3 deletes"); |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute3('testval19'), undef, "Class fails to mutate"); |
363
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute3(), undef, "Class fails to access"); |
364
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute3(undef), undef, "Class fails to delete"); |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_attr(['test_attribute4', '_isa_regex_accessor', '^\d+$', 'Numbers only', 'test code']), "Added numeric only regex attribute"); |
367
|
|
|
|
|
|
|
$test->ok($o->can('test_attribute4'), "Object sees attribute"); |
368
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->can('test_attribute4'), "Class sees attribute"); |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
$test->isnt(scalar $o->test_attribute4('foo'), 'foo', "Method test_attribute4 fails to set non-numeric"); |
371
|
|
|
|
|
|
|
$test->is($o->error, "Numbers only", "Proper object error message"); |
372
|
|
|
|
|
|
|
$test->is($o->errcode, "test code", "Proper object error code"); |
373
|
|
|
|
|
|
|
$test->isnt(scalar $o->test_attribute4('1234567890a'), '1234567890a', "Method test_attribute4 fails to set non-numeric"); |
374
|
|
|
|
|
|
|
$test->is($o->error, "Numbers only", "Proper object error message"); |
375
|
|
|
|
|
|
|
$test->is($o->errcode, "test code", "Proper object error code"); |
376
|
|
|
|
|
|
|
$test->isnt(scalar $o->test_attribute4('a1234567890'), 'a1234567890', "Method test_attribute4 fails to set non-numeric"); |
377
|
|
|
|
|
|
|
$test->is($o->error, "Numbers only", "Proper object error message"); |
378
|
|
|
|
|
|
|
$test->is($o->errcode, "test code", "Proper object error code"); |
379
|
|
|
|
|
|
|
$test->isnt(scalar $o->test_attribute4('123456a7890'), '123456a7890', "Method test_attribute4 fails to set non-numeric"); |
380
|
|
|
|
|
|
|
$test->is($o->error, "Numbers only", "Proper object error message"); |
381
|
|
|
|
|
|
|
$test->is($o->errcode, "test code", "Proper object error code"); |
382
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute4('12345'), '12345', "Method test_attribute4 mutates"); |
383
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute4(), '12345', "Method test_attribute4 accesses"); |
384
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute4(undef), undef, "Method test_attribute4 deletes"); |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute4('testval20'), undef, "Class fails to mutate"); |
387
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute4(), undef, "Class fails to access"); |
388
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute4(undef), undef, "Class fails to delete"); |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_attr(['test_attribute5', '_isa_regex_accessor', 'abcD', 'Must contain abcD', 'test code2']), "Added abcD only regex attribute"); |
391
|
|
|
|
|
|
|
$test->ok($o->can('test_attribute5'), "Object sees attribute"); |
392
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->can('test_attribute5'), "Class sees attribute"); |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
$test->isnt(scalar $o->test_attribute5('foo'), 'foo', "Method test_attribute4 fails to set non-abcD"); |
395
|
|
|
|
|
|
|
$test->is($o->error, "Must contain abcD", "Proper object error message"); |
396
|
|
|
|
|
|
|
$test->is($o->errcode, "test code2", "Proper object error code"); |
397
|
|
|
|
|
|
|
$test->isnt(scalar $o->test_attribute5('abc'), 'abc', "Method test_attribute4 fails to set non-abcD"); |
398
|
|
|
|
|
|
|
$test->is($o->error, "Must contain abcD", "Proper object error message"); |
399
|
|
|
|
|
|
|
$test->is($o->errcode, "test code2", "Proper object error code"); |
400
|
|
|
|
|
|
|
$test->isnt(scalar $o->test_attribute5('bcD'), 'bcD', "Method test_attribute4 fails to set non-abcD"); |
401
|
|
|
|
|
|
|
$test->is($o->error, "Must contain abcD", "Proper object error message"); |
402
|
|
|
|
|
|
|
$test->is($o->errcode, "test code2", "Proper object error code"); |
403
|
|
|
|
|
|
|
$test->isnt(scalar $o->test_attribute5('abD'), 'abD', "Method test_attribute4 fails to set non-abcD"); |
404
|
|
|
|
|
|
|
$test->is($o->error, "Must contain abcD", "Proper object error message"); |
405
|
|
|
|
|
|
|
$test->is($o->errcode, "test code2", "Proper object error code"); |
406
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute5('abcD'), 'abcD', "Method test_attribute5 mutates"); |
407
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute5('abcDE'), 'abcDE', "Method test_attribute5 mutates"); |
408
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute5('1abcD'), '1abcD', "Method test_attribute5 mutates"); |
409
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute5('zabcDz'), 'zabcDz', "Method test_attribute5 mutates"); |
410
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute5(), 'zabcDz', "Method test_attribute5 accesses"); |
411
|
|
|
|
|
|
|
$test->is(scalar $o->test_attribute5(undef), undef, "Method test_attribute5 deletes"); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute5('testval20'), undef, "Class fails to mutate"); |
414
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute5(), undef, "Class fails to access"); |
415
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_attribute5(undef), undef, "Class fails to delete"); |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_attr::Subclass1; |
418
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
my $sub_class = "Basset::Test::Testing::__PACKAGE__::add_attr::Subclass1"; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
my $so = $sub_class->new(); |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
$test->ok(scalar $sub_class->add_attr(['secret', '_isa_private_accessor']), 'added secret accessor'); |
425
|
|
|
|
|
|
|
$test->ok($so->can('secret'), "Object sees secret attribute"); |
426
|
|
|
|
|
|
|
$test->is($so->secret('foobar'), 'foobar', 'Object sets secret attribute'); |
427
|
|
|
|
|
|
|
$test->is($so->secret(), 'foobar', 'Object sees secret attribute'); |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
package __PACKAGE__; |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
$test->is(scalar $so->secret(), undef, 'Object cannot see secret attribute outside'); |
432
|
|
|
|
|
|
|
$test->is($so->errcode, 'BO-27', 'proper error code'); |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=end btest(add_attr) |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=cut |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=pod |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=item add_class_attr |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
This is similar to add_attr, but instead of adding object attributes, it adds class attributes. You B have |
443
|
|
|
|
|
|
|
object and class attributes with the same name. This is by design. (error is a special case) |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
Some::Class->add_attr('foo'); #object attribute foo |
446
|
|
|
|
|
|
|
Some::Class->add_class_attr('bar'): #class attribute bar |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
print $obj->foo(); |
449
|
|
|
|
|
|
|
print Some::Class->bar(); |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Behaves the same as an object method added with add_attr, mutating with a value, accessing without one. Note |
452
|
|
|
|
|
|
|
that add_class_attr does not have the capability for additional internal methods or static values. If you want |
453
|
|
|
|
|
|
|
those on a class method, you'll have to wrapper the class attribute yourself on a per case basis. |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Note that you can access class attributes via an object (as expected), but it's frowned upon since it may be |
456
|
|
|
|
|
|
|
confusing. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
class attributes are automatically initialized to any values in the conf file upon adding, if present. |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=cut |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=pod |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=begin btest(add_class_attr) |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
467
|
|
|
|
|
|
|
$test->ok($o, "Object created"); |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_class_attr('test_class_attribute_1'), "Added test class attribute"); |
470
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_class_attr('test_class_attribute_1'), "Re-added test class attribute"); |
471
|
|
|
|
|
|
|
$test->ok($o->can("test_class_attribute_1"), "object can see class attribute"); |
472
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->can("test_class_attribute_1"), "class can see class attribute"); |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
$test->is(__PACKAGE__->test_class_attribute_1('test value 1'), 'test value 1', 'class method call mutates'); |
475
|
|
|
|
|
|
|
$test->is(__PACKAGE__->test_class_attribute_1(), 'test value 1', 'class method call accesses'); |
476
|
|
|
|
|
|
|
$test->is(__PACKAGE__->test_class_attribute_1(undef), undef, 'class method call deletes'); |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
$test->is($o->test_class_attribute_1('test value 2'), 'test value 2', 'object method call mutates'); |
479
|
|
|
|
|
|
|
$test->is($o->test_class_attribute_1(), 'test value 2', 'object method call accesses'); |
480
|
|
|
|
|
|
|
$test->is($o->test_class_attribute_1(undef), undef, 'object method call deletes'); |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_class_attr('test_class_attribute_2', 14), "Added test class attribute 2"); |
483
|
|
|
|
|
|
|
$test->ok($o->can("test_class_attribute_2"), "object can see class attribute"); |
484
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->can("test_class_attribute_2"), "class can see class attribute"); |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
$test->is(__PACKAGE__->test_class_attribute_2(), 14, "Class has default arg"); |
487
|
|
|
|
|
|
|
$test->is(__PACKAGE__->test_class_attribute_2(), 14, "Object has default arg"); |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
$test->is(__PACKAGE__->test_class_attribute_2('test value 3'), 'test value 3', 'class method call mutates'); |
490
|
|
|
|
|
|
|
$test->is(__PACKAGE__->test_class_attribute_2(), 'test value 3', 'class method call accesses'); |
491
|
|
|
|
|
|
|
$test->is(__PACKAGE__->test_class_attribute_2(undef), undef, 'class method call deletes'); |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
$test->is($o->test_class_attribute_1('test value 4'), 'test value 4', 'class method call mutates'); |
494
|
|
|
|
|
|
|
$test->is($o->test_class_attribute_1(), 'test value 4', 'object method call accesses'); |
495
|
|
|
|
|
|
|
$test->is($o->test_class_attribute_1(undef), undef, 'object method call deletes'); |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1; |
498
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
package __PACKAGE__; |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
my $so = Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->new(); |
503
|
|
|
|
|
|
|
$test->ok($so, "Sub-Object created"); |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_class_attribute_1("newer test val"), "newer test val", "trickle method class re-mutates"); |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
$test->is(scalar $so->test_class_attribute_1(), "newer test val", "trickle method sub-object accesses super"); |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
$test->is(scalar $so->test_class_attribute_1("testval3"), "testval3", "trickle method sub-object mutates"); |
510
|
|
|
|
|
|
|
$test->is(scalar $so->test_class_attribute_1(), "testval3", "trickle method sub-object accesses"); |
511
|
|
|
|
|
|
|
$test->is(scalar $so->test_class_attribute_1(undef), undef, "trickle method sub-object deletes"); |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->test_class_attribute_1("testval4"), "testval4", "trickle method class mutates"); |
514
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->test_class_attribute_1(), "testval4", "trickle method subclass accesses"); |
515
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->test_class_attribute_1(undef), undef, "trickle method subclass deletes"); |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->test_class_attribute_1("sub value"), "sub value", "Subclass re-mutates"); |
518
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_class_attribute_1(), "sub value", "Super class affected on access"); |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->test_class_attribute_1("super value"), "super value", "Super class re-mutates"); |
521
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->test_class_attribute_1(), "super value", "Sub class affected on access"); |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass5; |
524
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
sub conf { |
527
|
|
|
|
|
|
|
return undef; |
528
|
|
|
|
|
|
|
}; |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
package __PACKAGE__; |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
{ |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
local $@ = undef; |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
eval { |
537
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass5->add_class_attr('test_class_attr'); |
538
|
|
|
|
|
|
|
}; |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
$test->like($@, qr/^Conf file error :/, 'could not add class attr w/o conf file'); |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
my $conf = __PACKAGE__->conf(); |
544
|
|
|
|
|
|
|
$conf->{'__PACKAGE__'}->{'_test_attribute'} = 'test value'; |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_class_attr('_test_attribute'), 'added test attribute'); |
547
|
|
|
|
|
|
|
$test->is(__PACKAGE__->_test_attribute, 'test value', 'populated with value from conf fiel'); |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=end btest(add_class_attr) |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=cut |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
sub add_class_attr { |
554
|
53
|
|
|
53
|
1
|
1550
|
my $pkg = shift; |
555
|
53
|
|
|
|
|
90
|
my $method = shift; |
556
|
|
|
|
|
|
|
|
557
|
8
|
|
|
8
|
|
56
|
no strict 'refs'; |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
2428
|
|
558
|
|
|
|
|
|
|
|
559
|
53
|
100
|
|
|
|
66
|
return $method if *{$pkg . "::$method"}{'CODE'}; |
|
53
|
|
|
|
|
402
|
|
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
#Slick. We'll use a proper closure here. |
562
|
52
|
|
|
|
|
168
|
my $attr = undef; |
563
|
52
|
|
|
|
|
789
|
*{$pkg . "::$method"} = sub { |
564
|
271
|
|
|
271
|
|
4497
|
my $pkg = shift; |
565
|
271
|
100
|
|
|
|
645
|
$attr = shift if @_; |
566
|
271
|
|
|
|
|
2861
|
return $attr; |
567
|
52
|
|
|
|
|
240
|
}; |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
#see if there's anything in the conf file |
570
|
|
|
|
|
|
|
|
571
|
52
|
100
|
|
|
|
277
|
my $conf = $pkg->conf or die "Conf file error : could not read conf file"; |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
|
574
|
49
|
100
|
|
|
|
420
|
if (exists $conf->{$pkg}->{$method}){ |
|
|
100
|
|
|
|
|
|
575
|
1
|
|
|
|
|
5
|
$pkg->$method($conf->{$pkg}->{$method}); |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
elsif (@_){ |
578
|
13
|
|
|
|
|
50
|
$pkg->$method(@_); |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
|
581
|
49
|
|
|
|
|
143
|
$pkg->_class_attributes->{$method}++; |
582
|
|
|
|
|
|
|
|
583
|
49
|
|
|
|
|
152
|
return $method; |
584
|
|
|
|
|
|
|
}; |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=pod |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=item add_trickle_class_attr |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
It's things like this why I really love Perl. |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
add_trickle_class_attr behaves the same as add_class_attr with the addition that it will trickle the attribute down |
593
|
|
|
|
|
|
|
into any class as it is called. This is useful for subclasses. |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
Watch: |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
package SuperClass; |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
SuperClass->add_class_attr('foo'); |
600
|
|
|
|
|
|
|
SuperClass->foo('bar'); |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
package SubClass; |
603
|
|
|
|
|
|
|
@ISA = qw(SuperClass); |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
print SubClass->foo(); #prints bar |
606
|
|
|
|
|
|
|
print SuperClass->foo(); #prints bar |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
print SuperClass->foo('baz'); #prints baz |
609
|
|
|
|
|
|
|
print SubClass->foo(); #prints baz |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
print SubClass->foo('dee'); #prints dee |
612
|
|
|
|
|
|
|
print SuperClass->foo(); #prints dee |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
See? The attribute is still stored in the super class, so changing it in a subclass changes it in the super class as well. |
615
|
|
|
|
|
|
|
Usually, this behavior is fine, but sometimes you don't want that to happen. That's where add_trickle_class_attr comes |
616
|
|
|
|
|
|
|
in. Its first call will snag the value from the SuperClass, but then it will have its own attribute that's separate. |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
Again, watch: |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
package SuperClass; |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
SuperClass->add_trickle_class_attr('foo'); |
623
|
|
|
|
|
|
|
SuperClass->foo('bar'); |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
package SubClass; |
626
|
|
|
|
|
|
|
@ISA = qw(SuperClass); |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
print SubClass->foo(); #prints bar |
629
|
|
|
|
|
|
|
print SuperClass->foo(); #prints bar |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
print SuperClass->foo('baz'); #prints baz |
632
|
|
|
|
|
|
|
print SubClass->foo(); #prints bar |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
print SubClass->foo('dee'); #prints dee (note we're setting the subclass here) |
635
|
|
|
|
|
|
|
print SuperClass->foo(); #prints baz |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
This is useful if you have an attribute that should be unique to a class and all subclasses. These are equivalent: |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
package SuperClass; |
640
|
|
|
|
|
|
|
SuperClass->add_class_attr('foo'); |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
package SubClass |
643
|
|
|
|
|
|
|
SubClass->add_class_attr('foo'); |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
and |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
package SuperClass; |
648
|
|
|
|
|
|
|
SuperClass->add_trickle_class_attr('foo'); |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
You'll usually just use add_class_attr. Only use trickle_class_attr if you know you need to, since you rarely would. |
651
|
|
|
|
|
|
|
There is a *slight* bit of additional processing required for trickled accessors. |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
trickled class attributes are automatically initialized to any values in the conf file upon adding, if present. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
References are a special case. If you add a hashref, that hashref will automatically be tied to a Basset::Container::Hash. |
656
|
|
|
|
|
|
|
Do not do this tying yourself, since bad things would occur. Once tied to Basset::Container::Hash, the hashref is now |
657
|
|
|
|
|
|
|
effectively layered so that subclasses may directly add to the hash without affecting parent values. Subclasses may not delete |
658
|
|
|
|
|
|
|
keys from the hash, only delete values they have added. Arrays are not tied. |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
Sometimes, you may be required to access the attribute via a wrapper method. |
661
|
|
|
|
|
|
|
For example: |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
sub wrapper { |
664
|
|
|
|
|
|
|
my $self = shift; |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
my $existing = $self->trickled_ref(); |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
if (@_) { |
669
|
|
|
|
|
|
|
my $dumped = $self->dump($existing); #take a dump of the ref |
670
|
|
|
|
|
|
|
no strict; no warnings; #make sure nothing complains |
671
|
|
|
|
|
|
|
$self->trickled_ref(eval $dump); #stick in a copy of it |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
return $self->trickled_ref(@_); |
675
|
|
|
|
|
|
|
} |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
Then you need to access the trickled method through the wrapper you've created. I don't want to |
678
|
|
|
|
|
|
|
add functionality like that into the add_trickle_class_attr method because I won't know when |
679
|
|
|
|
|
|
|
the value needs to be changed. You're getting back a reference, but then manipulating the value |
680
|
|
|
|
|
|
|
of the reference. So once you have a ref back, you immediately start changing the super class's |
681
|
|
|
|
|
|
|
value. The only way that I could fix it up here is to constantly re-copy the reference on |
682
|
|
|
|
|
|
|
every single access. But, of course, that then stops it from seeing changes in the super class, |
683
|
|
|
|
|
|
|
which is inconsistent. |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
Realistically, if you're using a ref and modifying it, you'll want wrapper methods to do things |
686
|
|
|
|
|
|
|
like add values within the ref, delete values within the ref, etc, you'll rarely (if ever) access |
687
|
|
|
|
|
|
|
the actual value of the ref directly. That is to say, you'll rarely change the hash pointed at, |
688
|
|
|
|
|
|
|
you'll change keys within the hash. So add_foo, delete_foo, change_foo, etc. wrappers that properly |
689
|
|
|
|
|
|
|
copy the hash as appropriate are the way to go. You can then still properly read the ref by |
690
|
|
|
|
|
|
|
just using the trickled attribute as always. |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
See the add_restrictions method below for an example of a wrapper like this. |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=cut |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
sub add_trickle_class_attr { |
697
|
249
|
|
|
249
|
1
|
969
|
my $internalpkg = shift; |
698
|
249
|
|
|
|
|
362
|
my $method = shift; |
699
|
|
|
|
|
|
|
|
700
|
8
|
|
|
8
|
|
55
|
no strict 'refs'; |
|
8
|
|
|
|
|
17
|
|
|
8
|
|
|
|
|
4479
|
|
701
|
|
|
|
|
|
|
|
702
|
249
|
100
|
|
|
|
344
|
return $method if *{$internalpkg . "::$method"}{'CODE'}; |
|
249
|
|
|
|
|
2545
|
|
703
|
|
|
|
|
|
|
|
704
|
248
|
|
|
|
|
340
|
my $attr = undef; |
705
|
248
|
|
|
|
|
635
|
my $initialized = {$internalpkg => 1}; |
706
|
|
|
|
|
|
|
|
707
|
248
|
|
|
|
|
1173
|
*{$internalpkg . "::$method"} = sub { |
708
|
|
|
|
|
|
|
|
709
|
2149
|
|
|
2149
|
|
18181
|
my $class = shift->pkg; |
710
|
|
|
|
|
|
|
|
711
|
2149
|
100
|
|
|
|
5791
|
unless ($initialized->{$class}) { |
712
|
161
|
|
|
|
|
377
|
$initialized->{$class}++; |
713
|
161
|
|
|
|
|
640
|
my $local_conf = $class->conf('local'); |
714
|
161
|
50
|
|
|
|
553
|
if (defined (my $confval = $local_conf->{$method})) { |
715
|
0
|
|
|
|
|
0
|
return $class->$method($confval); |
716
|
|
|
|
|
|
|
}; |
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
|
719
|
2149
|
100
|
|
|
|
4231
|
if (@_) { |
720
|
517
|
100
|
|
|
|
1112
|
if ($class ne $internalpkg) { |
721
|
56
|
|
|
|
|
325
|
$class->add_trickle_class_attr($method); |
722
|
56
|
|
|
|
|
102
|
my $val = shift; |
723
|
|
|
|
|
|
|
|
724
|
56
|
50
|
66
|
|
|
189
|
if (ref $val eq 'HASH' && ref $attr eq 'HASH') { |
725
|
|
|
|
|
|
|
#the tie blows away the values, so we need to keep a copy. |
726
|
0
|
|
|
|
|
0
|
my %tmp; |
727
|
0
|
|
|
|
|
0
|
@tmp{keys %$val} = values %$val; |
728
|
0
|
|
|
|
|
0
|
tie %$val, 'Basset::Container::Hash', $attr; |
729
|
0
|
|
|
|
|
0
|
$class->add_trickle_class_attr($method); |
730
|
0
|
|
|
|
|
0
|
@$val{keys %tmp} = values %tmp; |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
|
733
|
56
|
|
|
|
|
233
|
return $class->$method($val, @_); |
734
|
|
|
|
|
|
|
} |
735
|
461
|
|
|
|
|
765
|
$attr = shift; |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
|
738
|
2093
|
100
|
100
|
|
|
25172
|
if (ref $attr eq 'HASH' && $class ne $internalpkg) { |
739
|
100
|
|
|
|
|
706
|
tie my %empty, 'Basset::Container::Hash', $attr; |
740
|
100
|
|
|
|
|
536
|
$class->add_trickle_class_attr($method, \%empty); |
741
|
100
|
|
|
|
|
558
|
return $class->$method(); |
742
|
|
|
|
|
|
|
} |
743
|
|
|
|
|
|
|
|
744
|
1993
|
|
|
|
|
10653
|
return $attr; |
745
|
248
|
|
|
|
|
1931
|
}; |
746
|
|
|
|
|
|
|
|
747
|
248
|
|
|
|
|
720
|
my $conf = $internalpkg->conf; |
748
|
|
|
|
|
|
|
|
749
|
248
|
100
|
|
|
|
1014
|
if (defined (my $confval = $conf->{$internalpkg}->{$method})) { |
|
|
100
|
|
|
|
|
|
750
|
8
|
|
|
|
|
31
|
$internalpkg->$method($confval); |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
elsif (@_) { |
753
|
143
|
|
|
|
|
486
|
$internalpkg->$method(@_); |
754
|
|
|
|
|
|
|
} |
755
|
|
|
|
|
|
|
|
756
|
248
|
|
|
|
|
1218
|
$internalpkg->_class_attributes->{$method}++; |
757
|
|
|
|
|
|
|
|
758
|
248
|
|
|
|
|
834
|
return $method; |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=pod |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=begin btest(add_trickle_class_attr) |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
767
|
|
|
|
|
|
|
$test->ok($o, "Object created"); |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_trickle_class_attr('trick_attr1'), "Added test trickle class attribute"); |
770
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->add_trickle_class_attr('trick_attr1'), "Re-added test trickle class attribute"); |
771
|
|
|
|
|
|
|
$test->ok($o->can("trick_attr1"), "object can see trickle class attribute"); |
772
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->can("trick_attr1"), "class can see trickle class attribute"); |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1; |
775
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
package __PACKAGE__; |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
my $so = Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->new(); |
780
|
|
|
|
|
|
|
$test->ok($so, "Sub-Object created"); |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
$test->is(scalar $o->trick_attr1("testval1"), "testval1", "trickle method object mutates"); |
783
|
|
|
|
|
|
|
$test->is(scalar $o->trick_attr1(), "testval1", "trickle method object accesses"); |
784
|
|
|
|
|
|
|
$test->is(scalar $o->trick_attr1(undef), undef, "trickle method object deletes"); |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->trick_attr1("testval2"), "testval2", "trickle method class mutates"); |
787
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->trick_attr1(), "testval2", "trickle method class accesses"); |
788
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->trick_attr1(undef), undef, "trickle method class deletes"); |
789
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->trick_attr1("newer test val"), "newer test val", "trickle method class re-mutates"); |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
$test->is(scalar $so->trick_attr1(), "newer test val", "trickle method sub-object accesses super"); |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
$test->is(scalar $so->trick_attr1("testval3"), "testval3", "trickle method sub-object mutates"); |
794
|
|
|
|
|
|
|
$test->is(scalar $so->trick_attr1(), "testval3", "trickle method sub-object accesses"); |
795
|
|
|
|
|
|
|
$test->is(scalar $so->trick_attr1(undef), undef, "trickle method sub-object deletes"); |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->trick_attr1("supertestval"), "supertestval", "super trickle method class mutates"); |
798
|
|
|
|
|
|
|
$test->is(__PACKAGE__->trick_attr1(), "supertestval", "trickle method class accesses"); |
799
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->trick_attr1("testval4"), "testval4", "trickle method class mutates"); |
800
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->trick_attr1(), "testval4", "trickle method subclass accesses"); |
801
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->trick_attr1(undef), undef, "trickle method subclass deletes"); |
802
|
|
|
|
|
|
|
$test->is(Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->trick_attr1(), undef, "subclass still sees undef as value"); |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->trick_attr1("super value"), "super value", "Super class re-mutates"); |
805
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->trick_attr1("sub value"), "sub value", "Subclass re-mutates"); |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->trick_attr1(), "super value", "Super class unaffected on access"); |
808
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->trick_attr1("new super value"), "new super value", "Super class re-mutates"); |
809
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_class_attr::Subclass1->trick_attr1(), "sub value", "Sub class unaffected on access"); |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_trickle_class_attr::Subclass5; |
812
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
sub conf { |
815
|
|
|
|
|
|
|
return undef; |
816
|
|
|
|
|
|
|
}; |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
package __PACKAGE__; |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
{ |
821
|
|
|
|
|
|
|
local $@ = undef; |
822
|
|
|
|
|
|
|
eval { |
823
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::add_trickle_class_attr::Subclass5->add_class_attr('test_trickle_attr'); |
824
|
|
|
|
|
|
|
}; |
825
|
|
|
|
|
|
|
$test->like($@, qr/^Conf file error :/, 'could not add trickle class attr w/o conf file'); |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=end btest(add_trickle_class_attr) |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
=cut |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
=pod |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
=item add_default_class_attr |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
This adds a class attribute that is considered to be 'read-only' - it gets its value exclusively |
837
|
|
|
|
|
|
|
and utterly only from the conf file. Any modifications to this value are discarded in favor of the |
838
|
|
|
|
|
|
|
conf file value |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
=cut |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
sub add_default_class_attr { |
843
|
|
|
|
|
|
|
|
844
|
23
|
|
|
23
|
1
|
697
|
my $pkg = shift; |
845
|
23
|
|
|
|
|
35
|
my $method = shift; |
846
|
|
|
|
|
|
|
|
847
|
8
|
|
|
8
|
|
55
|
no strict 'refs'; |
|
8
|
|
|
|
|
18
|
|
|
8
|
|
|
|
|
5806
|
|
848
|
|
|
|
|
|
|
|
849
|
23
|
100
|
|
|
|
39
|
return $method if *{$pkg . "::$method"}{'CODE'}; |
|
23
|
|
|
|
|
187
|
|
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
#Slick. We'll use a proper closure here. |
852
|
22
|
|
|
|
|
42
|
my $attr = undef; |
853
|
22
|
|
|
|
|
104
|
*{$pkg . "::$method"} = sub { |
854
|
|
|
|
|
|
|
|
855
|
169
|
|
|
169
|
|
710
|
my $class = shift; |
856
|
|
|
|
|
|
|
|
857
|
169
|
50
|
|
|
|
548
|
my $conf = $pkg->conf or die "Conf file error : could not read conf file"; |
858
|
|
|
|
|
|
|
|
859
|
169
|
100
|
|
|
|
393
|
$conf->{$pkg}->{$method} = shift if @_; |
860
|
|
|
|
|
|
|
|
861
|
169
|
|
|
|
|
853
|
return $conf->{$pkg}->{$method}; |
862
|
22
|
|
|
|
|
102
|
}; |
863
|
|
|
|
|
|
|
|
864
|
22
|
|
|
|
|
69
|
$pkg->_class_attributes->{$method}++; |
865
|
|
|
|
|
|
|
|
866
|
22
|
|
|
|
|
60
|
return $method; |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
} |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
=pod |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
=begin btest(add_default_attr) |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_default_class_attr::subclass; |
875
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
package __PACKAGE__; |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::add_default_class_attr::subclass->add_default_class_attr('some_test_attr'), "Added default class attribute"); |
880
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::add_default_class_attr::subclass->add_default_class_attr('some_test_attr'), "Re-added default class attribute"); |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_default_class_attr::Subclass5; |
883
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
sub conf { |
886
|
|
|
|
|
|
|
return undef; |
887
|
|
|
|
|
|
|
}; |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
package __PACKAGE__; |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
{ |
892
|
|
|
|
|
|
|
local $@ = undef; |
893
|
|
|
|
|
|
|
eval { |
894
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::add_default_class_attr::Subclass5->add_class_attr('test_default_attr'); |
895
|
|
|
|
|
|
|
}; |
896
|
|
|
|
|
|
|
$test->like($@, qr/^Conf file error :/, 'could not add default class attr w/o conf file'); |
897
|
|
|
|
|
|
|
} |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
=end btest(add_default_attr) |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
=cut |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
=pod |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
=item attributes |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
Returns the attributes available to this object, based off of the flag passed in - "instance", "class", or "both". |
908
|
|
|
|
|
|
|
defaults to "instance". |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
Note - this method will not return attributes that begin with a leading underscore, as a courtesy. |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
=cut |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
sub attributes { |
915
|
8
|
|
|
8
|
1
|
5896
|
my $class = shift->pkg; |
916
|
8
|
|
100
|
|
|
30
|
my $type = shift || 'instance'; |
917
|
|
|
|
|
|
|
|
918
|
8
|
|
|
|
|
13
|
my @attributes = (); |
919
|
|
|
|
|
|
|
|
920
|
8
|
100
|
|
|
|
28
|
if ($type eq 'instance') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
921
|
3
|
|
|
|
|
5
|
@attributes = keys %{$class->_instance_attributes}; |
|
3
|
|
|
|
|
10
|
|
922
|
|
|
|
|
|
|
} |
923
|
|
|
|
|
|
|
elsif ($type eq 'class') { |
924
|
2
|
|
|
|
|
5
|
@attributes = keys %{$class->_class_attributes}; |
|
2
|
|
|
|
|
6
|
|
925
|
|
|
|
|
|
|
} |
926
|
|
|
|
|
|
|
elsif ($type eq 'both') { |
927
|
2
|
|
|
|
|
4
|
@attributes = (keys %{$class->_instance_attributes}, keys %{$class->_class_attributes}); |
|
2
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
7
|
|
928
|
|
|
|
|
|
|
} |
929
|
|
|
|
|
|
|
else { |
930
|
1
|
|
|
|
|
15
|
return $class->error("Cannot get attributes - don't know how to get '$type'", "BO-37"); |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
|
933
|
7
|
|
|
|
|
31
|
return [sort grep {! /^_/} @attributes]; |
|
127
|
|
|
|
|
296
|
|
934
|
|
|
|
|
|
|
} |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
=pod |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
=begin btest(attributes) |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::attributes::Subclass1; |
941
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
942
|
|
|
|
|
|
|
my $subclass = "Basset::Test::Testing::__PACKAGE__::attributes::Subclass1"; |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
$subclass->add_attr('foo'); |
945
|
|
|
|
|
|
|
$subclass->add_attr('bar'); |
946
|
|
|
|
|
|
|
$subclass->add_class_attr('baz'); |
947
|
|
|
|
|
|
|
$subclass->add_trickle_class_attr('trick'); |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
$test->is(ref $subclass->attributes('instance'), 'ARRAY', 'instance attributes is array'); |
950
|
|
|
|
|
|
|
$test->is(ref $subclass->attributes('class'), 'ARRAY', 'class attributes is array'); |
951
|
|
|
|
|
|
|
$test->is(ref $subclass->attributes('both'), 'ARRAY', 'both attributes is array'); |
952
|
|
|
|
|
|
|
$test->is(scalar $subclass->attributes('invalid'), undef, 'non token attributes is error'); |
953
|
|
|
|
|
|
|
$test->is($subclass->errcode, 'BO-37', 'proper error code'); |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
my $instance = { map {$_ => 1} @{$subclass->attributes} }; |
956
|
|
|
|
|
|
|
$test->is($instance->{'foo'}, 1, 'foo is instance attribute from anon'); |
957
|
|
|
|
|
|
|
$test->is($instance->{'bar'}, 1, 'bar is instance attribute from anon'); |
958
|
|
|
|
|
|
|
$test->is($instance->{'baz'}, undef, 'baz is not instance attribute from anon'); |
959
|
|
|
|
|
|
|
$test->is($instance->{'trick'}, undef, 'trick is not instance attribute from anon'); |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
my $instance_ex = { map {$_ => 1} @{$subclass->attributes('instance')} }; |
962
|
|
|
|
|
|
|
$test->is($instance_ex->{'foo'}, 1, 'foo is instance attribute from explicit'); |
963
|
|
|
|
|
|
|
$test->is($instance_ex->{'bar'}, 1, 'bar is instance attribute from explicit'); |
964
|
|
|
|
|
|
|
$test->is($instance_ex->{'baz'}, undef, 'baz is not instance attribute from explicit'); |
965
|
|
|
|
|
|
|
$test->is($instance_ex->{'trick'}, undef, 'trick is not instance attribute from explicit'); |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
my $both = { map {$_ => 1} @{$subclass->attributes('both')} }; |
968
|
|
|
|
|
|
|
$test->is($both->{'foo'}, 1, 'foo is instance attribute from both'); |
969
|
|
|
|
|
|
|
$test->is($both->{'bar'}, 1, 'bar is instance attribute from both'); |
970
|
|
|
|
|
|
|
$test->is($both->{'baz'}, 1, 'baz is class attribute from both'); |
971
|
|
|
|
|
|
|
$test->is($both->{'trick'}, 1, 'trick is class attribute from both'); |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
my $class = { map {$_ => 1} @{$subclass->attributes('class')} }; |
974
|
|
|
|
|
|
|
$test->is($class->{'foo'}, undef, 'foo is not instance attribute from class'); |
975
|
|
|
|
|
|
|
$test->is($class->{'bar'}, undef, 'bar is not instance attribute from class'); |
976
|
|
|
|
|
|
|
$test->is($class->{'baz'}, 1, 'baz is class attribute from both'); |
977
|
|
|
|
|
|
|
$test->is($class->{'trick'}, 1, 'trick is class attribute from class'); |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
=end btest(attributes) |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
=cut |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
=pod |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
=item is_attribute |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
=cut |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
sub is_attribute { |
990
|
28
|
|
|
28
|
1
|
105
|
my $class = shift->pkg; |
991
|
28
|
|
|
|
|
43
|
my $attribute = shift; |
992
|
28
|
|
100
|
|
|
68
|
my $type = shift || 'instance'; |
993
|
|
|
|
|
|
|
|
994
|
28
|
100
|
|
|
|
62
|
if ($type eq 'both') { |
995
|
16
|
|
100
|
|
|
46
|
return $class->_instance_attributes->{$attribute} || $class->_class_attributes->{$attribute} || 0; |
996
|
|
|
|
|
|
|
} |
997
|
12
|
100
|
|
|
|
30
|
if ($type eq 'instance') { |
|
|
100
|
|
|
|
|
|
998
|
8
|
|
100
|
|
|
18
|
return $class->_instance_attributes->{$attribute} || 0; |
999
|
|
|
|
|
|
|
} |
1000
|
|
|
|
|
|
|
elsif ($type eq 'class') { |
1001
|
3
|
|
100
|
|
|
11
|
return $class->_class_attributes->{$attribute} || 0; |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
else { |
1004
|
1
|
|
|
|
|
14
|
return $class->error("Cannot determine is_attribute for flag : $type", "BO-38"); |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
} |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
=pod |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
=begin btest(is_attribute) |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::is_attribute::Subclass1; |
1014
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
1015
|
|
|
|
|
|
|
my $subclass = "Basset::Test::Testing::__PACKAGE__::is_attribute::Subclass1"; |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
$subclass->add_attr('ins1'); |
1018
|
|
|
|
|
|
|
$subclass->add_attr('ins2'); |
1019
|
|
|
|
|
|
|
$subclass->add_class_attr('class'); |
1020
|
|
|
|
|
|
|
$subclass->add_trickle_class_attr('trick'); |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('ins1') != 0, 'ins1 is instance by default'); |
1023
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('ins2') != 0, 'ins2 is instance by default'); |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('ins1', 'instance') != 0, 'ins1 is instance by explicitly'); |
1026
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('ins2', 'instance') != 0, 'ins2 is instance by explicitly'); |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('class') == 0, 'class is not attribute by default'); |
1029
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('class', 'class') != 0, 'class is class attribute by default'); |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('trick') == 0, 'trick is not attribute by default'); |
1032
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('trick', 'class') != 0, 'trick is class attribute by default'); |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('ins1', 'both') != 0, 'ins1 is instance by both'); |
1035
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('ins2', 'both') != 0, 'ins2 is instance by both'); |
1036
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('trick', 'both') != 0, 'trick is class attribute by both'); |
1037
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('class', 'both') != 0, 'class is class attribute by both'); |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('fake_instance') == 0, 'fake_instance is not attribute by default'); |
1040
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('fake_instance','both') == 0, 'fake_instance is not attribute by both'); |
1041
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('fake_instance','instance') == 0, 'fake_instance is not attribute by instance'); |
1042
|
|
|
|
|
|
|
$test->ok($subclass->is_attribute('fake_instance','class') == 0, 'fake_instance is not attribute by class'); |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
$test->is(scalar $subclass->is_attribute('ins1', 'invalid'), undef, "invalid is_attribute flag is error condition"); |
1045
|
|
|
|
|
|
|
$test->is($subclass->errcode, "BO-38", "proper error code"); |
1046
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
=end btest(is_attribute) |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
=cut |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
=pod |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
=item add_wrapper |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
You can now wrapper methods with before and after hooks that will get executed before or after the method, as desired. Syntax is: |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
$class->add_wrapper('(before|after)', 'method_name', 'wrapper_name'); |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
That is, either before or after method_name is called, call wrapper_name first. Before wrappers are good to change the |
1060
|
|
|
|
|
|
|
values going into a method, after wrappers are good to change the values coming back out. |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
For example, |
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
sub foo_wrapper { |
1065
|
|
|
|
|
|
|
my $self = shift; |
1066
|
|
|
|
|
|
|
my @args = @_; # (whatever was passed in to foo) |
1067
|
|
|
|
|
|
|
print "I am executing foo!\n"; |
1068
|
|
|
|
|
|
|
return 1; |
1069
|
|
|
|
|
|
|
} |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
$class->add_wrapper('before', 'foo', 'foo_wrapper'); |
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
Now, $class->foo() is functionally the same as: |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
if ($class->foo_wrapper) { |
1076
|
|
|
|
|
|
|
$class->foo(); |
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
Ditto for the after wrapper. |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
if ($class->foo) { |
1082
|
|
|
|
|
|
|
$class->after_foo_wrapper; |
1083
|
|
|
|
|
|
|
} |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
Wrappers are run in reverse add order. That is, wrappers added later are executed before wrappers added earlier. |
1086
|
|
|
|
|
|
|
Wrappers are inherited in subclasses. Subclasses run all of their wrappers in reverse add order, then run all |
1087
|
|
|
|
|
|
|
super class wrappers in reverse add order. |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
Wrapper functions should return a true value upon success, or set an error upon failure. |
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
Performance hit is fairly negligible, since add_wrapper re-wires the symbol table. So be careful using this |
1092
|
|
|
|
|
|
|
functionality with other methods that may re-wire the symbol table (such as Basset::Object::Persistent's _instantiating_accessor) |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
See also the extended syntax for add_attr, and Basset::Object::Persistent's import_from_db and export_to_db methods |
1095
|
|
|
|
|
|
|
for different places to add in hooks, as well as the delegate attribute, below, for another way to extend code. |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
The performance hit for wrappers is reasonably small, but if a wrappered method is constantly being hit and the |
1098
|
|
|
|
|
|
|
wrapping code isn't always used (for example, wrapping an attribute. If your wrapper only does anything |
1099
|
|
|
|
|
|
|
upon mutation, it's wasteful, since the wrapper will still -always- be called), you can suffer badly. In those |
1100
|
|
|
|
|
|
|
cases, an extended attribute or an explicit wrapper function of your own may be more useful. Please note that wrappers |
1101
|
|
|
|
|
|
|
can only be defined on a per-method basis. If you want to re-use wrappers across multiple methods, you'll need your |
1102
|
|
|
|
|
|
|
own wrapping mechanism. For example, using the extended attribute syntax to use a different accessor method. |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
There is an optional fourth argument - the conditional operator. This is a method (or coderef called as a method) that |
1105
|
|
|
|
|
|
|
is executed before the wrapper is called. If the conditional returns true, the wrapper is then executed. If the conditional |
1106
|
|
|
|
|
|
|
returns false, the wrapper is not executed. |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
Some::Class->add_wrapper('after', 'name', 'validation', sub { |
1109
|
|
|
|
|
|
|
my $self = shift; |
1110
|
|
|
|
|
|
|
return @_; |
1111
|
|
|
|
|
|
|
} ); |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
That wrapper will only call the 'validation' method upon mutation (that is, when there are arguments passed) and |
1114
|
|
|
|
|
|
|
not upon simple access. |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
Subclasses may define additional wrapper types. |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
Please don't wrapper attributes. Things may break if the attribute value is legitimately undef (normally an error condition). Instead, |
1119
|
|
|
|
|
|
|
use the extended add_attr syntax to define a new accessor method for the attribute you wish to wrap. Or simply write your own subroutine |
1120
|
|
|
|
|
|
|
and directly call a separately added attribute yourself. |
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
=cut |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
sub add_wrapper { |
1125
|
|
|
|
|
|
|
|
1126
|
15
|
|
|
15
|
1
|
496
|
my $class = shift; |
1127
|
15
|
100
|
|
|
|
49
|
my $type = shift or return $class->error("Cannot add wrapper w/o type", "BO-31"); |
1128
|
14
|
100
|
|
|
|
37
|
my $method = shift or return $class->error("Cannot add wrapper w/o attribute", "BO-32"); |
1129
|
13
|
100
|
|
|
|
33
|
my $wrapper = shift or return $class->error("Cannot add wrapper w/o wrapper", "BO-33"); |
1130
|
12
|
|
100
|
|
|
60
|
my $conditional = shift || 'no_op'; |
1131
|
|
|
|
|
|
|
|
1132
|
12
|
100
|
|
|
|
115
|
return $class->error("Cannot add wrapper : class does not know how to $method", "BO-34") |
1133
|
|
|
|
|
|
|
unless $class->can($method); |
1134
|
|
|
|
|
|
|
|
1135
|
11
|
100
|
|
|
|
92
|
return $class->error("Cannot add wrapper : $method is an attribute. Explicitly wrapper or use a new accessor method", "BO-39") |
1136
|
|
|
|
|
|
|
if $class->is_attribute($method, 'both'); |
1137
|
|
|
|
|
|
|
|
1138
|
10
|
|
|
|
|
66
|
my $private = $class->privatize("privately_wrappered_$method"); |
1139
|
|
|
|
|
|
|
|
1140
|
8
|
|
|
8
|
|
62
|
no strict 'refs'; |
|
8
|
|
|
|
|
27
|
|
|
8
|
|
|
|
|
408
|
|
1141
|
8
|
|
|
8
|
|
42
|
no warnings; |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
17730
|
|
1142
|
|
|
|
|
|
|
|
1143
|
10
|
|
|
|
|
17
|
my $ptr; |
1144
|
|
|
|
|
|
|
|
1145
|
10
|
100
|
|
|
|
9
|
if (*{$class . "::$method"}{'CODE'}) { |
|
10
|
|
|
|
|
54
|
|
1146
|
|
|
|
|
|
|
|
1147
|
9
|
|
|
|
|
12
|
*{$class . "::$private"} = *{$class . "::$method"}{'CODE'}; |
|
9
|
|
|
|
|
75
|
|
|
9
|
|
|
|
|
31
|
|
1148
|
|
|
|
|
|
|
#if it's local to us, we're carefully hiding the function, so we need to look |
1149
|
|
|
|
|
|
|
#at an actual reference to the original |
1150
|
9
|
|
|
|
|
11
|
$ptr = *{$class . "::$private"}{'CODE'}; |
|
9
|
|
|
|
|
37
|
|
1151
|
|
|
|
|
|
|
} else { |
1152
|
|
|
|
|
|
|
#otherwise, we need to find out who owns it, and keep a soft pointer to it. |
1153
|
1
|
|
|
|
|
2
|
my @parents = reverse @{$class->isa_path}; |
|
1
|
|
|
|
|
5
|
|
1154
|
1
|
|
|
|
|
3
|
foreach my $parent (@parents) { |
1155
|
2
|
100
|
|
|
|
3
|
if (*{$parent . "::$method"}{'CODE'}) { |
|
2
|
|
|
|
|
12
|
|
1156
|
|
|
|
|
|
|
# but, if it's the parent's, then we need to only point to the name of the method |
1157
|
|
|
|
|
|
|
# in the parent's class. This allows the parent to add a wrapper on this method |
1158
|
|
|
|
|
|
|
# after we do, and we still get it. |
1159
|
1
|
|
|
|
|
3
|
$ptr = "${parent}::$method"; |
1160
|
1
|
|
|
|
|
4
|
last; |
1161
|
|
|
|
|
|
|
} |
1162
|
|
|
|
|
|
|
} |
1163
|
|
|
|
|
|
|
} |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
#of course, we can't do anything unless our wrapper is something the class can do, or it's an anonymous method |
1166
|
10
|
100
|
100
|
|
|
138
|
return $class->error("Cannot add wrapper: Class cannot $wrapper", "BO-35") |
1167
|
|
|
|
|
|
|
unless $class->can($wrapper) || ref $wrapper eq 'CODE'; |
1168
|
|
|
|
|
|
|
|
1169
|
9
|
100
|
|
|
|
32
|
if ($type eq 'before') { |
|
|
100
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
|
1171
|
7
|
|
|
|
|
46
|
*{$class . "::$method"} = sub { |
1172
|
24
|
|
|
24
|
|
38
|
my $self = shift; |
1173
|
|
|
|
|
|
|
|
1174
|
24
|
100
|
|
|
|
85
|
if ($self->$conditional(@_)) { |
1175
|
23
|
50
|
|
|
|
107
|
$self->$wrapper($ptr, @_) or return; |
1176
|
|
|
|
|
|
|
} |
1177
|
|
|
|
|
|
|
|
1178
|
24
|
|
|
|
|
75
|
return $self->$ptr(@_); |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
} |
1181
|
7
|
|
|
|
|
41
|
} |
1182
|
|
|
|
|
|
|
elsif ($type eq 'after') { |
1183
|
1
|
|
|
|
|
7
|
*{$class . "::$method"} = sub { |
1184
|
6
|
|
|
6
|
|
12
|
my $self = shift; |
1185
|
|
|
|
|
|
|
|
1186
|
6
|
50
|
|
|
|
19
|
my $rc = $self->$ptr(@_) or return; |
1187
|
|
|
|
|
|
|
|
1188
|
6
|
50
|
|
|
|
58
|
return $self->$conditional(@_) ? $self->$wrapper($ptr, $rc, @_) : $rc; |
1189
|
|
|
|
|
|
|
} |
1190
|
1
|
|
|
|
|
7
|
} else { |
1191
|
1
|
|
|
|
|
6
|
return $class->error("Cannot add wrapper: unknown type $type", "BO-36"); |
1192
|
|
|
|
|
|
|
} |
1193
|
|
|
|
|
|
|
|
1194
|
8
|
|
|
|
|
41
|
return 1; |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
} |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
=pod |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
=begin btest(add_wrapper) |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
my $subclass = "Basset::Test::Testing::__PACKAGE__::add_wrapper"; |
1203
|
|
|
|
|
|
|
my $subclass2 = "Basset::Test::Testing::__PACKAGE__::add_wrapper2"; |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_wrapper; |
1206
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
$subclass->add_attr('attr1'); |
1209
|
|
|
|
|
|
|
$subclass->add_attr('attr2'); |
1210
|
|
|
|
|
|
|
$subclass->add_attr('before_wrapper'); |
1211
|
|
|
|
|
|
|
$subclass->add_attr('before_wrapper2'); |
1212
|
|
|
|
|
|
|
$subclass->add_attr('after_wrapper'); |
1213
|
|
|
|
|
|
|
$subclass->add_attr('after_wrapper2'); |
1214
|
|
|
|
|
|
|
$subclass->add_attr('code_wrapper'); |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
my ($meth1, $meth2, $meth3, $meth4); |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
sub meth1 { |
1219
|
|
|
|
|
|
|
my $self = shift; |
1220
|
|
|
|
|
|
|
$meth1 = shift if @_; |
1221
|
|
|
|
|
|
|
return $meth1; |
1222
|
|
|
|
|
|
|
} |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
sub meth2 { |
1225
|
|
|
|
|
|
|
my $self = shift; |
1226
|
|
|
|
|
|
|
$meth2 = shift if @_; |
1227
|
|
|
|
|
|
|
return $meth2; |
1228
|
|
|
|
|
|
|
} |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
sub meth3 { |
1231
|
|
|
|
|
|
|
my $self = shift; |
1232
|
|
|
|
|
|
|
$meth3 = shift if @_; |
1233
|
|
|
|
|
|
|
return $meth3; |
1234
|
|
|
|
|
|
|
} |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
sub meth4 { |
1237
|
|
|
|
|
|
|
my $self = shift; |
1238
|
|
|
|
|
|
|
$meth4 = shift if @_; |
1239
|
|
|
|
|
|
|
return $meth4; |
1240
|
|
|
|
|
|
|
} |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
sub wrapper1 {shift->before_wrapper('set')}; |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
sub wrapper2 { |
1245
|
|
|
|
|
|
|
$_[0]->before_wrapper('B4SET'); |
1246
|
|
|
|
|
|
|
$_[0]->before_wrapper2('set2'); |
1247
|
|
|
|
|
|
|
} |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
sub wrapper3 { |
1250
|
|
|
|
|
|
|
$_[0]->before_wrapper('ASET1'); |
1251
|
|
|
|
|
|
|
$_[0]->before_wrapper2('ASET2'); |
1252
|
|
|
|
|
|
|
return $_[2]; |
1253
|
|
|
|
|
|
|
} |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
sub wrapper5 { |
1256
|
|
|
|
|
|
|
$_[0]->before_wrapper('5-BSET1'); |
1257
|
|
|
|
|
|
|
$_[0]->before_wrapper2('5-BSET2'); |
1258
|
|
|
|
|
|
|
$_[0]->after_wrapper('5-ASET1'); |
1259
|
|
|
|
|
|
|
$_[0]->after_wrapper2('5-ASET2'); |
1260
|
|
|
|
|
|
|
} |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
sub conditional_true { |
1263
|
|
|
|
|
|
|
return 1; |
1264
|
|
|
|
|
|
|
} |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
sub conditional_false { |
1267
|
|
|
|
|
|
|
my $self = shift; |
1268
|
|
|
|
|
|
|
return $self->error("failed false condition", "conditional_false_error_code"); |
1269
|
|
|
|
|
|
|
} |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_wrapper2; |
1272
|
|
|
|
|
|
|
our @ISA = ($subclass); |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
sub wrapper4 { |
1275
|
|
|
|
|
|
|
shift->after_wrapper('AWRAPPER'); |
1276
|
|
|
|
|
|
|
} |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
package __PACKAGE__; |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
$test->ok(! $subclass->add_wrapper, "Cannot add wrapper w/o type"); |
1281
|
|
|
|
|
|
|
$test->is($subclass->errcode, "BO-31", "proper error code"); |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
$test->ok(! $subclass->add_wrapper('before'), "Cannot add wrapper w/o attribute"); |
1284
|
|
|
|
|
|
|
$test->is($subclass->errcode, "BO-32", "proper error code"); |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
$test->ok(! $subclass->add_wrapper('before', 'bogus_wrapper'), "Cannot add wrapper w/o wrapper"); |
1287
|
|
|
|
|
|
|
$test->is($subclass->errcode, "BO-33", "proper error code"); |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
$test->ok(! $subclass->add_wrapper('before', 'bogus_attribute', 'bogus_wrapper'), "Cannot add wrapper: bogus attribute"); |
1290
|
|
|
|
|
|
|
$test->is($subclass->errcode, "BO-34", "proper error code"); |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
$test->ok(! $subclass->add_wrapper('before', 'attr2', 'bogus_wrapper'), "Cannot add wrapper: cannot wrapper attributes"); |
1293
|
|
|
|
|
|
|
$test->is($subclass->errcode, "BO-39", "proper error code"); |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
$test->ok(! $subclass->add_wrapper('before', 'meth2', 'bogus_wrapper'), "Cannot add wrapper: bogus wrapper"); |
1296
|
|
|
|
|
|
|
$test->is($subclass->errcode, "BO-35", "proper error code"); |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
$test->ok(! $subclass->add_wrapper('junk', 'meth2', 'wrapper1'), "Cannot add wrapper: bogus type"); |
1299
|
|
|
|
|
|
|
$test->is($subclass->errcode, "BO-36", "proper error code"); |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_wrapper('before', 'meth1', 'wrapper1'), "added wrapper to ref"); |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
my $o = $subclass->new(); |
1304
|
|
|
|
|
|
|
$test->ok($o, "got object"); |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
$test->is($o->before_wrapper, undef, "before_wrapper is undef"); |
1307
|
|
|
|
|
|
|
$test->is($o->meth1('foo'), 'foo', 'set meth1 to foo'); |
1308
|
|
|
|
|
|
|
$test->is($o->before_wrapper, 'set', 'before_wrapper is set'); |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
$test->is($o->before_wrapper(undef), undef, "before_wrapper is undef"); |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_wrapper('before', 'meth1', 'wrapper2'), "added wrapper to ref"); |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
$test->is($o->before_wrapper, undef, "before_wrapper is undef"); |
1315
|
|
|
|
|
|
|
$test->is($o->meth1('bar'), 'bar', 'set meth1 to baz'); |
1316
|
|
|
|
|
|
|
$test->is($o->before_wrapper, 'set', 'before_wrapper is set'); |
1317
|
|
|
|
|
|
|
$test->is($o->before_wrapper2, 'set2', 'before_wrapper2 is set2'); |
1318
|
|
|
|
|
|
|
$test->is($o->after_wrapper, undef, 'after_wrapper is undef'); |
1319
|
|
|
|
|
|
|
$test->is($o->after_wrapper2, undef, 'after_wrapper2 is undef'); |
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
$test->is($o->before_wrapper(undef), undef, "before_wrapper is undef"); |
1322
|
|
|
|
|
|
|
$test->is($o->before_wrapper2(undef), undef, "before_wrapper2 is undef"); |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_wrapper('after', 'meth1', 'wrapper3'), "added after wrapper to ref"); |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
$test->is($o->before_wrapper, undef, "before_wrapper is undef"); |
1327
|
|
|
|
|
|
|
$test->is($o->meth1('baz'), 'baz', 'set meth1 to baz'); |
1328
|
|
|
|
|
|
|
$test->is($o->before_wrapper, 'ASET1', 'before_wrapper is ASET1'); |
1329
|
|
|
|
|
|
|
$test->is($o->before_wrapper2, 'ASET2', 'before_wrapper2 is ASET2'); |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
my $o2 = $subclass2->new(); |
1332
|
|
|
|
|
|
|
$test->ok($o2, "got sub object"); |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
$test->ok(scalar $subclass2->add_wrapper('before', 'meth1', 'wrapper4'), "added after wrapper to ref"); |
1335
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
$test->is($o2->before_wrapper, undef, "before_wrapper is undef"); |
1337
|
|
|
|
|
|
|
$test->is($o2->meth1('baz'), 'baz', 'set meth1 to baz'); |
1338
|
|
|
|
|
|
|
$test->is($o2->before_wrapper, 'ASET1', 'before_wrapper is ASET1'); |
1339
|
|
|
|
|
|
|
$test->is($o2->before_wrapper2, 'ASET2', 'before_wrapper2 is ASET2'); |
1340
|
|
|
|
|
|
|
$test->is($o2->after_wrapper, 'AWRAPPER', 'after_wrapper is AWRAPPER'); |
1341
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
$test->is($o->before_wrapper(undef), undef, "before_wrapper is undef"); |
1343
|
|
|
|
|
|
|
$test->is($o->before_wrapper2(undef), undef, "before_wrapper2 is undef"); |
1344
|
|
|
|
|
|
|
$test->is($o->after_wrapper(undef), undef, "after_wrapper2 is undef"); |
1345
|
|
|
|
|
|
|
$test->is($o->after_wrapper2(undef), undef, "after_wrapper2 is undef"); |
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_wrapper('before', 'meth1', 'wrapper5'), "added before wrapper to ref"); |
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
$test->is($o->before_wrapper, undef, "before_wrapper is undef"); |
1350
|
|
|
|
|
|
|
$test->is($o->meth1('bar'), 'bar', 'set meth1 to baz'); |
1351
|
|
|
|
|
|
|
$test->is($o->before_wrapper, 'ASET1', 'before_wrapper is set ASET1'); |
1352
|
|
|
|
|
|
|
$test->is($o->before_wrapper2, 'ASET2', 'before_wrapper2 is ASET2'); |
1353
|
|
|
|
|
|
|
$test->is($o->after_wrapper, '5-ASET1', 'after_wrapper is 5-ASET1'); |
1354
|
|
|
|
|
|
|
$test->is($o->after_wrapper2, '5-ASET2', 'after_wrapper2 is 5-ASET2'); |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
$test->is($o2->before_wrapper(undef), undef, "before_wrapper is undef"); |
1357
|
|
|
|
|
|
|
$test->is($o2->before_wrapper2(undef), undef, "before_wrapper2 is undef"); |
1358
|
|
|
|
|
|
|
$test->is($o2->after_wrapper(undef), undef, "after_wrapper2 is undef"); |
1359
|
|
|
|
|
|
|
$test->is($o2->after_wrapper2(undef), undef, "after_wrapper2 is undef"); |
1360
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
$test->is($o2->before_wrapper, undef, "before_wrapper is undef"); |
1362
|
|
|
|
|
|
|
$test->is($o2->meth1('bar'), 'bar', 'set meth1 to baz'); |
1363
|
|
|
|
|
|
|
$test->is($o2->before_wrapper, 'ASET1', 'before_wrapper is set ASET1'); |
1364
|
|
|
|
|
|
|
$test->is($o2->before_wrapper2, 'ASET2', 'before_wrapper2 is ASET2'); |
1365
|
|
|
|
|
|
|
$test->is($o2->after_wrapper, '5-ASET1', 'after_wrapper is 5-ASET1'); |
1366
|
|
|
|
|
|
|
$test->is($o2->after_wrapper2, '5-ASET2', 'after_wrapper2 is 5-ASET2'); |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
$test->is($o->before_wrapper(undef), undef, "before_wrapper is undef"); |
1369
|
|
|
|
|
|
|
$test->is($o->before_wrapper2(undef), undef, "before_wrapper2 is undef"); |
1370
|
|
|
|
|
|
|
$test->is($o->after_wrapper(undef), undef, "after_wrapper2 is undef"); |
1371
|
|
|
|
|
|
|
$test->is($o->after_wrapper2(undef), undef, "after_wrapper2 is undef"); |
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
$test->is($o->before_wrapper, undef, "before_wrapper is undef"); |
1374
|
|
|
|
|
|
|
$test->is($o->meth1('bar'), 'bar', 'set meth1 to baz'); |
1375
|
|
|
|
|
|
|
$test->is($o->before_wrapper, 'ASET1', 'before_wrapper is set ASET1'); |
1376
|
|
|
|
|
|
|
$test->is($o->before_wrapper2, 'ASET2', 'before_wrapper2 is ASET2'); |
1377
|
|
|
|
|
|
|
$test->is($o->after_wrapper, '5-ASET1', 'after_wrapper is 5-ASET1'); |
1378
|
|
|
|
|
|
|
$test->is($o->after_wrapper2, '5-ASET2', 'after_wrapper2 is 5-ASET2'); |
1379
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_wrapper('before', 'meth1', sub {$_[0]->code_wrapper('SET CODE WRAP'); return 1}), 'added coderef wrapper'); |
1381
|
|
|
|
|
|
|
$test->is($o->meth1('code'), 'code', 'set meth1 to code'); |
1382
|
|
|
|
|
|
|
$test->is($o->code_wrapper, 'SET CODE WRAP', 'properly used coderef wrapper'); |
1383
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_wrapper('before', 'meth3', 'wrapper1', 'conditional_true'), "added conditional_true wrapper"); |
1385
|
|
|
|
|
|
|
$test->is($o->before_wrapper(undef), undef, "wiped out before_wrapper"); |
1386
|
|
|
|
|
|
|
$test->is($o->meth3('meth 3 val'), 'meth 3 val', 'properly set method 3 value'); |
1387
|
|
|
|
|
|
|
$test->is($o->before_wrapper, 'set', 'set before_wrapper'); |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_wrapper('before', 'meth4', 'wrapper1', 'conditional_false'), "added conditional_false wrapper"); |
1390
|
|
|
|
|
|
|
$test->is($o->before_wrapper(undef), undef, "wiped out before_wrapper"); |
1391
|
|
|
|
|
|
|
$test->is($o->meth4('meth 4 val'), 'meth 4 val', 'could not set method 4 value'); |
1392
|
|
|
|
|
|
|
$test->is($o->errcode, 'conditional_false_error_code', 'proper error code'); |
1393
|
|
|
|
|
|
|
$test->is($o->before_wrapper, undef, 'could not set before_wrapper'); |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
=end btest(add_wrapper) |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
=cut |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
=pod |
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
=item error and errcode |
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
error rocks. All error reporting is set and relayed through error. It's a standard accessor, and an *almost* |
1404
|
|
|
|
|
|
|
standard mutator. The difference is that when used as a mutator, it returns undef instead of the value |
1405
|
|
|
|
|
|
|
mutated to. |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
If a method fails, it is expected to return undef and set error. |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
example: |
1410
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
sub someMethod { |
1412
|
|
|
|
|
|
|
my $self = shift; |
1413
|
|
|
|
|
|
|
my $value = shift; |
1414
|
|
|
|
|
|
|
|
1415
|
|
|
|
|
|
|
if ($value > 10){ |
1416
|
|
|
|
|
|
|
return 1; #success |
1417
|
|
|
|
|
|
|
} |
1418
|
|
|
|
|
|
|
else { |
1419
|
|
|
|
|
|
|
return $self->error("Values must be greater than 10"); |
1420
|
|
|
|
|
|
|
}; |
1421
|
|
|
|
|
|
|
}; |
1422
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
$object->someMethod(15) || die $object->error; #succeeds |
1424
|
|
|
|
|
|
|
$object->someMethod(5) || die $object->error; #dies with an error..."Values must be greater than 10" |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
Be warned if your method can return '0', this is a valid successful return and shouldn't give an error. |
1427
|
|
|
|
|
|
|
But most of the time, you're fine with "true is success, false is failure" |
1428
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
As you can see in the example, we mutate the error attribute to the value passed, but it returns undef. |
1430
|
|
|
|
|
|
|
|
1431
|
|
|
|
|
|
|
However, error messages can change and can be difficult to parse. So we also have an error code, accessed |
1432
|
|
|
|
|
|
|
by errcode. This is expected to be consistent and machine parseable. It is mutated by the second argument |
1433
|
|
|
|
|
|
|
to ->error |
1434
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
example: |
1436
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
sub someMethod { |
1438
|
|
|
|
|
|
|
my $self = shift; |
1439
|
|
|
|
|
|
|
my $value = shift; |
1440
|
|
|
|
|
|
|
|
1441
|
|
|
|
|
|
|
if ($value > 10){ |
1442
|
|
|
|
|
|
|
return 1; #success |
1443
|
|
|
|
|
|
|
} |
1444
|
|
|
|
|
|
|
else { |
1445
|
|
|
|
|
|
|
return $self->error("Values must be greater than 10", "ERR77"); |
1446
|
|
|
|
|
|
|
}; |
1447
|
|
|
|
|
|
|
}; |
1448
|
|
|
|
|
|
|
|
1449
|
|
|
|
|
|
|
$object->someMethod(15) || die $object->error; #succeeds |
1450
|
|
|
|
|
|
|
$object->someMethod(5) || die $object->errcode; #dies with an error code ... "ERR77" |
1451
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
If your code is looking for an error, read the errcode. if a human is looking at it, display the error. |
1453
|
|
|
|
|
|
|
Easy as pie. |
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
Both classes and objects have error methods. |
1456
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
my $obj = Some::Class->new() || die Some::Class->error(); |
1458
|
|
|
|
|
|
|
$obj->foo() || die $obj->error(); |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
Note that error is a special method, and not just a normal accessor or class attribute. As such: |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
my $obj = Some::Class->new(); |
1463
|
|
|
|
|
|
|
Some::Class->error('foo'); |
1464
|
|
|
|
|
|
|
print $obj->error(); #prints undef |
1465
|
|
|
|
|
|
|
print Some::Class->error(); #prints foo |
1466
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
i.e., you will B get a class error message by calling ->error on an object. |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
error also posts an 'error' notification to the notification center. See Basset::NotificationCenter for more information. |
1470
|
|
|
|
|
|
|
The notification will not be posted if the optional third "silently" parameter is passed. |
1471
|
|
|
|
|
|
|
|
1472
|
|
|
|
|
|
|
Some::Class->error('foo', 'foo_code', 'silently'); |
1473
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
->error can (and will) die if an error occurs very very early in the compilation process, namely if an error |
1475
|
|
|
|
|
|
|
occurs before the 'exceptions' attribute is defined. It is assumed that if an error occurs that early on, it's a very |
1476
|
|
|
|
|
|
|
bad thing, and you should bail out. |
1477
|
|
|
|
|
|
|
|
1478
|
|
|
|
|
|
|
You may also always cause an exception by passing in the double plus secret fourth parameter - "throw anyway". |
1479
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
Some::Class->error('foo', 'foo_code', 0, 'HOLY COW BAIL OUT NOW!'); |
1481
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
Use the throw anyway parameter with care. It should be reserved to cover coding errors. An issue that if it occurs, there |
1483
|
|
|
|
|
|
|
is no way to continue and the programmer needs to fix it in advance. For example, _accessor throws an exception if you |
1484
|
|
|
|
|
|
|
try to call it as a class method, and with good reason. |
1485
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
=cut |
1487
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
=pod |
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
=begin btest(error) |
1491
|
|
|
|
|
|
|
|
1492
|
|
|
|
|
|
|
my $notes = 0; |
1493
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
sub notifier { |
1495
|
|
|
|
|
|
|
my $self = shift; |
1496
|
|
|
|
|
|
|
my $note = shift; |
1497
|
|
|
|
|
|
|
$notes++; |
1498
|
|
|
|
|
|
|
}; |
1499
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
my $center = __PACKAGE__->pkg_for_type('notificationcenter'); |
1501
|
|
|
|
|
|
|
$test->ok($center, "Got notification center class"); |
1502
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
$test->ok( |
1504
|
|
|
|
|
|
|
scalar |
1505
|
|
|
|
|
|
|
$center->addObserver( |
1506
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
1507
|
|
|
|
|
|
|
'notification' => 'error', |
1508
|
|
|
|
|
|
|
'object' => 'all', |
1509
|
|
|
|
|
|
|
'method' => 'notifier' |
1510
|
|
|
|
|
|
|
), "Added observer for error notifications" |
1511
|
|
|
|
|
|
|
); |
1512
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
1514
|
|
|
|
|
|
|
$test->ok($o, "Object created"); |
1515
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("classerr"), undef, "Class error set and returns undef"); |
1517
|
|
|
|
|
|
|
$test->is($notes, 1, "Posted a notification"); |
1518
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(), 'classerr', "Class error accesses"); |
1519
|
|
|
|
|
|
|
$test->is($notes, 1, "No notification"); |
1520
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("classerr2", "classcode2"), undef, "Class error and errcode set and returns undef"); |
1522
|
|
|
|
|
|
|
$test->is($notes, 2, "Posted a notification"); |
1523
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(), 'classerr2', "Class error accesses"); |
1524
|
|
|
|
|
|
|
$test->is($notes, 2, "No notification"); |
1525
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->errcode(), 'classcode2', "Class Class errcode accesses"); |
1526
|
|
|
|
|
|
|
$test->is($notes, 2, "No notification"); |
1527
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
$test->is(scalar $o->error("objerr"), undef, "Object error set and returns undef"); |
1529
|
|
|
|
|
|
|
$test->is($notes, 3, "Posted a notification"); |
1530
|
|
|
|
|
|
|
$test->is(scalar $o->error(), 'objerr', "Object error accesses"); |
1531
|
|
|
|
|
|
|
$test->is($notes, 3, "No notification"); |
1532
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(), 'classerr2', "Class error unaffected"); |
1533
|
|
|
|
|
|
|
$test->is($notes, 3, "No notification"); |
1534
|
|
|
|
|
|
|
|
1535
|
|
|
|
|
|
|
$test->is(scalar $o->error("objerr2", "objcode2"), undef, "Object error and errcode set and returns undef"); |
1536
|
|
|
|
|
|
|
$test->is($notes, 4, "Posted a notification"); |
1537
|
|
|
|
|
|
|
$test->is(scalar $o->error(), 'objerr2', "Object error accesses"); |
1538
|
|
|
|
|
|
|
$test->is($notes, 4, "No notification"); |
1539
|
|
|
|
|
|
|
$test->is(scalar $o->errcode(), 'objcode2', "Object errcode accesses"); |
1540
|
|
|
|
|
|
|
$test->is($notes, 4, "No notification"); |
1541
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(), 'classerr2', "Class error unaffected"); |
1542
|
|
|
|
|
|
|
$test->is($notes, 4, "No notification"); |
1543
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->errcode(), 'classcode2', "Class errcode unaffected"); |
1544
|
|
|
|
|
|
|
$test->is($notes, 4, "No notification"); |
1545
|
|
|
|
|
|
|
|
1546
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("classerr3", "clscode3"), undef, "Re-set class error"); |
1547
|
|
|
|
|
|
|
$test->is($notes, 5, "Posted notification"); |
1548
|
|
|
|
|
|
|
$test->is(scalar $o->error(), 'objerr2', "Object error unchanged"); |
1549
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1550
|
|
|
|
|
|
|
$test->is(scalar $o->errcode(), 'objcode2', "Object errcode unchanged"); |
1551
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1552
|
|
|
|
|
|
|
|
1553
|
|
|
|
|
|
|
$test->is(scalar $o->error("objerr3", "objcode3", "silently"), undef, "Silently set error"); |
1554
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1555
|
|
|
|
|
|
|
$test->is(scalar $o->error(), 'objerr3', "Object error accesses"); |
1556
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1557
|
|
|
|
|
|
|
$test->is(scalar $o->errcode(), 'objcode3', "Object errcode accesses"); |
1558
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1559
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(), 'classerr3', "Class error unaffected"); |
1560
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1561
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->errcode(), 'clscode3', "Class errcode unaffected"); |
1562
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1563
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
$test->is(scalar $o->error(["formatted error %d %.2f %s", 13, 3.14, "data"], "ec", "silently"), undef, "Object set formatted error"); |
1565
|
|
|
|
|
|
|
$test->is(scalar $o->error, "formatted error 13 3.14 data", "Formatted error accesses"); |
1566
|
|
|
|
|
|
|
$test->is(scalar $o->errcode, "ec", "Formatted errcode accesses"); |
1567
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(), 'classerr3', "Class error unaffected"); |
1568
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1569
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->errcode(), 'clscode3', "Class errcode unaffected"); |
1570
|
|
|
|
|
|
|
$test->is($notes, 5, "No notification"); |
1571
|
|
|
|
|
|
|
|
1572
|
|
|
|
|
|
|
my $confClass = __PACKAGE__->pkg_for_type('conf'); |
1573
|
|
|
|
|
|
|
$test->ok($confClass, "Got conf"); |
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
my $cfg = $confClass->conf; |
1576
|
|
|
|
|
|
|
$test->ok($cfg, "Got configuration"); |
1577
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
$test->ok($cfg->{"Basset::Object"}->{'exceptions'} = 1, "enables exceptions"); |
1579
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
eval { |
1581
|
|
|
|
|
|
|
$o->error("exception error", "excpcode"); |
1582
|
|
|
|
|
|
|
}; |
1583
|
|
|
|
|
|
|
$test->ok($@ =~ /^excpcode /, "Caught object exception code"); |
1584
|
|
|
|
|
|
|
$test->is($o->last_exception, "exception error", "Caught object exception"); |
1585
|
|
|
|
|
|
|
$test->is(__PACKAGE__->last_exception, "exception error", "Caught class exception"); |
1586
|
|
|
|
|
|
|
$test->is($notes, 6, "Posted a notification"); |
1587
|
|
|
|
|
|
|
|
1588
|
|
|
|
|
|
|
eval { |
1589
|
|
|
|
|
|
|
__PACKAGE__->error("exception error 2", "excpcode2"); |
1590
|
|
|
|
|
|
|
}; |
1591
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
$test->ok($@ =~ /^excpcode2 /, "Caught object exception code2"); |
1593
|
|
|
|
|
|
|
$test->is($o->last_exception, "exception error 2", "Caught object exception"); |
1594
|
|
|
|
|
|
|
$test->is(__PACKAGE__->last_exception, "exception error 2", "Caught class exception"); |
1595
|
|
|
|
|
|
|
$test->is($notes, 7, "Posted a notification"); |
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
eval { |
1598
|
|
|
|
|
|
|
__PACKAGE__->error("exception error 3", "excpcode3", "silently"); |
1599
|
|
|
|
|
|
|
}; |
1600
|
|
|
|
|
|
|
$test->ok($@ =~ /^excpcode3/, "Caught object exception code3"); |
1601
|
|
|
|
|
|
|
$test->is($o->last_exception, "exception error 3", "Caught object exception"); |
1602
|
|
|
|
|
|
|
$test->is(__PACKAGE__->last_exception, "exception error 3", "Caught class exception"); |
1603
|
|
|
|
|
|
|
$test->is($notes, 7, "No notification"); |
1604
|
|
|
|
|
|
|
|
1605
|
|
|
|
|
|
|
$test->is($cfg->{"Basset::Object"}->{'exceptions'} = 0, 0,"shut off exceptions"); |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
$test->ok( |
1608
|
|
|
|
|
|
|
scalar |
1609
|
|
|
|
|
|
|
$center->removeObserver( |
1610
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
1611
|
|
|
|
|
|
|
'notification' => 'error', |
1612
|
|
|
|
|
|
|
), "Removed observer for error notifications" |
1613
|
|
|
|
|
|
|
); |
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::error::Subclass1; |
1616
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
1617
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
sub can { |
1619
|
|
|
|
|
|
|
my $self = shift; |
1620
|
|
|
|
|
|
|
my $method = shift; |
1621
|
|
|
|
|
|
|
return 0 if $method =~ /_..._error/; |
1622
|
|
|
|
|
|
|
return $self->SUPER::can($method); |
1623
|
|
|
|
|
|
|
}; |
1624
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
package __PACKAGE__; |
1626
|
|
|
|
|
|
|
{ |
1627
|
|
|
|
|
|
|
local $@ = undef; |
1628
|
|
|
|
|
|
|
|
1629
|
|
|
|
|
|
|
eval { |
1630
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::error::Subclass1->error("some error"); |
1631
|
|
|
|
|
|
|
}; |
1632
|
|
|
|
|
|
|
$test->like($@, qr/^System start up failure/, 'Could not start system when cannot error'); |
1633
|
|
|
|
|
|
|
} |
1634
|
|
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::error::Subclass2; |
1636
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
1637
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
sub can { |
1639
|
|
|
|
|
|
|
my $self = shift; |
1640
|
|
|
|
|
|
|
my $method = shift; |
1641
|
|
|
|
|
|
|
return 0 if $method =~ /_..._errcode/; |
1642
|
|
|
|
|
|
|
return $self->SUPER::can($method); |
1643
|
|
|
|
|
|
|
}; |
1644
|
|
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
package __PACKAGE__; |
1646
|
|
|
|
|
|
|
|
1647
|
|
|
|
|
|
|
{ |
1648
|
|
|
|
|
|
|
local $@ = undef; |
1649
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
eval { |
1651
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::error::Subclass2->error("some error"); |
1652
|
|
|
|
|
|
|
}; |
1653
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
$test->like($@, qr/^System start up failure/, 'Could not start system when cannot errcode'); |
1655
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
$test->is(scalar(Basset::Test::Testing::__PACKAGE__::error::Subclass2->error), undef, 'accessing error merely returns'); |
1657
|
|
|
|
|
|
|
|
1658
|
|
|
|
|
|
|
} |
1659
|
|
|
|
|
|
|
|
1660
|
|
|
|
|
|
|
=end btest(error) |
1661
|
|
|
|
|
|
|
|
1662
|
|
|
|
|
|
|
=cut |
1663
|
|
|
|
|
|
|
|
1664
|
|
|
|
|
|
|
sub error { |
1665
|
362
|
|
|
362
|
1
|
23423
|
my $self = shift; |
1666
|
|
|
|
|
|
|
|
1667
|
362
|
100
|
|
|
|
921
|
my $errormethod = ref $self ? "_obj_error" : "_pkg_error"; |
1668
|
362
|
100
|
|
|
|
843
|
my $codemethod = ref $self ? "_obj_errcode" : "_pkg_errcode"; |
1669
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
# just in case we have an error very early on, we have our escape pod here. If something bad has happened, |
1671
|
|
|
|
|
|
|
# then just die. We cannot continue. |
1672
|
362
|
100
|
100
|
|
|
4016
|
unless ($self->can($errormethod) && $self->can($codemethod)) { |
1673
|
3
|
100
|
|
|
|
88
|
if (@_) { |
1674
|
2
|
|
|
|
|
593
|
croak("System start up failure : @_"); |
1675
|
|
|
|
|
|
|
} else { |
1676
|
1
|
|
|
|
|
6
|
return; |
1677
|
|
|
|
|
|
|
} |
1678
|
|
|
|
|
|
|
} |
1679
|
|
|
|
|
|
|
|
1680
|
359
|
100
|
|
|
|
1244
|
if (@_){ |
1681
|
|
|
|
|
|
|
|
1682
|
152
|
|
|
|
|
541
|
$self->$errormethod(shift); |
1683
|
152
|
100
|
|
|
|
630
|
$self->$codemethod(@_ ? shift : undef); |
1684
|
|
|
|
|
|
|
|
1685
|
152
|
100
|
|
|
|
413
|
if (defined $self->$errormethod()) { |
1686
|
|
|
|
|
|
|
|
1687
|
146
|
|
|
|
|
646
|
my $center = $self->pkg_for_type('notificationcenter', 'errorless'); |
1688
|
|
|
|
|
|
|
|
1689
|
146
|
|
100
|
|
|
705
|
my $silently = shift || 0; |
1690
|
146
|
|
50
|
|
|
581
|
my $throw_anyway = shift || 0; |
1691
|
146
|
100
|
|
|
|
367
|
unless ($silently) { |
1692
|
136
|
100
|
66
|
|
|
1527
|
if (defined $center && $center->can('postNotification')) { |
1693
|
135
|
|
|
|
|
579
|
$center->postNotification( |
1694
|
|
|
|
|
|
|
'notification' => 'error', |
1695
|
|
|
|
|
|
|
'object' => $self, |
1696
|
|
|
|
|
|
|
'args' => [$self->errvals], |
1697
|
|
|
|
|
|
|
); |
1698
|
|
|
|
|
|
|
} |
1699
|
|
|
|
|
|
|
} |
1700
|
|
|
|
|
|
|
|
1701
|
146
|
50
|
33
|
|
|
1203
|
if ($self->can('exceptions') || $throw_anyway) { |
1702
|
146
|
100
|
66
|
|
|
574
|
if ($self->exceptions && defined $self->$codemethod()) { |
1703
|
5
|
|
|
|
|
37
|
$self->last_exception($self->$errormethod()); |
1704
|
5
|
|
|
|
|
15
|
croak($self->$codemethod()); |
1705
|
|
|
|
|
|
|
}; |
1706
|
|
|
|
|
|
|
#something went horribly wrong very early on. Die with something useful. |
1707
|
|
|
|
|
|
|
} else { |
1708
|
0
|
|
|
|
|
0
|
die $self->errstring; |
1709
|
|
|
|
|
|
|
}; |
1710
|
|
|
|
|
|
|
} |
1711
|
|
|
|
|
|
|
|
1712
|
147
|
|
|
|
|
901
|
return; |
1713
|
|
|
|
|
|
|
} |
1714
|
|
|
|
|
|
|
else { |
1715
|
207
|
|
|
|
|
543
|
my $err = $self->$errormethod(); |
1716
|
207
|
100
|
100
|
|
|
983
|
if (defined $err && ref $err eq 'ARRAY') { |
1717
|
16
|
|
|
|
|
31
|
my $format = $err->[0]; |
1718
|
16
|
100
|
|
|
|
32
|
if (@$err > 1) { |
1719
|
14
|
|
|
|
|
30
|
$err = sprintf($format, @{$err}[1..$#$err]); |
|
14
|
|
|
|
|
74
|
|
1720
|
|
|
|
|
|
|
} else { |
1721
|
2
|
|
|
|
|
8
|
$err = $format; |
1722
|
|
|
|
|
|
|
}; |
1723
|
|
|
|
|
|
|
} |
1724
|
207
|
|
|
|
|
932
|
return $err; |
1725
|
|
|
|
|
|
|
#return $self->$errormethod(); |
1726
|
|
|
|
|
|
|
}; |
1727
|
|
|
|
|
|
|
}; |
1728
|
|
|
|
|
|
|
|
1729
|
|
|
|
|
|
|
=pod |
1730
|
|
|
|
|
|
|
|
1731
|
|
|
|
|
|
|
=item rawerror |
1732
|
|
|
|
|
|
|
|
1733
|
|
|
|
|
|
|
If you're using a formatted error string, ->error will always return the formatted value to you. |
1734
|
|
|
|
|
|
|
->rawerror will return the formattable data. |
1735
|
|
|
|
|
|
|
|
1736
|
|
|
|
|
|
|
$obj->error('foo'); |
1737
|
|
|
|
|
|
|
print $obj->error(); #prints 'foo' |
1738
|
|
|
|
|
|
|
print $obj->rawerror(); #prints 'foo' |
1739
|
|
|
|
|
|
|
|
1740
|
|
|
|
|
|
|
$obj->error(['foo %d', 77]); |
1741
|
|
|
|
|
|
|
print $obj->error(); #prints 'foo 77' |
1742
|
|
|
|
|
|
|
print $obj->rawerror(); #prints ARRAY0x1341 (etc.) |
1743
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
=cut |
1745
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
=pod |
1747
|
|
|
|
|
|
|
|
1748
|
|
|
|
|
|
|
=begin btest(rawerror) |
1749
|
|
|
|
|
|
|
|
1750
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
1751
|
|
|
|
|
|
|
$test->ok($o, "Object created"); |
1752
|
|
|
|
|
|
|
|
1753
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("raw class error", "roe"), undef, "Set class error"); |
1754
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->rawerror(), "raw class error", "Class raw error accesses"); |
1755
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(["raw class error %d"], "roe"), undef, "Set formatted class error"); |
1756
|
|
|
|
|
|
|
$test->is(ref __PACKAGE__->rawerror(), 'ARRAY', "Class formatted raw error accesses"); |
1757
|
|
|
|
|
|
|
$test->is(__PACKAGE__->rawerror()->[0], "raw class error %d", "Class formatted raw error accesses"); |
1758
|
|
|
|
|
|
|
|
1759
|
|
|
|
|
|
|
$test->is(scalar $o->error("raw object error", "roe"), undef, "Set object error"); |
1760
|
|
|
|
|
|
|
$test->is(scalar $o->rawerror(), "raw object error", "Object raw error accesses"); |
1761
|
|
|
|
|
|
|
$test->is(scalar $o->error(["raw object error %d"], "roe"), undef, "Set formatted object error"); |
1762
|
|
|
|
|
|
|
$test->is(ref $o->rawerror(), 'ARRAY', "Object formatted raw error accesses"); |
1763
|
|
|
|
|
|
|
$test->is($o->rawerror()->[0], 'raw object error %d', "Object formatted raw error accesses"); |
1764
|
|
|
|
|
|
|
$test->ok(ref $o->rawerror() eq 'ARRAY', "Class formatted raw error unaffected"); |
1765
|
|
|
|
|
|
|
$test->is(__PACKAGE__->rawerror()->[0], "raw class error %d", "Class formatted raw error unaffected"); |
1766
|
|
|
|
|
|
|
|
1767
|
|
|
|
|
|
|
=end btest(rawerror) |
1768
|
|
|
|
|
|
|
|
1769
|
|
|
|
|
|
|
=cut |
1770
|
|
|
|
|
|
|
|
1771
|
|
|
|
|
|
|
sub rawerror { |
1772
|
15
|
|
|
15
|
1
|
28
|
my $self = shift; |
1773
|
15
|
100
|
|
|
|
44
|
my $errormethod = ref $self ? "_obj_error" : "_pkg_error"; |
1774
|
|
|
|
|
|
|
|
1775
|
15
|
|
|
|
|
46
|
return $self->$errormethod(); |
1776
|
|
|
|
|
|
|
} |
1777
|
|
|
|
|
|
|
|
1778
|
|
|
|
|
|
|
=pod |
1779
|
|
|
|
|
|
|
|
1780
|
|
|
|
|
|
|
=item errcode |
1781
|
|
|
|
|
|
|
|
1782
|
|
|
|
|
|
|
errcode is an accessor ONLY. You can only mutate the errcode via error, see above. |
1783
|
|
|
|
|
|
|
|
1784
|
|
|
|
|
|
|
print $obj->errcode; |
1785
|
|
|
|
|
|
|
|
1786
|
|
|
|
|
|
|
Both objects and classes have errcode methods. |
1787
|
|
|
|
|
|
|
|
1788
|
|
|
|
|
|
|
my $obj = Some::Class->new() || die Some::Class->errcode(); |
1789
|
|
|
|
|
|
|
$obj->foo() || die $obj->errcode |
1790
|
|
|
|
|
|
|
|
1791
|
|
|
|
|
|
|
Do not ever ever B define an error code that starts with "B". Those are reserved for framework |
1792
|
|
|
|
|
|
|
error codes. Otherwise, standard C-style "namespace" conventions apply - give it a reasonably unique |
1793
|
|
|
|
|
|
|
prefix. Preferrably one that helps people identify where the error was. I like to use the the initials |
1794
|
|
|
|
|
|
|
of the module name. |
1795
|
|
|
|
|
|
|
|
1796
|
|
|
|
|
|
|
package Basset::Object::Persistent; #returns BOP-## error codes. |
1797
|
|
|
|
|
|
|
|
1798
|
|
|
|
|
|
|
=cut |
1799
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
=pod |
1801
|
|
|
|
|
|
|
|
1802
|
|
|
|
|
|
|
=begin btest(errcode) |
1803
|
|
|
|
|
|
|
|
1804
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("test error", "test code", "silently"), undef, "Class sets errcode"); |
1805
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->errcode(), "test code", "Class accesses"); |
1806
|
|
|
|
|
|
|
|
1807
|
|
|
|
|
|
|
=end btest(errcode) |
1808
|
|
|
|
|
|
|
|
1809
|
|
|
|
|
|
|
=cut |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
sub errcode { |
1812
|
254
|
|
|
254
|
1
|
2312
|
my $self = shift; |
1813
|
254
|
100
|
|
|
|
559
|
my $method = ref $self ? "_obj_errcode" : "_pkg_errcode"; |
1814
|
|
|
|
|
|
|
|
1815
|
254
|
|
|
|
|
799
|
return $self->$method(@_); |
1816
|
|
|
|
|
|
|
}; |
1817
|
|
|
|
|
|
|
|
1818
|
|
|
|
|
|
|
=pod |
1819
|
|
|
|
|
|
|
|
1820
|
|
|
|
|
|
|
=item errstring |
1821
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
errstring is a convenience accessor, it returns the error and code concatenated. |
1823
|
|
|
|
|
|
|
|
1824
|
|
|
|
|
|
|
$obj->someMethod() || die $obj->errstring; #dies "Values must be greater than 10...with code(ERR77)" |
1825
|
|
|
|
|
|
|
|
1826
|
|
|
|
|
|
|
=cut |
1827
|
|
|
|
|
|
|
|
1828
|
|
|
|
|
|
|
=pod |
1829
|
|
|
|
|
|
|
|
1830
|
|
|
|
|
|
|
=begin btest(errstring) |
1831
|
|
|
|
|
|
|
|
1832
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("test error", "test code"), undef, "Class sets error & errcode"); |
1833
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errstring(), "test error...with code (test code)", "Class accesses errstring"); |
1834
|
|
|
|
|
|
|
|
1835
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("test error2", "test code2", "silently"), undef, "Class silently sets error & errcode"); |
1836
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errstring(), "test error2...with code (test code2)", "Class accesses errstring"); |
1837
|
|
|
|
|
|
|
|
1838
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("test error3"), undef, "Class sets error & no errcode"); |
1839
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errstring(), "test error3...with code (code undefined)", "Class accesses errstring"); |
1840
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("test error4", undef, "silently"), undef, "Class silently sets error & no errcode"); |
1842
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errstring(), "test error4...with code (code undefined)", "Class accesses errstring"); |
1843
|
|
|
|
|
|
|
|
1844
|
|
|
|
|
|
|
__PACKAGE__->wipe_errors(); |
1845
|
|
|
|
|
|
|
|
1846
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->errstring), undef, 'errcode returns nothing w/o error and errcode'); |
1847
|
|
|
|
|
|
|
__PACKAGE__->errcode('test code'); |
1848
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errstring, 'error undefined...with code (test code)', 'errcode returns undefined w/o error'); |
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
=end btest(errstring) |
1851
|
|
|
|
|
|
|
|
1852
|
|
|
|
|
|
|
=cut |
1853
|
|
|
|
|
|
|
|
1854
|
|
|
|
|
|
|
sub errstring { |
1855
|
13
|
|
|
13
|
1
|
33
|
my $self = shift; |
1856
|
|
|
|
|
|
|
|
1857
|
13
|
100
|
|
|
|
49
|
if (defined $self->error) { |
|
|
100
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
return |
1859
|
10
|
100
|
|
|
|
26
|
$self->error |
1860
|
|
|
|
|
|
|
. "...with code (" . |
1861
|
|
|
|
|
|
|
(defined $self->errcode ? $self->errcode : 'code undefined') |
1862
|
|
|
|
|
|
|
. ")"; |
1863
|
|
|
|
|
|
|
} elsif (defined $self->errcode) { |
1864
|
1
|
|
|
|
|
4
|
return 'error undefined...with code (' . $self->errcode . ')'; |
1865
|
|
|
|
|
|
|
} else { |
1866
|
2
|
|
|
|
|
13
|
return; |
1867
|
|
|
|
|
|
|
}; |
1868
|
|
|
|
|
|
|
}; |
1869
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
=pod |
1871
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
=item errvals |
1873
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
similar to errstring, but returns the error and errcode in an array. This is great for bubbling |
1875
|
|
|
|
|
|
|
up error messages. Note that errvals will also include the extra 'silently' parameter to prevent |
1876
|
|
|
|
|
|
|
bubbled errors from posting notifications. |
1877
|
|
|
|
|
|
|
|
1878
|
|
|
|
|
|
|
$attribute = $obj->foo() or return $self->error($obj->errvals); |
1879
|
|
|
|
|
|
|
|
1880
|
|
|
|
|
|
|
=cut |
1881
|
|
|
|
|
|
|
|
1882
|
|
|
|
|
|
|
=pod |
1883
|
|
|
|
|
|
|
|
1884
|
|
|
|
|
|
|
=begin btest(errvals) |
1885
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
my $notes = 0; |
1887
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
sub notifier2 { |
1889
|
|
|
|
|
|
|
my $self = shift; |
1890
|
|
|
|
|
|
|
my $note = shift; |
1891
|
|
|
|
|
|
|
$notes++; |
1892
|
|
|
|
|
|
|
}; |
1893
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
my $center = __PACKAGE__->pkg_for_type('notificationcenter'); |
1895
|
|
|
|
|
|
|
$test->ok($center, "Got notification center class"); |
1896
|
|
|
|
|
|
|
|
1897
|
|
|
|
|
|
|
$test->ok( |
1898
|
|
|
|
|
|
|
scalar |
1899
|
|
|
|
|
|
|
$center->addObserver( |
1900
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
1901
|
|
|
|
|
|
|
'notification' => 'error', |
1902
|
|
|
|
|
|
|
'object' => 'all', |
1903
|
|
|
|
|
|
|
'method' => 'notifier2' |
1904
|
|
|
|
|
|
|
), "Added observer for error notifications" |
1905
|
|
|
|
|
|
|
); |
1906
|
|
|
|
|
|
|
|
1907
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
1908
|
|
|
|
|
|
|
$test->ok($o, "Object created"); |
1909
|
|
|
|
|
|
|
|
1910
|
|
|
|
|
|
|
$test->is(scalar $o->error("test error", "test code"), undef, "Object set error"); |
1911
|
|
|
|
|
|
|
$test->is($notes, 1, "Posted notification"); |
1912
|
|
|
|
|
|
|
|
1913
|
|
|
|
|
|
|
my @errvals = $o->errvals; |
1914
|
|
|
|
|
|
|
$test->is($notes, 1, "No notification"); |
1915
|
|
|
|
|
|
|
$test->is($errvals[0], "test error", "Object accesses error"); |
1916
|
|
|
|
|
|
|
$test->is($notes, 1, "No notification"); |
1917
|
|
|
|
|
|
|
$test->is($errvals[1], "test code", "Object accesses error"); |
1918
|
|
|
|
|
|
|
$test->is($notes, 1, "No notification"); |
1919
|
|
|
|
|
|
|
$test->is($errvals[2], "silently", "errvals always silent"); |
1920
|
|
|
|
|
|
|
$test->is($notes, 1, "No notification"); |
1921
|
|
|
|
|
|
|
|
1922
|
|
|
|
|
|
|
$test->ok( |
1923
|
|
|
|
|
|
|
scalar |
1924
|
|
|
|
|
|
|
$center->removeObserver( |
1925
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
1926
|
|
|
|
|
|
|
'notification' => 'error', |
1927
|
|
|
|
|
|
|
), "Removed observer for error notifications" |
1928
|
|
|
|
|
|
|
); |
1929
|
|
|
|
|
|
|
|
1930
|
|
|
|
|
|
|
=end btest(errvals) |
1931
|
|
|
|
|
|
|
|
1932
|
|
|
|
|
|
|
=cut |
1933
|
|
|
|
|
|
|
|
1934
|
|
|
|
|
|
|
sub errvals { |
1935
|
139
|
|
|
139
|
1
|
232
|
my $self = shift; |
1936
|
|
|
|
|
|
|
|
1937
|
139
|
|
|
|
|
539
|
return ($self->error, $self->errcode, 'silently'); |
1938
|
|
|
|
|
|
|
|
1939
|
|
|
|
|
|
|
}; |
1940
|
|
|
|
|
|
|
|
1941
|
|
|
|
|
|
|
=pod |
1942
|
|
|
|
|
|
|
|
1943
|
|
|
|
|
|
|
=item usererror |
1944
|
|
|
|
|
|
|
|
1945
|
|
|
|
|
|
|
errors are great, but they can be a bit cryptic. usererror takes the last error message |
1946
|
|
|
|
|
|
|
and re-formats it into a more end user friendly syntax. If there's no way to re-format it, it |
1947
|
|
|
|
|
|
|
just returns the actual error. |
1948
|
|
|
|
|
|
|
|
1949
|
|
|
|
|
|
|
Alternatively, you can also use the error translator to change an error code into something |
1950
|
|
|
|
|
|
|
more user friendly |
1951
|
|
|
|
|
|
|
|
1952
|
|
|
|
|
|
|
See "errortranslator", below, for more info. |
1953
|
|
|
|
|
|
|
|
1954
|
|
|
|
|
|
|
=cut |
1955
|
|
|
|
|
|
|
|
1956
|
|
|
|
|
|
|
=pod |
1957
|
|
|
|
|
|
|
|
1958
|
|
|
|
|
|
|
=begin btest(usererror) |
1959
|
|
|
|
|
|
|
|
1960
|
|
|
|
|
|
|
my $translator = __PACKAGE__->errortranslator(); |
1961
|
|
|
|
|
|
|
$test->ok( |
1962
|
|
|
|
|
|
|
scalar |
1963
|
|
|
|
|
|
|
__PACKAGE__->errortranslator( |
1964
|
|
|
|
|
|
|
{ |
1965
|
|
|
|
|
|
|
'test code' => "friendly test message", |
1966
|
|
|
|
|
|
|
'formatted test error %d' => "friendlier test message", |
1967
|
|
|
|
|
|
|
'formatted test error 7' => 'friendliest test message', |
1968
|
|
|
|
|
|
|
'extra error' => 'friendliest test message 2' |
1969
|
|
|
|
|
|
|
}), |
1970
|
|
|
|
|
|
|
'Class set error translator' |
1971
|
|
|
|
|
|
|
); |
1972
|
|
|
|
|
|
|
|
1973
|
|
|
|
|
|
|
my $uses_real = __PACKAGE__->use_real_errors(); |
1974
|
|
|
|
|
|
|
$test->is(__PACKAGE__->use_real_errors(0), 0, "Uses real errors"); |
1975
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("extra error", "test code"), undef, "Class sets error"); |
1977
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "friendliest test message 2", "Class gets user error for literal"); |
1978
|
|
|
|
|
|
|
|
1979
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(["formatted test error %d", 7], "test code"), undef, "Class sets formatted error"); |
1980
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "friendliest test message", "Class gets user error for formatted string"); |
1981
|
|
|
|
|
|
|
|
1982
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(["formatted test error %d", 9], "test code"), undef, "Class sets formatted error"); |
1983
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "friendlier test message", "Class gets user error for string format"); |
1984
|
|
|
|
|
|
|
|
1985
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("Some test error", "test code"), undef, "Class sets standard error"); |
1986
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "friendly test message", "Class gets user error for error code"); |
1987
|
|
|
|
|
|
|
|
1988
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("Some unknown error", "unknown code"), undef, "Class sets standard error w/o translation"); |
1989
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "Some unknown error", "Class gets no user error"); |
1990
|
|
|
|
|
|
|
|
1991
|
|
|
|
|
|
|
$test->ok( |
1992
|
|
|
|
|
|
|
scalar |
1993
|
|
|
|
|
|
|
__PACKAGE__->errortranslator( |
1994
|
|
|
|
|
|
|
{ |
1995
|
|
|
|
|
|
|
'test code' => "friendly test message", |
1996
|
|
|
|
|
|
|
'formatted test error %d' => "friendlier test message", |
1997
|
|
|
|
|
|
|
'formatted test error 7' => 'friendliest test message', |
1998
|
|
|
|
|
|
|
'extra error' => 'friendliest test message 2', |
1999
|
|
|
|
|
|
|
'*' => 'star error', |
2000
|
|
|
|
|
|
|
}), |
2001
|
|
|
|
|
|
|
'Class changed error translator' |
2002
|
|
|
|
|
|
|
); |
2003
|
|
|
|
|
|
|
|
2004
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("Some unknown error", "unknown code"), undef, "Class sets standard error w/o translation"); |
2005
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "star error", "Class gets star error"); |
2006
|
|
|
|
|
|
|
|
2007
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errortranslator($translator), $translator, 'Class reset error translator'); |
2008
|
|
|
|
|
|
|
$test->is(__PACKAGE__->use_real_errors($uses_real), $uses_real, "resets uses real errors"); |
2009
|
|
|
|
|
|
|
|
2010
|
|
|
|
|
|
|
=end btest(usererror) |
2011
|
|
|
|
|
|
|
|
2012
|
|
|
|
|
|
|
=cut |
2013
|
|
|
|
|
|
|
|
2014
|
|
|
|
|
|
|
sub usererror { |
2015
|
13
|
|
|
13
|
1
|
37
|
my $self = shift; |
2016
|
|
|
|
|
|
|
|
2017
|
13
|
100
|
|
|
|
83
|
return $self->errstring if $self->use_real_errors; |
2018
|
|
|
|
|
|
|
|
2019
|
7
|
|
|
|
|
9
|
my $usererror; |
2020
|
7
|
|
|
|
|
23
|
my $rawerror = $self->rawerror; |
2021
|
7
|
|
|
|
|
13
|
my $error; |
2022
|
|
|
|
|
|
|
|
2023
|
7
|
100
|
|
|
|
20
|
if (ref $rawerror) { |
2024
|
2
|
|
|
|
|
5
|
$error = $rawerror->[0]; |
2025
|
|
|
|
|
|
|
} else { |
2026
|
|
|
|
|
|
|
#the variable name doesn't make sense here, but hey, we'll recycle it. |
2027
|
5
|
|
|
|
|
9
|
$error = $rawerror; |
2028
|
|
|
|
|
|
|
} |
2029
|
|
|
|
|
|
|
|
2030
|
7
|
100
|
33
|
|
|
21
|
if (defined $self->errortranslator && defined $self->error && exists $self->errortranslator->{$self->error}) { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
2031
|
3
|
|
|
|
|
8
|
$usererror = $self->errortranslator->{$self->error}; |
2032
|
|
|
|
|
|
|
} |
2033
|
|
|
|
|
|
|
elsif (defined $self->errortranslator && defined $error && exists $self->errortranslator->{$error}) { |
2034
|
1
|
|
|
|
|
4
|
$usererror = $self->errortranslator->{$error}; |
2035
|
|
|
|
|
|
|
} |
2036
|
|
|
|
|
|
|
elsif (defined $self->errortranslator && defined $self->errcode && exists $self->errortranslator->{$self->errcode}) { |
2037
|
1
|
|
|
|
|
3
|
$usererror = $self->errortranslator->{$self->errcode}; |
2038
|
|
|
|
|
|
|
} |
2039
|
|
|
|
|
|
|
elsif (defined $self->errortranslator && exists $self->errortranslator->{'*'}) { |
2040
|
1
|
|
|
|
|
4
|
$usererror = $self->errortranslator->{'*'}; |
2041
|
|
|
|
|
|
|
} |
2042
|
|
|
|
|
|
|
else { |
2043
|
1
|
|
|
|
|
6
|
$usererror = $error; |
2044
|
|
|
|
|
|
|
} |
2045
|
|
|
|
|
|
|
|
2046
|
7
|
100
|
|
|
|
25
|
if (ref $rawerror) { |
2047
|
2
|
|
|
|
|
5
|
return sprintf($usererror, @{$rawerror}[1..$#$rawerror]); |
|
2
|
|
|
|
|
14
|
|
2048
|
|
|
|
|
|
|
} else { |
2049
|
5
|
|
|
|
|
25
|
return $usererror; |
2050
|
|
|
|
|
|
|
} |
2051
|
|
|
|
|
|
|
|
2052
|
|
|
|
|
|
|
}; |
2053
|
|
|
|
|
|
|
|
2054
|
|
|
|
|
|
|
=pod |
2055
|
|
|
|
|
|
|
|
2056
|
|
|
|
|
|
|
=item wipe_errors |
2057
|
|
|
|
|
|
|
|
2058
|
|
|
|
|
|
|
Wipes out the current error message and error code. |
2059
|
|
|
|
|
|
|
|
2060
|
|
|
|
|
|
|
=cut |
2061
|
|
|
|
|
|
|
|
2062
|
|
|
|
|
|
|
=pod |
2063
|
|
|
|
|
|
|
|
2064
|
|
|
|
|
|
|
=begin btest(wipe_errors) |
2065
|
|
|
|
|
|
|
|
2066
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("test error", "error code"), undef, "Class set error and errcode"); |
2067
|
|
|
|
|
|
|
$test->is(__PACKAGE__->error(), "test error", "Class accesses error"); |
2068
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode(), "error code", "Class accesses errcode"); |
2069
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->wipe_errors(), "Class wiped errors"); |
2070
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(), undef, "Class error wiped out"); |
2071
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->errcode(), undef, "Class errcode wiped out"); |
2072
|
|
|
|
|
|
|
|
2073
|
|
|
|
|
|
|
my $confClass = __PACKAGE__->pkg_for_type('conf'); |
2074
|
|
|
|
|
|
|
$test->ok($confClass, "Got conf"); |
2075
|
|
|
|
|
|
|
|
2076
|
|
|
|
|
|
|
my $cfg = $confClass->conf; |
2077
|
|
|
|
|
|
|
$test->ok($cfg, "Got configuration"); |
2078
|
|
|
|
|
|
|
|
2079
|
|
|
|
|
|
|
$test->ok($cfg->{"Basset::Object"}->{'exceptions'} = 1, "enables exceptions"); |
2080
|
|
|
|
|
|
|
|
2081
|
|
|
|
|
|
|
eval { |
2082
|
|
|
|
|
|
|
__PACKAGE__->error("test exception", "test exception code"); |
2083
|
|
|
|
|
|
|
}; |
2084
|
|
|
|
|
|
|
$test->ok($@, "Caught exception"); |
2085
|
|
|
|
|
|
|
$test->like($@, qr/test exception code/, "Exception matches"); |
2086
|
|
|
|
|
|
|
$test->like(__PACKAGE__->last_exception, qr/test exception/, "Exception is present"); |
2087
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->wipe_errors(), "Class wiped errors"); |
2088
|
|
|
|
|
|
|
$test->is(__PACKAGE__->last_exception, undef, "last exception wiped out"); |
2089
|
|
|
|
|
|
|
$test->is($cfg->{"Basset::Object"}->{'exceptions'} = 0, 0,"disables exceptions"); |
2090
|
|
|
|
|
|
|
|
2091
|
|
|
|
|
|
|
=end btest(wipe_errors) |
2092
|
|
|
|
|
|
|
|
2093
|
|
|
|
|
|
|
=cut |
2094
|
|
|
|
|
|
|
|
2095
|
|
|
|
|
|
|
sub wipe_errors { |
2096
|
5
|
|
|
5
|
1
|
14081
|
my $self = shift; |
2097
|
|
|
|
|
|
|
|
2098
|
5
|
|
|
|
|
22
|
$self->error(undef); |
2099
|
5
|
|
|
|
|
19
|
$self->errcode(undef); |
2100
|
5
|
50
|
|
|
|
42
|
$self->last_exception(undef) if $self->can('exceptions'); |
2101
|
|
|
|
|
|
|
|
2102
|
5
|
|
|
|
|
23
|
return 1; |
2103
|
|
|
|
|
|
|
}; |
2104
|
|
|
|
|
|
|
|
2105
|
|
|
|
|
|
|
=pod |
2106
|
|
|
|
|
|
|
|
2107
|
|
|
|
|
|
|
=item notify |
2108
|
|
|
|
|
|
|
|
2109
|
|
|
|
|
|
|
Used for non-fatal messages, usually an error message that shouldn't cause things to abort. Expects at least one argument, |
2110
|
|
|
|
|
|
|
the notification being posted. Additional arguments will be passed through to any handlers. |
2111
|
|
|
|
|
|
|
|
2112
|
|
|
|
|
|
|
sub lockThing { |
2113
|
|
|
|
|
|
|
my $self = shift; |
2114
|
|
|
|
|
|
|
my $thing = shift; |
2115
|
|
|
|
|
|
|
|
2116
|
|
|
|
|
|
|
if ($thing->locked) { |
2117
|
|
|
|
|
|
|
$self->notify("info", "Cannot lock - thing is already locked"); |
2118
|
|
|
|
|
|
|
} else { |
2119
|
|
|
|
|
|
|
$thing->lock(); |
2120
|
|
|
|
|
|
|
}; |
2121
|
|
|
|
|
|
|
|
2122
|
|
|
|
|
|
|
return 1; |
2123
|
|
|
|
|
|
|
} |
2124
|
|
|
|
|
|
|
|
2125
|
|
|
|
|
|
|
In this example, we have a method called "lockThing" that locks a thing (whatever that means). But it only locks the thing |
2126
|
|
|
|
|
|
|
if it is not already locked. If it is locked, it sends an informational message that the thing is already locked. But that's not |
2127
|
|
|
|
|
|
|
fatal - we still end up with a locked thing, so we're happy no matter what. No need to kick back an error. |
2128
|
|
|
|
|
|
|
|
2129
|
|
|
|
|
|
|
notify is a wrapper around the notification center. |
2130
|
|
|
|
|
|
|
|
2131
|
|
|
|
|
|
|
$obj->notify('foo') == Basset::NotificationCenter->postNotification('object' => $obj, 'notification' => 'foo'); |
2132
|
|
|
|
|
|
|
|
2133
|
|
|
|
|
|
|
=cut |
2134
|
|
|
|
|
|
|
|
2135
|
|
|
|
|
|
|
=pod |
2136
|
|
|
|
|
|
|
|
2137
|
|
|
|
|
|
|
=begin btest(notify) |
2138
|
|
|
|
|
|
|
|
2139
|
|
|
|
|
|
|
my $test1notes = undef; |
2140
|
|
|
|
|
|
|
my $test2notes = undef; |
2141
|
|
|
|
|
|
|
|
2142
|
|
|
|
|
|
|
sub test1notifier { |
2143
|
|
|
|
|
|
|
my $self = shift; |
2144
|
|
|
|
|
|
|
my $note = shift; |
2145
|
|
|
|
|
|
|
$test1notes = $note->{'args'}->[0]; |
2146
|
|
|
|
|
|
|
}; |
2147
|
|
|
|
|
|
|
|
2148
|
|
|
|
|
|
|
sub test2notifier { |
2149
|
|
|
|
|
|
|
my $self = shift; |
2150
|
|
|
|
|
|
|
my $note = shift; |
2151
|
|
|
|
|
|
|
$test2notes = $note->{'args'}->[0]; |
2152
|
|
|
|
|
|
|
}; |
2153
|
|
|
|
|
|
|
|
2154
|
|
|
|
|
|
|
my $center = __PACKAGE__->pkg_for_type('notificationcenter'); |
2155
|
|
|
|
|
|
|
$test->ok($center, "Got notification center class"); |
2156
|
|
|
|
|
|
|
|
2157
|
|
|
|
|
|
|
$test->ok( |
2158
|
|
|
|
|
|
|
scalar |
2159
|
|
|
|
|
|
|
$center->addObserver( |
2160
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
2161
|
|
|
|
|
|
|
'notification' => 'test1', |
2162
|
|
|
|
|
|
|
'object' => 'all', |
2163
|
|
|
|
|
|
|
'method' => 'test1notifier' |
2164
|
|
|
|
|
|
|
), "Added observer for test1 notifications" |
2165
|
|
|
|
|
|
|
); |
2166
|
|
|
|
|
|
|
|
2167
|
|
|
|
|
|
|
$test->ok( |
2168
|
|
|
|
|
|
|
scalar |
2169
|
|
|
|
|
|
|
$center->addObserver( |
2170
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
2171
|
|
|
|
|
|
|
'notification' => 'test2', |
2172
|
|
|
|
|
|
|
'object' => 'all', |
2173
|
|
|
|
|
|
|
'method' => 'test2notifier' |
2174
|
|
|
|
|
|
|
), "Added observer for test2 notifications" |
2175
|
|
|
|
|
|
|
); |
2176
|
|
|
|
|
|
|
|
2177
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
2178
|
|
|
|
|
|
|
$test->ok($o, "Object created"); |
2179
|
|
|
|
|
|
|
|
2180
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->notify('test1', "Test 1 note 1"), "Class posted notification"); |
2181
|
|
|
|
|
|
|
$test->is($test1notes, "Test 1 note 1", "Received note"); |
2182
|
|
|
|
|
|
|
$test->is($test2notes, undef, "No note for test 2"); |
2183
|
|
|
|
|
|
|
|
2184
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->notify('test2', "Test 2 note 2"), "Class posted notification"); |
2185
|
|
|
|
|
|
|
$test->is($test2notes, "Test 2 note 2", "Received note"); |
2186
|
|
|
|
|
|
|
$test->is($test1notes, "Test 1 note 1", "Test 1 note unchanged"); |
2187
|
|
|
|
|
|
|
|
2188
|
|
|
|
|
|
|
$test->ok( |
2189
|
|
|
|
|
|
|
scalar |
2190
|
|
|
|
|
|
|
$center->removeObserver( |
2191
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
2192
|
|
|
|
|
|
|
'notification' => 'test1', |
2193
|
|
|
|
|
|
|
), "Removed observer for test1 notifications" |
2194
|
|
|
|
|
|
|
); |
2195
|
|
|
|
|
|
|
|
2196
|
|
|
|
|
|
|
$test->ok( |
2197
|
|
|
|
|
|
|
scalar |
2198
|
|
|
|
|
|
|
$center->addObserver( |
2199
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
2200
|
|
|
|
|
|
|
'notification' => 'test1', |
2201
|
|
|
|
|
|
|
'object' => $o, |
2202
|
|
|
|
|
|
|
'method' => 'test1notifier' |
2203
|
|
|
|
|
|
|
), "Added specific observer for test1 notifications" |
2204
|
|
|
|
|
|
|
); |
2205
|
|
|
|
|
|
|
|
2206
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->notify('test1', 'Test 1 note 2'), "Class posted notification"); |
2207
|
|
|
|
|
|
|
$test->is($test1notes, "Test 1 note 1", "Test 1 note unchanged"); |
2208
|
|
|
|
|
|
|
$test->is($test2notes, "Test 2 note 2", "Test 2 note unchanged"); |
2209
|
|
|
|
|
|
|
|
2210
|
|
|
|
|
|
|
$test->ok(scalar $o->notify('test1', 'Test 1 note 3'), "Object posted notification"); |
2211
|
|
|
|
|
|
|
$test->is($test1notes, "Test 1 note 3", "Recieved note"); |
2212
|
|
|
|
|
|
|
|
2213
|
|
|
|
|
|
|
$test->is($test2notes, "Test 2 note 2", "Test 2 note unchanged"); |
2214
|
|
|
|
|
|
|
|
2215
|
|
|
|
|
|
|
$test->ok( |
2216
|
|
|
|
|
|
|
scalar |
2217
|
|
|
|
|
|
|
$center->removeObserver( |
2218
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
2219
|
|
|
|
|
|
|
'notification' => 'test1', |
2220
|
|
|
|
|
|
|
), "Removed observer for test1 notifications" |
2221
|
|
|
|
|
|
|
); |
2222
|
|
|
|
|
|
|
|
2223
|
|
|
|
|
|
|
$test->ok( |
2224
|
|
|
|
|
|
|
scalar |
2225
|
|
|
|
|
|
|
$center->removeObserver( |
2226
|
|
|
|
|
|
|
'observer' => '__PACKAGE__', |
2227
|
|
|
|
|
|
|
'notification' => 'test2', |
2228
|
|
|
|
|
|
|
), "Removed observer for test2 notifications" |
2229
|
|
|
|
|
|
|
); |
2230
|
|
|
|
|
|
|
|
2231
|
|
|
|
|
|
|
=end btest(notify) |
2232
|
|
|
|
|
|
|
|
2233
|
|
|
|
|
|
|
=cut |
2234
|
|
|
|
|
|
|
|
2235
|
|
|
|
|
|
|
sub notify { |
2236
|
4
|
|
|
4
|
1
|
493
|
my $self = shift; |
2237
|
4
|
|
|
|
|
8
|
my $notification = shift; |
2238
|
|
|
|
|
|
|
|
2239
|
4
|
|
|
|
|
14
|
my $center = $self->pkg_for_type('notificationcenter'); |
2240
|
|
|
|
|
|
|
|
2241
|
4
|
50
|
33
|
|
|
41
|
if (defined $center && $center->can('postNotification')) { |
2242
|
4
|
|
|
|
|
24
|
$center->postNotification( |
2243
|
|
|
|
|
|
|
'notification' => $notification, |
2244
|
|
|
|
|
|
|
'object' => $self, |
2245
|
|
|
|
|
|
|
'args' => [@_], |
2246
|
|
|
|
|
|
|
); |
2247
|
|
|
|
|
|
|
} |
2248
|
|
|
|
|
|
|
|
2249
|
4
|
|
|
|
|
24
|
return 1; |
2250
|
|
|
|
|
|
|
}; |
2251
|
|
|
|
|
|
|
|
2252
|
|
|
|
|
|
|
=pod |
2253
|
|
|
|
|
|
|
|
2254
|
|
|
|
|
|
|
=item add_restrictions |
2255
|
|
|
|
|
|
|
|
2256
|
|
|
|
|
|
|
Class method. Expects a hash of arrayrefs, listing permissions and method re-maps. |
2257
|
|
|
|
|
|
|
|
2258
|
|
|
|
|
|
|
Some::Package->add_restrictions( |
2259
|
|
|
|
|
|
|
'readonly' => [ |
2260
|
|
|
|
|
|
|
'commit' => 'failed_restricted_method', |
2261
|
|
|
|
|
|
|
'write' => 'failed_restricted_method', |
2262
|
|
|
|
|
|
|
], |
2263
|
|
|
|
|
|
|
'writeonly' => [ |
2264
|
|
|
|
|
|
|
'load' => 'failed_restricted_method', |
2265
|
|
|
|
|
|
|
], |
2266
|
|
|
|
|
|
|
'subuser' => [ |
2267
|
|
|
|
|
|
|
'commit' => 'validating_commit' |
2268
|
|
|
|
|
|
|
] |
2269
|
|
|
|
|
|
|
); |
2270
|
|
|
|
|
|
|
|
2271
|
|
|
|
|
|
|
We require a hash of arrayrefs so that we can guarantee the order in which the methods will be |
2272
|
|
|
|
|
|
|
re-mapped. |
2273
|
|
|
|
|
|
|
|
2274
|
|
|
|
|
|
|
This specifies that Some::Package can be restricted in several ways, with a 'readonly' restriction, |
2275
|
|
|
|
|
|
|
a 'writeonly' restriction, and a 'subuser' restriction. If the package is restricted, then the methods |
2276
|
|
|
|
|
|
|
are re-mapped as defined. i.e., if the 'readonly' restriction is in place, then calling 'commit' |
2277
|
|
|
|
|
|
|
actually calls "failed_restricted_method" Add restrictions by calling either add_restricted_method |
2278
|
|
|
|
|
|
|
or (better!) by calling restrict. |
2279
|
|
|
|
|
|
|
|
2280
|
|
|
|
|
|
|
my $inline_class = Some::Package->restrict('readonly'); |
2281
|
|
|
|
|
|
|
|
2282
|
|
|
|
|
|
|
my $o = Some::Package->new(); |
2283
|
|
|
|
|
|
|
$o->commit() || die $o->errstring; #succeeds! |
2284
|
|
|
|
|
|
|
|
2285
|
|
|
|
|
|
|
my $o2 = $inline_class->new(); |
2286
|
|
|
|
|
|
|
$o2->commit() || die $o2->errstring; #fails. access to commit is restricted. |
2287
|
|
|
|
|
|
|
|
2288
|
|
|
|
|
|
|
see add_restricted_method and restrict, below. |
2289
|
|
|
|
|
|
|
|
2290
|
|
|
|
|
|
|
=cut |
2291
|
|
|
|
|
|
|
|
2292
|
|
|
|
|
|
|
=pod |
2293
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
=begin btest(add_restrictions) |
2295
|
|
|
|
|
|
|
|
2296
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_restrictions::Subclass1; |
2297
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
2298
|
|
|
|
|
|
|
|
2299
|
|
|
|
|
|
|
my %restrictions = ( |
2300
|
|
|
|
|
|
|
'specialerror' => [ |
2301
|
|
|
|
|
|
|
'error' => 'error2', |
2302
|
|
|
|
|
|
|
'errcode' => 'errcode2' |
2303
|
|
|
|
|
|
|
], |
2304
|
|
|
|
|
|
|
'invalidrestriction' => [ |
2305
|
|
|
|
|
|
|
'junkymethod' => 'otherjunkymethod' |
2306
|
|
|
|
|
|
|
] |
2307
|
|
|
|
|
|
|
); |
2308
|
|
|
|
|
|
|
|
2309
|
|
|
|
|
|
|
$test->ok(scalar Basset::Test::Testing::__PACKAGE__::add_restrictions::Subclass1->add_restrictions(%restrictions), "Added restrictions to subclass"); |
2310
|
|
|
|
|
|
|
|
2311
|
|
|
|
|
|
|
=end btest(add_restrictions) |
2312
|
|
|
|
|
|
|
|
2313
|
|
|
|
|
|
|
=cut |
2314
|
|
|
|
|
|
|
|
2315
|
|
|
|
|
|
|
sub add_restrictions { |
2316
|
5
|
|
|
5
|
1
|
927
|
my $self = shift; |
2317
|
5
|
50
|
|
|
|
51
|
my %newrestrictions = @_ or return $self->error("Cannot add restriction w/o restrictions", "BO-17"); |
2318
|
|
|
|
|
|
|
|
2319
|
5
|
|
|
|
|
44
|
my $restrictions = $self->restrictions(); |
2320
|
|
|
|
|
|
|
|
2321
|
|
|
|
|
|
|
# @$restrictions{keys %newrestrictions} = values %newrestrictions; |
2322
|
|
|
|
|
|
|
|
2323
|
|
|
|
|
|
|
#this is a nuisance. We're here, so we know that we're adding restrictions. |
2324
|
|
|
|
|
|
|
#if there's already a restrictions hash, we need to duplicate it here. See the |
2325
|
|
|
|
|
|
|
#docs for add_trickle_class_attr above for more info on dealing with trickled class attributes |
2326
|
|
|
|
|
|
|
#that contain references |
2327
|
5
|
50
|
|
|
|
16
|
if ($restrictions) { |
2328
|
0
|
|
|
|
|
0
|
my $val = $self->dump($restrictions); |
2329
|
0
|
|
|
|
|
0
|
$val =~ /^(\$\w+)/; |
2330
|
0
|
|
|
|
|
0
|
local $@ = undef; |
2331
|
0
|
|
|
|
|
0
|
$restrictions = eval qq{ |
2332
|
|
|
|
|
|
|
my $1; |
2333
|
|
|
|
|
|
|
eval \$val; |
2334
|
|
|
|
|
|
|
}; |
2335
|
|
|
|
|
|
|
} |
2336
|
|
|
|
|
|
|
#otherwise, we create a new hash |
2337
|
|
|
|
|
|
|
else { |
2338
|
5
|
|
|
|
|
10
|
$restrictions = {}; |
2339
|
|
|
|
|
|
|
}; |
2340
|
|
|
|
|
|
|
|
2341
|
5
|
|
|
|
|
25
|
@$restrictions{keys %newrestrictions} = values %newrestrictions; |
2342
|
|
|
|
|
|
|
|
2343
|
|
|
|
|
|
|
#finally, we can properly set the new hash because we're guaranteed that it's always a copy |
2344
|
|
|
|
|
|
|
#that we want to operate on. |
2345
|
5
|
|
|
|
|
21
|
$self->restrictions($restrictions); |
2346
|
|
|
|
|
|
|
|
2347
|
5
|
|
|
|
|
34
|
return 1; |
2348
|
|
|
|
|
|
|
} |
2349
|
|
|
|
|
|
|
|
2350
|
|
|
|
|
|
|
=pod |
2351
|
|
|
|
|
|
|
|
2352
|
|
|
|
|
|
|
=item add_restricted_method |
2353
|
|
|
|
|
|
|
|
2354
|
|
|
|
|
|
|
Given a restriction and a method, restricts only that method to that restriction. |
2355
|
|
|
|
|
|
|
|
2356
|
|
|
|
|
|
|
Some::Package->add_restricted_method('writeonly', 'commit'); |
2357
|
|
|
|
|
|
|
|
2358
|
|
|
|
|
|
|
This applies the writeonly restriction to the commit method (as defined above in the add_restrictions |
2359
|
|
|
|
|
|
|
pod). Note that this does not apply the restriction to the 'write' method, only to 'commit'. |
2360
|
|
|
|
|
|
|
|
2361
|
|
|
|
|
|
|
You will rarely (if ever) use this method, use 'restrict' instead. |
2362
|
|
|
|
|
|
|
|
2363
|
|
|
|
|
|
|
=cut |
2364
|
|
|
|
|
|
|
|
2365
|
|
|
|
|
|
|
=begin btest(add_restricted_method) |
2366
|
|
|
|
|
|
|
|
2367
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::add_restricted_method::Subclass1; |
2368
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
2369
|
|
|
|
|
|
|
|
2370
|
|
|
|
|
|
|
my %restrictions = ( |
2371
|
|
|
|
|
|
|
'specialerror' => [ |
2372
|
|
|
|
|
|
|
'error' => 'error2', |
2373
|
|
|
|
|
|
|
'errcode' => 'errcode2' |
2374
|
|
|
|
|
|
|
], |
2375
|
|
|
|
|
|
|
'invalidrestriction' => [ |
2376
|
|
|
|
|
|
|
'junkymethod' => 'otherjunkymethod' |
2377
|
|
|
|
|
|
|
] |
2378
|
|
|
|
|
|
|
); |
2379
|
|
|
|
|
|
|
|
2380
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('e2'); |
2381
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('c2'); |
2382
|
|
|
|
|
|
|
|
2383
|
|
|
|
|
|
|
$test->is(__PACKAGE__->e2(0), 0, "set e2 to 0"); |
2384
|
|
|
|
|
|
|
$test->is(__PACKAGE__->c2(0), 0, "set c2 to 0"); |
2385
|
|
|
|
|
|
|
|
2386
|
|
|
|
|
|
|
sub error2 { |
2387
|
|
|
|
|
|
|
my $self = shift; |
2388
|
|
|
|
|
|
|
$self->e2($self->e2 + 1); |
2389
|
|
|
|
|
|
|
return $self->SUPER::error(@_); |
2390
|
|
|
|
|
|
|
} |
2391
|
|
|
|
|
|
|
|
2392
|
|
|
|
|
|
|
sub errcode2 { |
2393
|
|
|
|
|
|
|
my $self = shift; |
2394
|
|
|
|
|
|
|
$self->c2($self->c2 + 1); |
2395
|
|
|
|
|
|
|
return $self->SUPER::errcode(@_); |
2396
|
|
|
|
|
|
|
} |
2397
|
|
|
|
|
|
|
|
2398
|
|
|
|
|
|
|
$test->ok(scalar Basset::Test::Testing::__PACKAGE__::add_restricted_method::Subclass1->add_restrictions(%restrictions), "Added restrictions to subclass"); |
2399
|
|
|
|
|
|
|
|
2400
|
|
|
|
|
|
|
package __PACKAGE__; |
2401
|
|
|
|
|
|
|
|
2402
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::add_restricted_method::Subclass1->isa('__PACKAGE__'), 'Proper subclass'); |
2403
|
|
|
|
|
|
|
|
2404
|
|
|
|
|
|
|
my $subclass = Basset::Test::Testing::__PACKAGE__::add_restricted_method::Subclass1->inline_class(); |
2405
|
|
|
|
|
|
|
$test->ok(scalar $subclass, "Got restricted class"); |
2406
|
|
|
|
|
|
|
$test->ok($subclass->restricted, "Subclass is restricted"); |
2407
|
|
|
|
|
|
|
$test->ok(scalar $subclass->isa('Basset::Test::Testing::__PACKAGE__::add_restricted_method::Subclass1'), "Is subclass"); |
2408
|
|
|
|
|
|
|
$test->ok(scalar $subclass->isa('__PACKAGE__'), "Is subclass"); |
2409
|
|
|
|
|
|
|
|
2410
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_restricted_method('specialerror', 'error'), "Restricted error"); |
2411
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_restricted_method('specialerror', 'errcode'), "Restricted errcode"); |
2412
|
|
|
|
|
|
|
$test->ok(! scalar $subclass->add_restricted_method('invalidrestriction', 'junkymethod'), "Could not add invalid restriction"); |
2413
|
|
|
|
|
|
|
|
2414
|
|
|
|
|
|
|
$test->ok(! scalar $subclass->add_restricted_method('specialerror', 'error2'), "Could not add invalid restricted method"); |
2415
|
|
|
|
|
|
|
$test->ok(! scalar $subclass->add_restricted_method('specialerror', 'errcode2'), "Could not add invalid restricted method"); |
2416
|
|
|
|
|
|
|
$test->ok(! scalar $subclass->add_restricted_method('specialerror', 'junkymethod2'), "Could not add invalid restricted method"); |
2417
|
|
|
|
|
|
|
|
2418
|
|
|
|
|
|
|
my $e2 = $subclass->e2; |
2419
|
|
|
|
|
|
|
my $c2 = $subclass->c2; |
2420
|
|
|
|
|
|
|
|
2421
|
|
|
|
|
|
|
#we post silently or else error and errcode would be called when it posts the error notification. |
2422
|
|
|
|
|
|
|
$test->is(scalar $subclass->error("test error", "test code", "silently"), undef, "Set error for subclass"); |
2423
|
|
|
|
|
|
|
|
2424
|
|
|
|
|
|
|
$test->is($subclass->e2, $e2 + 1, "Subclass restricted error incremented"); |
2425
|
|
|
|
|
|
|
$test->is($subclass->c2, $c2, "Subclass restricted errcode unchanged"); |
2426
|
|
|
|
|
|
|
$test->is($subclass->error(), "test error", "Subclass accesses error method"); |
2427
|
|
|
|
|
|
|
$test->is($subclass->e2, $e2 + 2, "Subclass restricted error incremented"); |
2428
|
|
|
|
|
|
|
$test->is($subclass->c2, $c2, "Subclass restricted errcode unchanged"); |
2429
|
|
|
|
|
|
|
$test->is($subclass->errcode(), "test code", "Subclass accesses errcode method"); |
2430
|
|
|
|
|
|
|
$test->is($subclass->e2, $e2 + 2, "Subclass restricted error unchanged"); |
2431
|
|
|
|
|
|
|
$test->is($subclass->c2, $c2 + 1, "Subclass restricted errcode incremented"); |
2432
|
|
|
|
|
|
|
|
2433
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::add_restricted_method::Subclass1->error("super test error", "super test code", "silently"), undef, "Superclass sets error"); |
2434
|
|
|
|
|
|
|
$test->is($subclass->e2, $e2 + 2, "Subclass restricted error unchanged"); |
2435
|
|
|
|
|
|
|
$test->is($subclass->c2, $c2 + 1, "Subclass restricted errcode unchanged"); |
2436
|
|
|
|
|
|
|
|
2437
|
|
|
|
|
|
|
=end btest(add_restricted_method) |
2438
|
|
|
|
|
|
|
|
2439
|
|
|
|
|
|
|
=cut |
2440
|
|
|
|
|
|
|
|
2441
|
|
|
|
|
|
|
sub add_restricted_method { |
2442
|
11
|
|
|
11
|
1
|
935
|
my $pkg = shift; |
2443
|
11
|
|
|
|
|
18
|
my $restriction = shift; |
2444
|
11
|
|
|
|
|
15
|
my $method = shift; |
2445
|
|
|
|
|
|
|
|
2446
|
11
|
|
|
|
|
44
|
my $restrictions = $pkg->restrictions; |
2447
|
|
|
|
|
|
|
|
2448
|
11
|
|
|
|
|
59
|
my $restriction_set = $restrictions->{$restriction}; |
2449
|
|
|
|
|
|
|
|
2450
|
11
|
|
|
|
|
24
|
my $restricted_method = undef; |
2451
|
|
|
|
|
|
|
|
2452
|
11
|
50
|
|
|
|
22
|
if (defined $restriction_set) { |
2453
|
11
|
|
|
|
|
45
|
my $map = {@$restriction_set}; |
2454
|
|
|
|
|
|
|
|
2455
|
11
|
100
|
|
|
|
50
|
$restricted_method = $map->{$method} |
2456
|
|
|
|
|
|
|
or return $pkg->error("No method for restriction ($restriction) on method ($method)", "BO-14"); |
2457
|
|
|
|
|
|
|
|
2458
|
|
|
|
|
|
|
} else { |
2459
|
0
|
|
|
|
|
0
|
return $pkg->error("Cannot add restricted method ($method) w/o restriction set ($restriction)", "BO-19"); |
2460
|
|
|
|
|
|
|
}; |
2461
|
|
|
|
|
|
|
|
2462
|
|
|
|
|
|
|
# my $restricted_method = $restrictions->{$restriction}->{$method} |
2463
|
|
|
|
|
|
|
# or return $pkg->error("No method for restriction ($restriction) on method ($method)", "BO-14"); |
2464
|
|
|
|
|
|
|
|
2465
|
8
|
|
|
8
|
|
61
|
no strict 'refs'; |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
2440
|
|
2466
|
|
|
|
|
|
|
|
2467
|
8
|
50
|
|
|
|
24
|
if (ref $restricted_method eq 'CODE') { |
2468
|
0
|
|
|
|
|
0
|
*{$pkg . "::$method"} = $restricted_method; |
|
0
|
|
|
|
|
0
|
|
2469
|
0
|
|
|
|
|
0
|
return $method; |
2470
|
|
|
|
|
|
|
}; |
2471
|
|
|
|
|
|
|
|
2472
|
8
|
|
|
|
|
59
|
my $parents = $pkg->isa_path; |
2473
|
|
|
|
|
|
|
|
2474
|
|
|
|
|
|
|
#remember the isa path is most distant -> closest. Here we want to look at the closest |
2475
|
|
|
|
|
|
|
#ancestor that is not restricted. |
2476
|
|
|
|
|
|
|
# |
2477
|
|
|
|
|
|
|
#We march up the tree. Once we find a parent (or ourselves) that can perform the method |
2478
|
|
|
|
|
|
|
#we're looking for, we stop and are happy. |
2479
|
8
|
|
|
|
|
24
|
foreach my $parent (reverse @$parents) { |
2480
|
|
|
|
|
|
|
|
2481
|
20
|
|
|
|
|
23
|
my $code = *{$parent . '::' . $restricted_method}{'CODE'}; |
|
20
|
|
|
|
|
118
|
|
2482
|
20
|
100
|
|
|
|
54
|
if (defined $code ) { |
2483
|
6
|
|
|
|
|
10
|
*{$pkg . "::$method"} = $code; |
|
6
|
|
|
|
|
43
|
|
2484
|
6
|
|
|
|
|
46
|
return $method; |
2485
|
0
|
|
|
|
|
0
|
last; |
2486
|
|
|
|
|
|
|
}; |
2487
|
|
|
|
|
|
|
}; |
2488
|
|
|
|
|
|
|
|
2489
|
2
|
|
|
|
|
14
|
return $pkg->error("could not restrict method - no super class defines $restricted_method", "BO-15"); |
2490
|
|
|
|
|
|
|
}; |
2491
|
|
|
|
|
|
|
|
2492
|
|
|
|
|
|
|
=pod |
2493
|
|
|
|
|
|
|
|
2494
|
|
|
|
|
|
|
=item failed_restricted_method |
2495
|
|
|
|
|
|
|
|
2496
|
|
|
|
|
|
|
Simple convenience method. Always fails with a known error and errorcode - "Access to this method is |
2497
|
|
|
|
|
|
|
restricted", "BO-16" |
2498
|
|
|
|
|
|
|
|
2499
|
|
|
|
|
|
|
=cut |
2500
|
|
|
|
|
|
|
|
2501
|
|
|
|
|
|
|
=pod |
2502
|
|
|
|
|
|
|
|
2503
|
|
|
|
|
|
|
=begin btest(failed_restricted_method) |
2504
|
|
|
|
|
|
|
|
2505
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::failed_restricted_method::Subclass2; |
2506
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
2507
|
|
|
|
|
|
|
|
2508
|
|
|
|
|
|
|
sub successful { |
2509
|
|
|
|
|
|
|
return 1; |
2510
|
|
|
|
|
|
|
}; |
2511
|
|
|
|
|
|
|
|
2512
|
|
|
|
|
|
|
my %restrictions = ( |
2513
|
|
|
|
|
|
|
'failure' => [ |
2514
|
|
|
|
|
|
|
'successful' => 'failed_restricted_method', |
2515
|
|
|
|
|
|
|
], |
2516
|
|
|
|
|
|
|
); |
2517
|
|
|
|
|
|
|
|
2518
|
|
|
|
|
|
|
package __PACKAGE__; |
2519
|
|
|
|
|
|
|
|
2520
|
|
|
|
|
|
|
my $subclass = Basset::Test::Testing::__PACKAGE__::failed_restricted_method::Subclass2->inline_class; |
2521
|
|
|
|
|
|
|
$test->ok($subclass, "Got restricted subclass"); |
2522
|
|
|
|
|
|
|
$test->ok(scalar $subclass->restricted, "Subclass is restricted"); |
2523
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_restrictions(%restrictions), "Subclass added restrictions"); |
2524
|
|
|
|
|
|
|
|
2525
|
|
|
|
|
|
|
$test->ok(! scalar __PACKAGE__->failed_restricted_method, "Failed restricted method always fails"); |
2526
|
|
|
|
|
|
|
$test->ok(! scalar Basset::Test::Testing::__PACKAGE__::failed_restricted_method::Subclass2->failed_restricted_method, "Failed restricted method always fails"); |
2527
|
|
|
|
|
|
|
$test->ok(! scalar $subclass->failed_restricted_method, "Failed restricted method always fails"); |
2528
|
|
|
|
|
|
|
|
2529
|
|
|
|
|
|
|
$test->ok(scalar Basset::Test::Testing::__PACKAGE__::failed_restricted_method::Subclass2->successful, "Super Success is successful"); |
2530
|
|
|
|
|
|
|
$test->ok(scalar $subclass->successful, "Subclass success is successful"); |
2531
|
|
|
|
|
|
|
$test->ok(scalar $subclass->add_restricted_method('failure', 'successful'), "Restricted subclass to fail upon success"); |
2532
|
|
|
|
|
|
|
$test->ok(scalar Basset::Test::Testing::__PACKAGE__::failed_restricted_method::Subclass2->successful, "Super Success is successful"); |
2533
|
|
|
|
|
|
|
$test->ok(! scalar $subclass->successful, "Subclass success fails"); |
2534
|
|
|
|
|
|
|
|
2535
|
|
|
|
|
|
|
=end btest(failed_restricted_method) |
2536
|
|
|
|
|
|
|
|
2537
|
|
|
|
|
|
|
=cut |
2538
|
|
|
|
|
|
|
|
2539
|
|
|
|
|
|
|
sub failed_restricted_method { |
2540
|
7
|
|
|
7
|
1
|
61
|
return shift->error("Access to this method is restricted", "BO-16"); |
2541
|
|
|
|
|
|
|
}; |
2542
|
|
|
|
|
|
|
|
2543
|
|
|
|
|
|
|
=pod |
2544
|
|
|
|
|
|
|
|
2545
|
|
|
|
|
|
|
=item inline_class |
2546
|
|
|
|
|
|
|
|
2547
|
|
|
|
|
|
|
Another internal method that you will rarely, if ever call. |
2548
|
|
|
|
|
|
|
|
2549
|
|
|
|
|
|
|
my $inline_class = Some::Package->inline_class(); |
2550
|
|
|
|
|
|
|
|
2551
|
|
|
|
|
|
|
This creates a new class, which is a subclass of Some::Package. The only difference is that |
2552
|
|
|
|
|
|
|
it has its restricted flag turned on. To apply restrictions, use the restrict method instead. |
2553
|
|
|
|
|
|
|
|
2554
|
|
|
|
|
|
|
=cut |
2555
|
|
|
|
|
|
|
|
2556
|
|
|
|
|
|
|
=pod |
2557
|
|
|
|
|
|
|
|
2558
|
|
|
|
|
|
|
=begin btest(inline_class) |
2559
|
|
|
|
|
|
|
|
2560
|
|
|
|
|
|
|
my $class = __PACKAGE__->inline_class(); |
2561
|
|
|
|
|
|
|
$test->ok($class, "Got restricted class"); |
2562
|
|
|
|
|
|
|
$test->ok($class->restricted(), "Class is restricted"); |
2563
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->restricted(), "Superclass is not restricted"); |
2564
|
|
|
|
|
|
|
|
2565
|
|
|
|
|
|
|
=end btest(inline_class) |
2566
|
|
|
|
|
|
|
|
2567
|
|
|
|
|
|
|
=cut |
2568
|
|
|
|
|
|
|
|
2569
|
|
|
|
|
|
|
our $restrict_counter = 0; |
2570
|
|
|
|
|
|
|
our %inlined = (); |
2571
|
|
|
|
|
|
|
|
2572
|
|
|
|
|
|
|
sub inline_class { |
2573
|
12
|
|
|
12
|
1
|
1946
|
my $pkg = shift; |
2574
|
|
|
|
|
|
|
|
2575
|
8
|
|
|
8
|
|
47
|
no strict 'refs'; |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
2607
|
|
2576
|
12
|
|
|
|
|
47
|
my $class = $pkg . '::BASSETINLINE::R' . $restrict_counter++; |
2577
|
12
|
|
|
|
|
28
|
@{$class . "::ISA"} = ($pkg); |
|
12
|
|
|
|
|
358
|
|
2578
|
12
|
|
|
|
|
138
|
$class->restricted(1); |
2579
|
|
|
|
|
|
|
|
2580
|
12
|
|
|
|
|
39
|
$inlined{$class}++; |
2581
|
|
|
|
|
|
|
|
2582
|
12
|
|
|
|
|
39
|
return $class; |
2583
|
|
|
|
|
|
|
}; |
2584
|
|
|
|
|
|
|
|
2585
|
|
|
|
|
|
|
sub load_pkg { |
2586
|
190
|
|
|
190
|
0
|
515
|
my $class = shift; |
2587
|
|
|
|
|
|
|
|
2588
|
190
|
50
|
|
|
|
527
|
my $newclass = shift or return $class->error("Cannot load_pkg w/o class", "BO-28"); |
2589
|
190
|
|
100
|
|
|
563
|
my $errorless = shift || 0; |
2590
|
|
|
|
|
|
|
|
2591
|
190
|
|
|
|
|
388
|
local $@ = undef; |
2592
|
190
|
100
|
100
|
4
|
|
1101
|
eval "use $newclass" unless $inlined{$newclass} || $INC{$class->module_for_class($newclass)}; |
|
4
|
|
|
|
|
11955
|
|
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
80
|
|
2593
|
|
|
|
|
|
|
|
2594
|
190
|
100
|
|
|
|
571
|
if ($@) { |
2595
|
3
|
100
|
|
|
|
30
|
return $errorless ? undef : $class->error("Cannot load class ($newclass) : $@", "BO-29"); |
2596
|
|
|
|
|
|
|
} |
2597
|
|
|
|
|
|
|
|
2598
|
187
|
|
|
|
|
547
|
return $newclass; |
2599
|
|
|
|
|
|
|
} |
2600
|
|
|
|
|
|
|
|
2601
|
|
|
|
|
|
|
=pod |
2602
|
|
|
|
|
|
|
|
2603
|
|
|
|
|
|
|
=begin btest(load_pkg) |
2604
|
|
|
|
|
|
|
|
2605
|
|
|
|
|
|
|
my $iclass = __PACKAGE__->inline_class; |
2606
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->load_pkg($iclass), "Can load inline class"); |
2607
|
|
|
|
|
|
|
|
2608
|
|
|
|
|
|
|
=end btest(load_pkg) |
2609
|
|
|
|
|
|
|
|
2610
|
|
|
|
|
|
|
=cut |
2611
|
|
|
|
|
|
|
|
2612
|
|
|
|
|
|
|
=pod |
2613
|
|
|
|
|
|
|
|
2614
|
|
|
|
|
|
|
=item restrict |
2615
|
|
|
|
|
|
|
|
2616
|
|
|
|
|
|
|
Called on a class, this creates a new subclass with restrictions in place. |
2617
|
|
|
|
|
|
|
|
2618
|
|
|
|
|
|
|
my $inline_class = Some::Package->restrict('readonly', 'writeonly', 'subuser'); |
2619
|
|
|
|
|
|
|
|
2620
|
|
|
|
|
|
|
Will return a new class which is a subclass of Some::Package that has the readonly, writeonly, |
2621
|
|
|
|
|
|
|
and subuser restrictions applied. Note that restrictions are applied in order, so that a later |
2622
|
|
|
|
|
|
|
one may wipe out an earlier one. In this case, the re-defined commit method from subuser wins over |
2623
|
|
|
|
|
|
|
the one defined in writeonly. |
2624
|
|
|
|
|
|
|
|
2625
|
|
|
|
|
|
|
This is used to restrict access to class methods, probably depending upon some sort of user permission |
2626
|
|
|
|
|
|
|
scheme. |
2627
|
|
|
|
|
|
|
|
2628
|
|
|
|
|
|
|
=cut |
2629
|
|
|
|
|
|
|
|
2630
|
|
|
|
|
|
|
=pod |
2631
|
|
|
|
|
|
|
|
2632
|
|
|
|
|
|
|
=begin btest(restrict) |
2633
|
|
|
|
|
|
|
|
2634
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::restrict::Subclass1; |
2635
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
2636
|
|
|
|
|
|
|
|
2637
|
|
|
|
|
|
|
sub successful { |
2638
|
|
|
|
|
|
|
return 1; |
2639
|
|
|
|
|
|
|
}; |
2640
|
|
|
|
|
|
|
|
2641
|
|
|
|
|
|
|
my %restrictions = ( |
2642
|
|
|
|
|
|
|
'failure' => [ |
2643
|
|
|
|
|
|
|
'successful' => 'failed_restricted_method', |
2644
|
|
|
|
|
|
|
], |
2645
|
|
|
|
|
|
|
); |
2646
|
|
|
|
|
|
|
|
2647
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::restrict::Subclass1->add_restrictions(%restrictions), "Subclass added restrictions"); |
2648
|
|
|
|
|
|
|
|
2649
|
|
|
|
|
|
|
package __PACKAGE__; |
2650
|
|
|
|
|
|
|
|
2651
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->can('failed_restricted_method'), "__PACKAGE__ has failed_restricted_method"); |
2652
|
|
|
|
|
|
|
$test->ok(scalar Basset::Test::Testing::__PACKAGE__::restrict::Subclass1->can('failed_restricted_method'), "Subclass has failed_restricted_method"); |
2653
|
|
|
|
|
|
|
|
2654
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::restrict::Subclass1->isa('__PACKAGE__'), 'Proper subclass'); |
2655
|
|
|
|
|
|
|
$test->ok(! scalar __PACKAGE__->failed_restricted_method, "Method properly fails"); |
2656
|
|
|
|
|
|
|
$test->ok(! scalar Basset::Test::Testing::__PACKAGE__::restrict::Subclass1->failed_restricted_method, "Method properly fails"); |
2657
|
|
|
|
|
|
|
|
2658
|
|
|
|
|
|
|
my $subclass = Basset::Test::Testing::__PACKAGE__::restrict::Subclass1->restrict('failure'); |
2659
|
|
|
|
|
|
|
|
2660
|
|
|
|
|
|
|
$test->ok($subclass, "Got restricted subclass"); |
2661
|
|
|
|
|
|
|
|
2662
|
|
|
|
|
|
|
$test->ok($subclass->restricted, "Subclass is restricted"); |
2663
|
|
|
|
|
|
|
$test->ok(! Basset::Test::Testing::__PACKAGE__::restrict::Subclass1->restricted, "Superclass unaffected"); |
2664
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->restricted, "Superclass unaffected"); |
2665
|
|
|
|
|
|
|
|
2666
|
|
|
|
|
|
|
$test->ok(! scalar $subclass->successful, "Subclass restricted"); |
2667
|
|
|
|
|
|
|
$test->ok(scalar Basset::Test::Testing::__PACKAGE__::restrict::Subclass1->successful, "Superclass unaffected"); |
2668
|
|
|
|
|
|
|
|
2669
|
|
|
|
|
|
|
$test->ok(scalar Basset::Test::Testing::__PACKAGE__::restrict::Subclass1->restrict('worthless restriction'), "Added unknown restriction"); |
2670
|
|
|
|
|
|
|
|
2671
|
|
|
|
|
|
|
=end btest(restrict) |
2672
|
|
|
|
|
|
|
|
2673
|
|
|
|
|
|
|
=cut |
2674
|
|
|
|
|
|
|
|
2675
|
|
|
|
|
|
|
our $prior = {}; |
2676
|
|
|
|
|
|
|
|
2677
|
|
|
|
|
|
|
sub restrict { |
2678
|
3
|
|
|
3
|
1
|
11
|
my $pkg = shift; |
2679
|
|
|
|
|
|
|
|
2680
|
3
|
50
|
|
|
|
14
|
my @restrictions = @_ or return $pkg->error("Cannot restrict package w/o restrictions", "BO-13"); |
2681
|
|
|
|
|
|
|
|
2682
|
3
|
|
|
|
|
12
|
my $key = join(',', $pkg, @restrictions); |
2683
|
|
|
|
|
|
|
|
2684
|
3
|
50
|
|
|
|
13
|
return $prior->{$key} if defined $prior->{$key}; |
2685
|
|
|
|
|
|
|
|
2686
|
8
|
|
|
8
|
|
47
|
no strict 'refs'; |
|
8
|
|
|
|
|
19
|
|
|
8
|
|
|
|
|
10823
|
|
2687
|
|
|
|
|
|
|
|
2688
|
3
|
|
|
|
|
23
|
my $class = $pkg->inline_class(); |
2689
|
|
|
|
|
|
|
|
2690
|
3
|
|
|
|
|
164
|
my $pkgrestrictions = $pkg->restrictions(); |
2691
|
|
|
|
|
|
|
|
2692
|
3
|
|
|
|
|
10
|
my @applied = @{$pkg->applied_restrictions()}; |
|
3
|
|
|
|
|
30
|
|
2693
|
|
|
|
|
|
|
|
2694
|
3
|
|
|
|
|
10
|
foreach my $restriction (@restrictions) { |
2695
|
|
|
|
|
|
|
|
2696
|
|
|
|
|
|
|
#keep track of the restrictions we've applied |
2697
|
3
|
|
|
|
|
11
|
push @applied, $restriction; |
2698
|
|
|
|
|
|
|
|
2699
|
|
|
|
|
|
|
#grab our restriction map |
2700
|
3
|
100
|
|
|
|
7
|
my @map = @{$pkgrestrictions->{$restriction} || []}; |
|
3
|
|
|
|
|
21
|
|
2701
|
|
|
|
|
|
|
|
2702
|
|
|
|
|
|
|
#iterate through it. It's a hash masquerading as an arrayref, so the first |
2703
|
|
|
|
|
|
|
#element is our key, the second is the value (which we don't need right now) |
2704
|
3
|
|
|
|
|
14
|
while (@map) { |
2705
|
3
|
|
|
|
|
6
|
my $method = shift @map; |
2706
|
3
|
|
|
|
|
8
|
my $restricted_method = shift @map; |
2707
|
|
|
|
|
|
|
|
2708
|
3
|
50
|
|
|
|
35
|
$class->add_restricted_method($restriction, $method) |
2709
|
|
|
|
|
|
|
or return $pkg->error($class->errvals); |
2710
|
|
|
|
|
|
|
} |
2711
|
|
|
|
|
|
|
}; |
2712
|
|
|
|
|
|
|
|
2713
|
3
|
|
|
|
|
21
|
$prior->{$key} = $class; |
2714
|
|
|
|
|
|
|
|
2715
|
3
|
|
|
|
|
31
|
$class->applied_restrictions(\@applied); |
2716
|
|
|
|
|
|
|
|
2717
|
3
|
|
|
|
|
17
|
return $class; |
2718
|
|
|
|
|
|
|
} |
2719
|
|
|
|
|
|
|
|
2720
|
|
|
|
|
|
|
=pod |
2721
|
|
|
|
|
|
|
|
2722
|
|
|
|
|
|
|
=item nonrestricted_parent |
2723
|
|
|
|
|
|
|
|
2724
|
|
|
|
|
|
|
Called on a class, returns the first non-restricted parent of that class |
2725
|
|
|
|
|
|
|
|
2726
|
|
|
|
|
|
|
=cut |
2727
|
|
|
|
|
|
|
|
2728
|
|
|
|
|
|
|
=pod |
2729
|
|
|
|
|
|
|
|
2730
|
|
|
|
|
|
|
=begin btest(nonrestricted_parent) |
2731
|
|
|
|
|
|
|
|
2732
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::nonrestricted_parent::Subclass1; |
2733
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
2734
|
|
|
|
|
|
|
|
2735
|
|
|
|
|
|
|
package __PACKAGE__; |
2736
|
|
|
|
|
|
|
|
2737
|
|
|
|
|
|
|
$test->is(__PACKAGE__->nonrestricted_parent, "__PACKAGE__", "__PACKAGE__ own nonrestricted parent"); |
2738
|
|
|
|
|
|
|
$test->is(Basset::Test::Testing::__PACKAGE__::nonrestricted_parent::Subclass1->nonrestricted_parent, "Basset::Test::Testing::__PACKAGE__::nonrestricted_parent::Subclass1", "Subclass own nonrestricted parent"); |
2739
|
|
|
|
|
|
|
|
2740
|
|
|
|
|
|
|
my $subclass = Basset::Test::Testing::__PACKAGE__::nonrestricted_parent::Subclass1->inline_class; |
2741
|
|
|
|
|
|
|
$test->ok($subclass, "Got restricted class"); |
2742
|
|
|
|
|
|
|
$test->is($subclass->nonrestricted_parent, "Basset::Test::Testing::__PACKAGE__::nonrestricted_parent::Subclass1", "Restricted class has proper non restricted parent"); |
2743
|
|
|
|
|
|
|
|
2744
|
|
|
|
|
|
|
my $subclass2 = $subclass->inline_class; |
2745
|
|
|
|
|
|
|
$test->ok($subclass2, "Got restricted class of restricted class"); |
2746
|
|
|
|
|
|
|
$test->is($subclass2->nonrestricted_parent, "Basset::Test::Testing::__PACKAGE__::nonrestricted_parent::Subclass1", "Restricted class has proper non restricted parent"); |
2747
|
|
|
|
|
|
|
|
2748
|
|
|
|
|
|
|
my $subclass3 = __PACKAGE__->inline_class; |
2749
|
|
|
|
|
|
|
$test->ok($subclass3, "Got restricted class"); |
2750
|
|
|
|
|
|
|
$test->is($subclass3->nonrestricted_parent, "__PACKAGE__", "Restricted class has proper non restricted parent"); |
2751
|
|
|
|
|
|
|
|
2752
|
|
|
|
|
|
|
=end btest(nonrestricted_parent) |
2753
|
|
|
|
|
|
|
|
2754
|
|
|
|
|
|
|
=cut |
2755
|
|
|
|
|
|
|
|
2756
|
|
|
|
|
|
|
sub nonrestricted_parent { |
2757
|
5
|
|
|
5
|
1
|
1994
|
my $self = shift; |
2758
|
|
|
|
|
|
|
|
2759
|
5
|
|
|
|
|
34
|
my $parents = $self->isa_path; |
2760
|
|
|
|
|
|
|
|
2761
|
|
|
|
|
|
|
#remember the isa path is most distant -> closest. Here we want to look at the closest |
2762
|
|
|
|
|
|
|
#ancestor that is not restricted. |
2763
|
|
|
|
|
|
|
# |
2764
|
|
|
|
|
|
|
#We march up the tree. Once we find a parent (or ourselves) that can perform the method |
2765
|
|
|
|
|
|
|
#we're looking for, we stop and are happy. |
2766
|
5
|
|
|
|
|
11
|
foreach my $parent (reverse @$parents) { |
2767
|
9
|
100
|
|
|
|
33
|
return $parent unless $parent->restricted(); |
2768
|
|
|
|
|
|
|
}; |
2769
|
|
|
|
|
|
|
|
2770
|
0
|
|
|
|
|
0
|
return $self->error("class ($self) has no non-restricted parents", "BO-18"); |
2771
|
|
|
|
|
|
|
} |
2772
|
|
|
|
|
|
|
|
2773
|
|
|
|
|
|
|
=pod |
2774
|
|
|
|
|
|
|
|
2775
|
|
|
|
|
|
|
=item dump |
2776
|
|
|
|
|
|
|
|
2777
|
|
|
|
|
|
|
->dump dumps out the object (using Data::Dumper internally), this is useful to show you what an object looks like. |
2778
|
|
|
|
|
|
|
|
2779
|
|
|
|
|
|
|
print $obj->dump |
2780
|
|
|
|
|
|
|
|
2781
|
|
|
|
|
|
|
Alternatively, you can hand in something to dump. |
2782
|
|
|
|
|
|
|
|
2783
|
|
|
|
|
|
|
print $obj->dump($something_else); |
2784
|
|
|
|
|
|
|
|
2785
|
|
|
|
|
|
|
=cut |
2786
|
|
|
|
|
|
|
|
2787
|
|
|
|
|
|
|
=pod |
2788
|
|
|
|
|
|
|
|
2789
|
|
|
|
|
|
|
=begin btest(dump) |
2790
|
|
|
|
|
|
|
|
2791
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
2792
|
|
|
|
|
|
|
$test->ok($o, "Created object"); |
2793
|
|
|
|
|
|
|
my $o2 = __PACKAGE__->new(); |
2794
|
|
|
|
|
|
|
$test->ok($o2, "Created object"); |
2795
|
|
|
|
|
|
|
|
2796
|
|
|
|
|
|
|
$test->ok($o->dump, "Dumped object"); |
2797
|
|
|
|
|
|
|
$test->ok($o->dump(['a']), "Dumped array"); |
2798
|
|
|
|
|
|
|
$test->ok($o->dump({'k' => 'v'}), "Dumped hash"); |
2799
|
|
|
|
|
|
|
$test->ok($o2->dump, "Dumped other object"); |
2800
|
|
|
|
|
|
|
$test->is($o->dump($o2), $o2->dump, "Dumps equal"); |
2801
|
|
|
|
|
|
|
$test->is($o->dump, $o2->dump($o), "Dumps equal"); |
2802
|
|
|
|
|
|
|
|
2803
|
|
|
|
|
|
|
=end btest(dump) |
2804
|
|
|
|
|
|
|
|
2805
|
|
|
|
|
|
|
=cut |
2806
|
|
|
|
|
|
|
|
2807
|
|
|
|
|
|
|
sub dump { |
2808
|
17
|
|
|
17
|
1
|
3581
|
my $self = shift; |
2809
|
|
|
|
|
|
|
|
2810
|
17
|
100
|
|
|
|
87
|
return Data::Dumper::Dumper(@_ ? shift : $self); |
2811
|
|
|
|
|
|
|
}; |
2812
|
|
|
|
|
|
|
|
2813
|
|
|
|
|
|
|
=pod |
2814
|
|
|
|
|
|
|
|
2815
|
|
|
|
|
|
|
=item new |
2816
|
|
|
|
|
|
|
|
2817
|
|
|
|
|
|
|
Finally! The B. It's very easy, for a minimalist object, do this: |
2818
|
|
|
|
|
|
|
|
2819
|
|
|
|
|
|
|
my $obj = Class->new() || die Class->error(); |
2820
|
|
|
|
|
|
|
|
2821
|
|
|
|
|
|
|
Ta da! You have an object. Any attributes specified in the conf file will be loaded into your object. So if your |
2822
|
|
|
|
|
|
|
conf file defines 'foo' as 'bar', then $obj->foo will now equal 'bar'. |
2823
|
|
|
|
|
|
|
|
2824
|
|
|
|
|
|
|
If you'd like, you can also pass in method/value pairs to the constructor. |
2825
|
|
|
|
|
|
|
|
2826
|
|
|
|
|
|
|
my $obj = Class->new( |
2827
|
|
|
|
|
|
|
'attribute' => '17', |
2828
|
|
|
|
|
|
|
'foo' => 'baz', |
2829
|
|
|
|
|
|
|
'method' => '88' |
2830
|
|
|
|
|
|
|
) || die Class->error(); |
2831
|
|
|
|
|
|
|
|
2832
|
|
|
|
|
|
|
This is (roughly) the same as: |
2833
|
|
|
|
|
|
|
|
2834
|
|
|
|
|
|
|
my $obj = Class->new() || die Class->error(); |
2835
|
|
|
|
|
|
|
|
2836
|
|
|
|
|
|
|
$obj->attribute(17) || die $obj->error(); |
2837
|
|
|
|
|
|
|
$obj->foo('baz') || die $obj->error(); |
2838
|
|
|
|
|
|
|
$obj->method(88) || die $obj->error(); |
2839
|
|
|
|
|
|
|
|
2840
|
|
|
|
|
|
|
Any accessors or methods you'd like may be passed to the constructor. Any unknown pairs will be silently ignored. |
2841
|
|
|
|
|
|
|
If you pass a method/value pair to the constructor, it will override any equivalent method/value pair in the |
2842
|
|
|
|
|
|
|
conf file. |
2843
|
|
|
|
|
|
|
|
2844
|
|
|
|
|
|
|
Also note that any methods that return undef are assumed to be errors and will cause your construction to fail. But, if you explicitly pass |
2845
|
|
|
|
|
|
|
in an 'undef' parameter and your method/mutator fails, then we will assume you know what you're doing and it's allowed. You only fail |
2846
|
|
|
|
|
|
|
if you pass in a value other than undef, but the result of the method call is an undef. |
2847
|
|
|
|
|
|
|
|
2848
|
|
|
|
|
|
|
$obj = Class->new( |
2849
|
|
|
|
|
|
|
'attr' => undef |
2850
|
|
|
|
|
|
|
) || die Class->error; |
2851
|
|
|
|
|
|
|
|
2852
|
|
|
|
|
|
|
If you really really need to to explicitly set something to undef, you'll need to do it afterwards: |
2853
|
|
|
|
|
|
|
|
2854
|
|
|
|
|
|
|
$obj = Class->new(); |
2855
|
|
|
|
|
|
|
$obj->method(undef); |
2856
|
|
|
|
|
|
|
|
2857
|
|
|
|
|
|
|
Note that in this case, setting 'method' to undef isn't actually an error, since that's what you want to do. But, |
2858
|
|
|
|
|
|
|
the constructor has no way to know when an accessor returning undef is an error, or when you explicitly set the accessor |
2859
|
|
|
|
|
|
|
to undef. |
2860
|
|
|
|
|
|
|
|
2861
|
|
|
|
|
|
|
=cut |
2862
|
|
|
|
|
|
|
|
2863
|
|
|
|
|
|
|
=pod |
2864
|
|
|
|
|
|
|
|
2865
|
|
|
|
|
|
|
=begin btest(new) |
2866
|
|
|
|
|
|
|
|
2867
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
2868
|
|
|
|
|
|
|
|
2869
|
|
|
|
|
|
|
$test->ok($o, "created a new object"); |
2870
|
|
|
|
|
|
|
|
2871
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::new::Subclass1; |
2872
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
2873
|
|
|
|
|
|
|
|
2874
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::new::Subclass1->add_attr('attr1'); |
2875
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::new::Subclass1->add_attr('attr2'); |
2876
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::new::Subclass1->add_attr('attr3'); |
2877
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::new::Subclass1->add_class_attr('class_attr'); |
2878
|
|
|
|
|
|
|
|
2879
|
|
|
|
|
|
|
package __PACKAGE__; |
2880
|
|
|
|
|
|
|
|
2881
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::new::Subclass1->isa('__PACKAGE__'), "Subclass is subclass"); |
2882
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::new::Subclass1->can('attr1'), 'class can attr1'); |
2883
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::new::Subclass1->can('attr2'), 'class can attr2'); |
2884
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::new::Subclass1->can('attr3'), 'class can attr3'); |
2885
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::new::Subclass1->can('class_attr'), 'class can class_attr'); |
2886
|
|
|
|
|
|
|
|
2887
|
|
|
|
|
|
|
my $o2 = Basset::Test::Testing::__PACKAGE__::new::Subclass1->new(); |
2888
|
|
|
|
|
|
|
$test->ok($o2, "created a subclass object"); |
2889
|
|
|
|
|
|
|
|
2890
|
|
|
|
|
|
|
my $o3 = Basset::Test::Testing::__PACKAGE__::new::Subclass1->new( |
2891
|
|
|
|
|
|
|
'attr1' => 'attr1val', |
2892
|
|
|
|
|
|
|
); |
2893
|
|
|
|
|
|
|
|
2894
|
|
|
|
|
|
|
$test->ok($o3, "Created a subclass object"); |
2895
|
|
|
|
|
|
|
$test->is(scalar $o3->attr1, 'attr1val', 'subclass object has attribute from constructor'); |
2896
|
|
|
|
|
|
|
|
2897
|
|
|
|
|
|
|
my $o4 = Basset::Test::Testing::__PACKAGE__::new::Subclass1->new( |
2898
|
|
|
|
|
|
|
'attr1' => 'attr1val', |
2899
|
|
|
|
|
|
|
'attr2' => 'attr2val', |
2900
|
|
|
|
|
|
|
); |
2901
|
|
|
|
|
|
|
|
2902
|
|
|
|
|
|
|
$test->ok($o4, "Created a subclass object"); |
2903
|
|
|
|
|
|
|
$test->is(scalar $o4->attr1, 'attr1val', 'subclass object has attribute from constructor'); |
2904
|
|
|
|
|
|
|
$test->is(scalar $o4->attr2, 'attr2val', 'subclass object has attribute from constructor'); |
2905
|
|
|
|
|
|
|
|
2906
|
|
|
|
|
|
|
my $o5 = Basset::Test::Testing::__PACKAGE__::new::Subclass1->new( |
2907
|
|
|
|
|
|
|
'attr1' => 'attr1val', |
2908
|
|
|
|
|
|
|
'attr2' => 'attr2val', |
2909
|
|
|
|
|
|
|
'attr7' => 'attr7val', |
2910
|
|
|
|
|
|
|
'attr8' => 'attr8val', |
2911
|
|
|
|
|
|
|
); |
2912
|
|
|
|
|
|
|
|
2913
|
|
|
|
|
|
|
$test->ok($o5, "Created a subclass object w/junk values"); |
2914
|
|
|
|
|
|
|
$test->is(scalar $o5->attr1, 'attr1val', 'subclass object has attribute from constructor'); |
2915
|
|
|
|
|
|
|
$test->is(scalar $o5->attr2, 'attr2val', 'subclass object has attribute from constructor'); |
2916
|
|
|
|
|
|
|
|
2917
|
|
|
|
|
|
|
#these tests would now pass. |
2918
|
|
|
|
|
|
|
#my $o6 = Basset::Test::Testing::__PACKAGE__::new::Subclass1->new( |
2919
|
|
|
|
|
|
|
# 'attr1' => undef, |
2920
|
|
|
|
|
|
|
#); |
2921
|
|
|
|
|
|
|
# |
2922
|
|
|
|
|
|
|
#$test->ok(! $o6, "Failed to create object w/undef value"); |
2923
|
|
|
|
|
|
|
|
2924
|
|
|
|
|
|
|
my $o7 = Basset::Test::Testing::__PACKAGE__::new::Subclass1->new( |
2925
|
|
|
|
|
|
|
'attr1' => 7, |
2926
|
|
|
|
|
|
|
'attr2' => 0, |
2927
|
|
|
|
|
|
|
); |
2928
|
|
|
|
|
|
|
|
2929
|
|
|
|
|
|
|
$test->ok($o7, "Created object w/0 value"); |
2930
|
|
|
|
|
|
|
$test->is($o7->attr1, 7, 'attr1 value set'); |
2931
|
|
|
|
|
|
|
$test->is($o7->attr2, 0, 'attr2 value set'); |
2932
|
|
|
|
|
|
|
|
2933
|
|
|
|
|
|
|
my $o8 = Basset::Test::Testing::__PACKAGE__::new::Subclass1->new( |
2934
|
|
|
|
|
|
|
{ |
2935
|
|
|
|
|
|
|
'attr1' => 8, |
2936
|
|
|
|
|
|
|
'attr2' => 9 |
2937
|
|
|
|
|
|
|
}, |
2938
|
|
|
|
|
|
|
'attr1' => 7 |
2939
|
|
|
|
|
|
|
); |
2940
|
|
|
|
|
|
|
|
2941
|
|
|
|
|
|
|
$test->ok($o8, "Created object w/0 value"); |
2942
|
|
|
|
|
|
|
$test->is($o8->attr1, 7, 'attr1 value set'); |
2943
|
|
|
|
|
|
|
$test->is($o8->attr2, 9, 'attr2 value set'); |
2944
|
|
|
|
|
|
|
|
2945
|
|
|
|
|
|
|
=end btest(new) |
2946
|
|
|
|
|
|
|
|
2947
|
|
|
|
|
|
|
=cut |
2948
|
|
|
|
|
|
|
|
2949
|
|
|
|
|
|
|
sub new { |
2950
|
83
|
|
|
83
|
1
|
92108
|
my $class = shift->pkg; |
2951
|
83
|
|
|
|
|
746
|
my $self = bless {}, $class; |
2952
|
|
|
|
|
|
|
|
2953
|
83
|
|
66
|
|
|
545
|
return $self->init( |
2954
|
|
|
|
|
|
|
@_ |
2955
|
|
|
|
|
|
|
) || $class->error($self->errvals); |
2956
|
|
|
|
|
|
|
}; |
2957
|
|
|
|
|
|
|
|
2958
|
|
|
|
|
|
|
=pod |
2959
|
|
|
|
|
|
|
|
2960
|
|
|
|
|
|
|
=item init |
2961
|
|
|
|
|
|
|
|
2962
|
|
|
|
|
|
|
The object initializer. Arguably more important than the constructor, but not something you need to worry about. |
2963
|
|
|
|
|
|
|
The constructor calls it internally, and you really shouldn't touch it or override it. But I wanted it here so |
2964
|
|
|
|
|
|
|
you know what it does. |
2965
|
|
|
|
|
|
|
|
2966
|
|
|
|
|
|
|
Simply, it iterates through the conf file and mutates any of your object attributes to the value specified in the conf |
2967
|
|
|
|
|
|
|
file. It then iterates through the hash you passed to ->new() and does the same thing, overriding any conf values, if |
2968
|
|
|
|
|
|
|
necessary. |
2969
|
|
|
|
|
|
|
|
2970
|
|
|
|
|
|
|
init is smart enough to use all super class values defined in the conf file, in hierarchy order. So if your conf file |
2971
|
|
|
|
|
|
|
contains: |
2972
|
|
|
|
|
|
|
|
2973
|
|
|
|
|
|
|
define package SuperClass |
2974
|
|
|
|
|
|
|
|
2975
|
|
|
|
|
|
|
foo = 'bar' |
2976
|
|
|
|
|
|
|
|
2977
|
|
|
|
|
|
|
And you're creating a new SubClass object, then it will get the default of foo = 'bar' as in the conf file, despite |
2978
|
|
|
|
|
|
|
the fact that it was not defined for your own package. Naturally, the more significant definition is used. |
2979
|
|
|
|
|
|
|
|
2980
|
|
|
|
|
|
|
define package SuperClass |
2981
|
|
|
|
|
|
|
|
2982
|
|
|
|
|
|
|
foo = 'bar' |
2983
|
|
|
|
|
|
|
|
2984
|
|
|
|
|
|
|
define package SubClass |
2985
|
|
|
|
|
|
|
|
2986
|
|
|
|
|
|
|
foo = 'baz' |
2987
|
|
|
|
|
|
|
|
2988
|
|
|
|
|
|
|
SuperClass objects will default foo to 'bar', SubClass objects will default foo to 'baz' |
2989
|
|
|
|
|
|
|
|
2990
|
|
|
|
|
|
|
If the initializer is given a hashref as its first argument, then it will use those values first. Note that |
2991
|
|
|
|
|
|
|
values passed in via a hashref like this may be overridden by defaults AND by passed in arguments. |
2992
|
|
|
|
|
|
|
|
2993
|
|
|
|
|
|
|
For example: |
2994
|
|
|
|
|
|
|
|
2995
|
|
|
|
|
|
|
#in your conf file |
2996
|
|
|
|
|
|
|
define package Some::Class |
2997
|
|
|
|
|
|
|
foo = bar |
2998
|
|
|
|
|
|
|
one = two |
2999
|
|
|
|
|
|
|
alpha = beta |
3000
|
|
|
|
|
|
|
|
3001
|
|
|
|
|
|
|
#in your code |
3002
|
|
|
|
|
|
|
|
3003
|
|
|
|
|
|
|
my $x = Some::Class->new( |
3004
|
|
|
|
|
|
|
{ |
3005
|
|
|
|
|
|
|
'foo' => 'fnar', |
3006
|
|
|
|
|
|
|
'mister' => 'peepers', |
3007
|
|
|
|
|
|
|
'alpha' => 'kappa', |
3008
|
|
|
|
|
|
|
}, |
3009
|
|
|
|
|
|
|
'alpha' => 'gamma' |
3010
|
|
|
|
|
|
|
); |
3011
|
|
|
|
|
|
|
|
3012
|
|
|
|
|
|
|
print $x->foo; #prints 'bar' (from conf file) |
3013
|
|
|
|
|
|
|
print $x->one; #prints 'two' (from conf file) |
3014
|
|
|
|
|
|
|
print $x->mister; #prints 'peepers' (from initial hash) |
3015
|
|
|
|
|
|
|
print $x->alpha; #prints 'gamma' (passed argument) |
3016
|
|
|
|
|
|
|
|
3017
|
|
|
|
|
|
|
=cut |
3018
|
|
|
|
|
|
|
|
3019
|
|
|
|
|
|
|
=pod |
3020
|
|
|
|
|
|
|
|
3021
|
|
|
|
|
|
|
=begin btest(init) |
3022
|
|
|
|
|
|
|
|
3023
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::init::Subclass2; |
3024
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3025
|
|
|
|
|
|
|
|
3026
|
|
|
|
|
|
|
sub conf { |
3027
|
|
|
|
|
|
|
return undef; |
3028
|
|
|
|
|
|
|
}; |
3029
|
|
|
|
|
|
|
|
3030
|
|
|
|
|
|
|
package __PACKAGE__; |
3031
|
|
|
|
|
|
|
|
3032
|
|
|
|
|
|
|
{ |
3033
|
|
|
|
|
|
|
my $o = undef; |
3034
|
|
|
|
|
|
|
local $@ = undef; |
3035
|
|
|
|
|
|
|
$o = Basset::Test::Testing::__PACKAGE__::init::Subclass2->new(); |
3036
|
|
|
|
|
|
|
$test->is($o, undef, 'could not create object w/o conf file'); |
3037
|
|
|
|
|
|
|
} |
3038
|
|
|
|
|
|
|
|
3039
|
|
|
|
|
|
|
{ |
3040
|
|
|
|
|
|
|
my $o = __PACKAGE__->new('__j_known_junk_method' => 'a'); |
3041
|
|
|
|
|
|
|
$test->ok($o, 'created object'); |
3042
|
|
|
|
|
|
|
} |
3043
|
|
|
|
|
|
|
|
3044
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::init::Subclass3; |
3045
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3046
|
|
|
|
|
|
|
my $subclass = 'Basset::Test::Testing::__PACKAGE__::init::Subclass3'; |
3047
|
|
|
|
|
|
|
|
3048
|
|
|
|
|
|
|
sub known_failure { |
3049
|
|
|
|
|
|
|
my $self = shift; |
3050
|
|
|
|
|
|
|
return $self->error("I failed", "known_error_code"); |
3051
|
|
|
|
|
|
|
} |
3052
|
|
|
|
|
|
|
|
3053
|
|
|
|
|
|
|
sub known_failure_2 { |
3054
|
|
|
|
|
|
|
my $self = shift; |
3055
|
|
|
|
|
|
|
return; |
3056
|
|
|
|
|
|
|
} |
3057
|
|
|
|
|
|
|
|
3058
|
|
|
|
|
|
|
my $obj1 = $subclass->new(); |
3059
|
|
|
|
|
|
|
$test->ok($obj1, "Got empty object w/o known failure"); |
3060
|
|
|
|
|
|
|
|
3061
|
|
|
|
|
|
|
my $obj2 = $subclass->new( |
3062
|
|
|
|
|
|
|
'known_failure' => 1 |
3063
|
|
|
|
|
|
|
); |
3064
|
|
|
|
|
|
|
|
3065
|
|
|
|
|
|
|
$test->is($obj2, undef, "obj2 not created because of known_failure"); |
3066
|
|
|
|
|
|
|
$test->is($subclass->errcode, 'known_error_code', 'proper error code'); |
3067
|
|
|
|
|
|
|
|
3068
|
|
|
|
|
|
|
my $obj3 = $subclass->new( |
3069
|
|
|
|
|
|
|
'known_failure_2' => 1 |
3070
|
|
|
|
|
|
|
); |
3071
|
|
|
|
|
|
|
|
3072
|
|
|
|
|
|
|
$test->is($obj3, undef, "obj3 not created because of known_failure_2"); |
3073
|
|
|
|
|
|
|
$test->is($subclass->errcode, 'BO-03', 'proper error code'); |
3074
|
|
|
|
|
|
|
|
3075
|
|
|
|
|
|
|
=end btest(init) |
3076
|
|
|
|
|
|
|
|
3077
|
|
|
|
|
|
|
=cut |
3078
|
|
|
|
|
|
|
|
3079
|
|
|
|
|
|
|
sub init { |
3080
|
83
|
|
|
83
|
1
|
166
|
my $self = shift; |
3081
|
|
|
|
|
|
|
|
3082
|
83
|
100
|
|
|
|
473
|
my $conf = $self->conf or return; |
3083
|
|
|
|
|
|
|
|
3084
|
82
|
|
|
|
|
439
|
my $parents = $self->isa_path(); |
3085
|
|
|
|
|
|
|
|
3086
|
82
|
|
|
|
|
212
|
my %defaults = (); |
3087
|
|
|
|
|
|
|
|
3088
|
82
|
100
|
|
|
|
268
|
if (ref $_[0] eq 'HASH') { |
3089
|
1
|
|
|
|
|
2
|
my $defhash = shift @_; |
3090
|
1
|
|
|
|
|
5
|
@defaults{keys %$defhash} = values %$defhash; |
3091
|
|
|
|
|
|
|
} |
3092
|
|
|
|
|
|
|
|
3093
|
|
|
|
|
|
|
#initialize our values brought in from the conf file |
3094
|
82
|
|
|
|
|
201
|
foreach my $pkg (@$parents){ |
3095
|
|
|
|
|
|
|
|
3096
|
144
|
|
|
|
|
206
|
my %pkgdef = map {substr($_,1), $conf->{$pkg}->{$_}} grep {/^-/} keys %{$conf->{$pkg}}; |
|
16
|
|
|
|
|
102
|
|
|
197
|
|
|
|
|
628
|
|
|
144
|
|
|
|
|
484
|
|
3097
|
|
|
|
|
|
|
|
3098
|
144
|
|
|
|
|
453
|
@defaults{keys %pkgdef} = values %pkgdef; |
3099
|
|
|
|
|
|
|
|
3100
|
|
|
|
|
|
|
} |
3101
|
|
|
|
|
|
|
|
3102
|
82
|
|
|
|
|
331
|
my @init = (%defaults, @_); |
3103
|
|
|
|
|
|
|
|
3104
|
|
|
|
|
|
|
#initialize our values passed in to the constructor |
3105
|
|
|
|
|
|
|
# foreach my $method (keys %init){ |
3106
|
|
|
|
|
|
|
# my $value = $init{$method}; |
3107
|
82
|
|
|
|
|
259
|
while (@init) { |
3108
|
419
|
|
|
|
|
789
|
my ($method, $value) = splice(@init, 0, 2); |
3109
|
|
|
|
|
|
|
#my $method = shift @init; |
3110
|
|
|
|
|
|
|
#my $value = shift @init; |
3111
|
|
|
|
|
|
|
|
3112
|
419
|
100
|
|
|
|
1712
|
if ($self->can($method)){ |
3113
|
|
|
|
|
|
|
# $self->wipe_errors(); |
3114
|
416
|
|
|
|
|
1029
|
my $return = $self->$method($value); |
3115
|
|
|
|
|
|
|
|
3116
|
416
|
100
|
100
|
|
|
1672
|
return $self->error("Could not initilize method ($method) to value ($value)" |
|
|
100
|
66
|
|
|
|
|
3117
|
|
|
|
|
|
|
. (defined $self->error ? " : " . $self->error : ' ') |
3118
|
|
|
|
|
|
|
, ($self->errcode || "BO-03") |
3119
|
|
|
|
|
|
|
) unless defined $return || ! defined $value; |
3120
|
|
|
|
|
|
|
}; |
3121
|
|
|
|
|
|
|
}; |
3122
|
|
|
|
|
|
|
|
3123
|
80
|
|
|
|
|
643
|
return $self; |
3124
|
|
|
|
|
|
|
}; |
3125
|
|
|
|
|
|
|
|
3126
|
|
|
|
|
|
|
=pod |
3127
|
|
|
|
|
|
|
|
3128
|
|
|
|
|
|
|
=item pkg |
3129
|
|
|
|
|
|
|
|
3130
|
|
|
|
|
|
|
Returns the package (class) of the object. Note that this is not necessarily the same as ref $object. This is |
3131
|
|
|
|
|
|
|
because of some wackiness in how perl handles some internal things that I don't quite understand. |
3132
|
|
|
|
|
|
|
Suffice to say that even if you bless an object into a class Foo, ref $object may not always be 'Foo'. |
3133
|
|
|
|
|
|
|
Sometimes it may be 'main::Foo' and sometimes it may be '::Foo'. I'll leave the reasons why for |
3134
|
|
|
|
|
|
|
others to document. This method is just here to keep that from biting you. |
3135
|
|
|
|
|
|
|
|
3136
|
|
|
|
|
|
|
=cut |
3137
|
|
|
|
|
|
|
|
3138
|
|
|
|
|
|
|
=pod |
3139
|
|
|
|
|
|
|
|
3140
|
|
|
|
|
|
|
=begin btest(pkg) |
3141
|
|
|
|
|
|
|
|
3142
|
|
|
|
|
|
|
package main::Basset::Test::Testing::__PACKAGE__::MainSubClass; |
3143
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3144
|
|
|
|
|
|
|
|
3145
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::MainSubClass2; |
3146
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3147
|
|
|
|
|
|
|
|
3148
|
|
|
|
|
|
|
package ::Basset::Test::Testing::__PACKAGE__::MainSubClass3; |
3149
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3150
|
|
|
|
|
|
|
|
3151
|
|
|
|
|
|
|
package __PACKAGE__; |
3152
|
|
|
|
|
|
|
|
3153
|
|
|
|
|
|
|
$test->ok(main::Basset::Test::Testing::__PACKAGE__::MainSubClass->isa('__PACKAGE__'), "Created subclass"); |
3154
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::MainSubClass2->isa('__PACKAGE__'), "Created subclass"); |
3155
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::MainSubClass3->isa('__PACKAGE__'), "Created subclass"); |
3156
|
|
|
|
|
|
|
|
3157
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
3158
|
|
|
|
|
|
|
$test->ok($o, "Created object"); |
3159
|
|
|
|
|
|
|
|
3160
|
|
|
|
|
|
|
my $so1 = main::Basset::Test::Testing::__PACKAGE__::MainSubClass->new(); |
3161
|
|
|
|
|
|
|
$test->ok($so1, "Created sub-object"); |
3162
|
|
|
|
|
|
|
|
3163
|
|
|
|
|
|
|
my $so2 = Basset::Test::Testing::__PACKAGE__::MainSubClass2->new(); |
3164
|
|
|
|
|
|
|
$test->ok($so2, "Created sub-object"); |
3165
|
|
|
|
|
|
|
|
3166
|
|
|
|
|
|
|
my $so3 = Basset::Test::Testing::__PACKAGE__::MainSubClass3->new(); |
3167
|
|
|
|
|
|
|
$test->ok($so3, "Created sub-object"); |
3168
|
|
|
|
|
|
|
|
3169
|
|
|
|
|
|
|
$test->is($o->pkg, "__PACKAGE__", "Superclass works"); |
3170
|
|
|
|
|
|
|
$test->is($so1->pkg, "Basset::Test::Testing::__PACKAGE__::MainSubClass", "Subclass works"); |
3171
|
|
|
|
|
|
|
$test->is($so2->pkg, "Basset::Test::Testing::__PACKAGE__::MainSubClass2", "Subclass works"); |
3172
|
|
|
|
|
|
|
$test->is($so3->pkg, "Basset::Test::Testing::__PACKAGE__::MainSubClass3", "Subclass works"); |
3173
|
|
|
|
|
|
|
|
3174
|
|
|
|
|
|
|
=end btest(pkg) |
3175
|
|
|
|
|
|
|
|
3176
|
|
|
|
|
|
|
=cut |
3177
|
|
|
|
|
|
|
|
3178
|
|
|
|
|
|
|
sub pkg { |
3179
|
3123
|
|
66
|
3123
|
1
|
11075
|
my $class = ref($_[0]) || $_[0]; |
3180
|
3123
|
100
|
|
|
|
16526
|
if (index($class, '::') == 0) { |
|
|
100
|
|
|
|
|
|
3181
|
3
|
|
|
|
|
8
|
$class = substr($class, 2); |
3182
|
|
|
|
|
|
|
} elsif (index($class, 'main::') == 0) { |
3183
|
4
|
|
|
|
|
10
|
$class = substr($class, 6); |
3184
|
|
|
|
|
|
|
}; |
3185
|
|
|
|
|
|
|
|
3186
|
3123
|
|
|
|
|
5945
|
return $class; |
3187
|
|
|
|
|
|
|
}; |
3188
|
|
|
|
|
|
|
|
3189
|
|
|
|
|
|
|
=pod |
3190
|
|
|
|
|
|
|
|
3191
|
|
|
|
|
|
|
=item factory |
3192
|
|
|
|
|
|
|
|
3193
|
|
|
|
|
|
|
Abstract factory constructor. Works just like ->new() except it expects to receive a type. The types are listed in the conf |
3194
|
|
|
|
|
|
|
file to determine which type of object to instantiate. |
3195
|
|
|
|
|
|
|
|
3196
|
|
|
|
|
|
|
In conf file: |
3197
|
|
|
|
|
|
|
|
3198
|
|
|
|
|
|
|
define package Basset::Object |
3199
|
|
|
|
|
|
|
types @= user=Basset::User |
3200
|
|
|
|
|
|
|
types @= group=Basset::Group |
3201
|
|
|
|
|
|
|
|
3202
|
|
|
|
|
|
|
And then, in your program: |
3203
|
|
|
|
|
|
|
|
3204
|
|
|
|
|
|
|
my $user = Basset::Object->factory( |
3205
|
|
|
|
|
|
|
'type' => 'user' |
3206
|
|
|
|
|
|
|
); |
3207
|
|
|
|
|
|
|
|
3208
|
|
|
|
|
|
|
$user is a Basset::User object. Use for objects that are supposed to be used in multiple applications. This allows you to swap |
3209
|
|
|
|
|
|
|
out particular objects for different (but similar!) ones by just changing the conf file, not all your code. |
3210
|
|
|
|
|
|
|
|
3211
|
|
|
|
|
|
|
=cut |
3212
|
|
|
|
|
|
|
|
3213
|
|
|
|
|
|
|
=pod |
3214
|
|
|
|
|
|
|
|
3215
|
|
|
|
|
|
|
=begin btest(factory) |
3216
|
|
|
|
|
|
|
|
3217
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::factory::Subclass; |
3218
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3219
|
|
|
|
|
|
|
|
3220
|
|
|
|
|
|
|
package __PACKAGE__; |
3221
|
|
|
|
|
|
|
|
3222
|
|
|
|
|
|
|
my $oldtypes = __PACKAGE__->types(); |
3223
|
|
|
|
|
|
|
$test->ok($oldtypes, "Saved old types"); |
3224
|
|
|
|
|
|
|
my $newtypes = {%$oldtypes, 'factory_test_type' => '__PACKAGE__'}; |
3225
|
|
|
|
|
|
|
$test->is(__PACKAGE__->types($newtypes), $newtypes, "Set new types"); |
3226
|
|
|
|
|
|
|
$test->is(__PACKAGE__->pkg_for_type('factory_test_type'), '__PACKAGE__', 'can get class for type'); |
3227
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
3228
|
|
|
|
|
|
|
$test->ok($o, "Created new object"); |
3229
|
|
|
|
|
|
|
my $o2 = __PACKAGE__->factory('type' => 'factory_test_type'); |
3230
|
|
|
|
|
|
|
$test->ok($o2, "Factoried new object"); |
3231
|
|
|
|
|
|
|
$test->ok($o2->isa('__PACKAGE__'), "Factory object isa class object"); |
3232
|
|
|
|
|
|
|
$test->is(__PACKAGE__->types($oldtypes), $oldtypes, "reset old types"); |
3233
|
|
|
|
|
|
|
|
3234
|
|
|
|
|
|
|
=end btest(factory) |
3235
|
|
|
|
|
|
|
|
3236
|
|
|
|
|
|
|
=cut |
3237
|
|
|
|
|
|
|
|
3238
|
|
|
|
|
|
|
sub factory { |
3239
|
4
|
|
|
4
|
1
|
416
|
my $class = shift; |
3240
|
|
|
|
|
|
|
|
3241
|
4
|
|
|
|
|
20
|
my %init = @_; |
3242
|
|
|
|
|
|
|
|
3243
|
4
|
50
|
|
|
|
20
|
if ($init{'type'}) { |
3244
|
|
|
|
|
|
|
|
3245
|
4
|
|
|
|
|
9
|
my $abstype = $init{'type'}; |
3246
|
4
|
|
|
|
|
10
|
delete $init{'type'}; |
3247
|
|
|
|
|
|
|
|
3248
|
4
|
50
|
|
|
|
23
|
my $typeClass = $class->pkg_for_type($abstype) or return; |
3249
|
|
|
|
|
|
|
|
3250
|
4
|
|
33
|
|
|
28
|
return $typeClass->new(%init) || $class->error($typeClass->errvals); |
3251
|
|
|
|
|
|
|
} |
3252
|
|
|
|
|
|
|
else { |
3253
|
0
|
|
|
|
|
0
|
return $class->new(@_); |
3254
|
|
|
|
|
|
|
}; |
3255
|
|
|
|
|
|
|
} |
3256
|
|
|
|
|
|
|
|
3257
|
|
|
|
|
|
|
=pod |
3258
|
|
|
|
|
|
|
|
3259
|
|
|
|
|
|
|
=item copy |
3260
|
|
|
|
|
|
|
|
3261
|
|
|
|
|
|
|
Copies the object. B! Copy does a B copy of the object. So any objects/references/etc |
3262
|
|
|
|
|
|
|
pointed to by the original object will also be copied. |
3263
|
|
|
|
|
|
|
|
3264
|
|
|
|
|
|
|
You may optionally pass in a different object/structure and copy that instead. |
3265
|
|
|
|
|
|
|
|
3266
|
|
|
|
|
|
|
my $backupBoard = $game->copy($game->board); |
3267
|
|
|
|
|
|
|
|
3268
|
|
|
|
|
|
|
=cut |
3269
|
|
|
|
|
|
|
|
3270
|
|
|
|
|
|
|
=pod |
3271
|
|
|
|
|
|
|
|
3272
|
|
|
|
|
|
|
=begin btest(copy) |
3273
|
|
|
|
|
|
|
|
3274
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::copy::subclass; |
3275
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3276
|
|
|
|
|
|
|
|
3277
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::copy::subclass->add_attr('attr1'); |
3278
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::copy::subclass->add_attr('attr2'); |
3279
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::copy::subclass->add_attr('attr3'); |
3280
|
|
|
|
|
|
|
|
3281
|
|
|
|
|
|
|
package __PACKAGE__; |
3282
|
|
|
|
|
|
|
|
3283
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
3284
|
|
|
|
|
|
|
$test->ok($o, "Instantiated object"); |
3285
|
|
|
|
|
|
|
my $o2 = $o->copy; |
3286
|
|
|
|
|
|
|
$test->ok($o2, "Copied object"); |
3287
|
|
|
|
|
|
|
$test->is(length $o->dump, length $o2->dump, "dumps are same size"); |
3288
|
|
|
|
|
|
|
|
3289
|
|
|
|
|
|
|
my $o3 = Basset::Test::Testing::__PACKAGE__::copy::subclass->new( |
3290
|
|
|
|
|
|
|
'attr1' => 'first attribute', |
3291
|
|
|
|
|
|
|
'attr2' => 'second attribute', |
3292
|
|
|
|
|
|
|
'attr3' => 'third attribute' |
3293
|
|
|
|
|
|
|
); |
3294
|
|
|
|
|
|
|
|
3295
|
|
|
|
|
|
|
$test->ok($o3, "Instantiated sub-object"); |
3296
|
|
|
|
|
|
|
|
3297
|
|
|
|
|
|
|
$test->is($o3->attr1, 'first attribute', 'Subobject attr1 matches'); |
3298
|
|
|
|
|
|
|
$test->is($o3->attr2, 'second attribute', 'Subobject attr2 matches'); |
3299
|
|
|
|
|
|
|
$test->is($o3->attr3, 'third attribute', 'Subobject attr3 matches'); |
3300
|
|
|
|
|
|
|
|
3301
|
|
|
|
|
|
|
my $o4 = $o3->copy; |
3302
|
|
|
|
|
|
|
|
3303
|
|
|
|
|
|
|
$test->ok($o4, "Copied sub-object"); |
3304
|
|
|
|
|
|
|
|
3305
|
|
|
|
|
|
|
$test->is($o4->attr1, 'first attribute', 'Copied subobject attr1 matches'); |
3306
|
|
|
|
|
|
|
$test->is($o4->attr2, 'second attribute', 'Copied subobject attr2 matches'); |
3307
|
|
|
|
|
|
|
$test->is($o4->attr3, 'third attribute', 'Copied subobject attr3 matches'); |
3308
|
|
|
|
|
|
|
|
3309
|
|
|
|
|
|
|
$test->is(length $o3->dump, length $o4->dump, "Sub object dumps are same size"); |
3310
|
|
|
|
|
|
|
|
3311
|
|
|
|
|
|
|
my $array = ['a', 2, {'foo' => 'bar'}]; |
3312
|
|
|
|
|
|
|
|
3313
|
|
|
|
|
|
|
$test->ok($array, "Got array"); |
3314
|
|
|
|
|
|
|
|
3315
|
|
|
|
|
|
|
my $array2 = __PACKAGE__->copy($array); |
3316
|
|
|
|
|
|
|
|
3317
|
|
|
|
|
|
|
$test->ok($array2, "Copied array"); |
3318
|
|
|
|
|
|
|
$test->is($array->[0], $array2->[0], "First element matches"); |
3319
|
|
|
|
|
|
|
$test->is($array->[1], $array2->[1], "Second element matches"); |
3320
|
|
|
|
|
|
|
$test->is($array->[2]->{'foo'}, $array2->[2]->{'foo'}, "Third element matches"); |
3321
|
|
|
|
|
|
|
|
3322
|
|
|
|
|
|
|
=end btest(copy) |
3323
|
|
|
|
|
|
|
|
3324
|
|
|
|
|
|
|
=cut |
3325
|
|
|
|
|
|
|
|
3326
|
|
|
|
|
|
|
sub copy { |
3327
|
5
|
|
|
5
|
1
|
1454
|
my $self = shift; |
3328
|
5
|
|
66
|
|
|
28
|
my $obj = shift || $self; |
3329
|
|
|
|
|
|
|
|
3330
|
5
|
|
|
|
|
28
|
my $objdump = $self->dump($obj); |
3331
|
5
|
|
|
|
|
503
|
$objdump =~ /^(\$\w+)/; |
3332
|
|
|
|
|
|
|
|
3333
|
5
|
|
|
|
|
12
|
local $@ = undef; |
3334
|
5
|
|
|
|
|
355
|
return eval qq{ |
3335
|
|
|
|
|
|
|
my $1; |
3336
|
|
|
|
|
|
|
eval \$objdump; |
3337
|
|
|
|
|
|
|
}; |
3338
|
|
|
|
|
|
|
} |
3339
|
|
|
|
|
|
|
|
3340
|
|
|
|
|
|
|
=pod |
3341
|
|
|
|
|
|
|
|
3342
|
|
|
|
|
|
|
=item pkg_for_type |
3343
|
|
|
|
|
|
|
|
3344
|
|
|
|
|
|
|
Use internally by factory(), also sometimes useful in code. Given a type, returns the class as defined in the conf file. |
3345
|
|
|
|
|
|
|
|
3346
|
|
|
|
|
|
|
my $class = Basset::Object->pkg_for_type('user'); #returns Basset::User (for example) |
3347
|
|
|
|
|
|
|
|
3348
|
|
|
|
|
|
|
=cut |
3349
|
|
|
|
|
|
|
|
3350
|
|
|
|
|
|
|
=pod |
3351
|
|
|
|
|
|
|
|
3352
|
|
|
|
|
|
|
=begin btest(pkg_for_type) |
3353
|
|
|
|
|
|
|
|
3354
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->types, "Got types out of the conf file"); |
3355
|
|
|
|
|
|
|
my $typesbkp = __PACKAGE__->types(); |
3356
|
|
|
|
|
|
|
my $newtypes = {%$typesbkp, 'testtype1' => '__PACKAGE__', 'testtype2' => 'boguspkg'}; |
3357
|
|
|
|
|
|
|
$test->ok($typesbkp, "Backed up the types"); |
3358
|
|
|
|
|
|
|
$test->is(__PACKAGE__->types($newtypes), $newtypes, "Set new types"); |
3359
|
|
|
|
|
|
|
$test->is(__PACKAGE__->pkg_for_type('testtype1'), '__PACKAGE__', "Got class for new type"); |
3360
|
|
|
|
|
|
|
$test->ok(! scalar __PACKAGE__->pkg_for_type('testtype2'), "Could not access invalid type"); |
3361
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, 'BO-29', 'proper error code'); |
3362
|
|
|
|
|
|
|
|
3363
|
|
|
|
|
|
|
__PACKAGE__->wipe_errors; |
3364
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->pkg_for_type('testtype2', 'errorless')), undef, "Could not access invalid type w/ second arg"); |
3365
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->errcode), undef, 'no error code set w/second arg'); |
3366
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->errstring), undef, 'no error string set w/second arg'); |
3367
|
|
|
|
|
|
|
|
3368
|
|
|
|
|
|
|
my $h = {}; |
3369
|
|
|
|
|
|
|
|
3370
|
|
|
|
|
|
|
$test->is(__PACKAGE__->types($h), $h, 'wiped out types'); |
3371
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->pkg_for_type('testtype3')), undef, 'could not get type w/o types'); |
3372
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, 'BO-09', 'proper error code for no types'); |
3373
|
|
|
|
|
|
|
|
3374
|
|
|
|
|
|
|
$test->is(__PACKAGE__->types($typesbkp), $typesbkp, "Re-set original types"); |
3375
|
|
|
|
|
|
|
|
3376
|
|
|
|
|
|
|
=end btest(pkg_for_type) |
3377
|
|
|
|
|
|
|
|
3378
|
|
|
|
|
|
|
=cut |
3379
|
|
|
|
|
|
|
|
3380
|
|
|
|
|
|
|
sub pkg_for_type { |
3381
|
189
|
|
|
189
|
1
|
2343
|
my $class = shift; |
3382
|
189
|
|
|
|
|
284
|
my $abstype = shift; |
3383
|
|
|
|
|
|
|
#this is a hack and not publically accessible. If you pass in a second parameter for pkg_for_type, |
3384
|
|
|
|
|
|
|
#it won't report an error if it doesn't find the class. This should be used in one and only one place - |
3385
|
|
|
|
|
|
|
#inside of the error method itself. error requests a notification center, and if there is no notification |
3386
|
|
|
|
|
|
|
#center, then it needs to be able to continue. If pkg_for_type spit back an error, it'd fall into an infinite |
3387
|
|
|
|
|
|
|
#recursion. So we take the 2nd parameter to prevent that from happening. |
3388
|
189
|
100
|
|
|
|
599
|
my $errorless = @_ ? shift : 0; |
3389
|
|
|
|
|
|
|
|
3390
|
189
|
|
|
|
|
1420
|
my $types = $class->types; |
3391
|
|
|
|
|
|
|
|
3392
|
189
|
|
|
|
|
1138
|
my $pkg = $types->{$abstype}; |
3393
|
|
|
|
|
|
|
|
3394
|
189
|
100
|
|
|
|
525
|
if (defined $pkg) { |
3395
|
|
|
|
|
|
|
|
3396
|
187
|
100
|
|
|
|
778
|
return unless $class->load_pkg($pkg, $errorless); |
3397
|
|
|
|
|
|
|
|
3398
|
184
|
|
|
|
|
1035
|
return $pkg; |
3399
|
|
|
|
|
|
|
|
3400
|
|
|
|
|
|
|
} else { |
3401
|
2
|
100
|
|
|
|
12
|
return $errorless ? undef : $class->error("No class for type ($abstype)", "BO-09"); |
3402
|
|
|
|
|
|
|
} |
3403
|
|
|
|
|
|
|
|
3404
|
|
|
|
|
|
|
}; |
3405
|
|
|
|
|
|
|
|
3406
|
|
|
|
|
|
|
=pod |
3407
|
|
|
|
|
|
|
|
3408
|
|
|
|
|
|
|
=item inherits |
3409
|
|
|
|
|
|
|
|
3410
|
|
|
|
|
|
|
This method is deprecated and b be removed in Basset 1.0.4. The concept remains the same, but I, like an idiot, overlooked a |
3411
|
|
|
|
|
|
|
much simpler syntax. Just push the result of pkg_for_type onto @ISA as normal. |
3412
|
|
|
|
|
|
|
|
3413
|
|
|
|
|
|
|
use Basset::Object; |
3414
|
|
|
|
|
|
|
our @ISA = Basset::Object->pkg_for_type('object'); |
3415
|
|
|
|
|
|
|
|
3416
|
|
|
|
|
|
|
Voila! Same effect. You may now proceed to read the long expository explanation here as to why you would do that. This exposition is going |
3417
|
|
|
|
|
|
|
to slide over into the pkg_for_type method. |
3418
|
|
|
|
|
|
|
|
3419
|
|
|
|
|
|
|
Basset is a nice framework. It kicks all sorts of ass. But, it's entirely possible that it's not quite functional enough for you. |
3420
|
|
|
|
|
|
|
Let's say you work for some company, WidgetTech. |
3421
|
|
|
|
|
|
|
|
3422
|
|
|
|
|
|
|
WidgetTech has information in a database, it's mostly fairly object-relational in nature, you can certainly use Basset::Object::Persistent. |
3423
|
|
|
|
|
|
|
So you go through and write up 50 modules that all inherit from Basset::Object::Persistent. All is right with the world. |
3424
|
|
|
|
|
|
|
|
3425
|
|
|
|
|
|
|
3 months later, someone decides that instead of deleting old records from the database, as you'd been doing, you need to instead |
3426
|
|
|
|
|
|
|
leave them there and change their status flag to 'D'. The status flag is already there (you use it for other things, active, pending |
3427
|
|
|
|
|
|
|
suspended, etc.). So you don't need to change anything in your modules - just add the drop down to your interface and all is good. |
3428
|
|
|
|
|
|
|
|
3429
|
|
|
|
|
|
|
2 days later, you're getting angry phonecalls from users saying that deleted data is showing up in the system. This is bad. You |
3430
|
|
|
|
|
|
|
forgot that Basset::Object::Persistent doesn't know anything about status flags and just loads up everything. Very bad. |
3431
|
|
|
|
|
|
|
|
3432
|
|
|
|
|
|
|
Options? Well, you could go into every single module (50 of 'em) and override their load_all and delete methods. |
3433
|
|
|
|
|
|
|
But man, that's gonna take forever. And probably get out of sync. And be a maintenance disaster. And it's just not the Basset way. |
3434
|
|
|
|
|
|
|
|
3435
|
|
|
|
|
|
|
So what do you do instead? You hack up Basset::Object::Persistent. You modify the load_all method so that it tacks on a where |
3436
|
|
|
|
|
|
|
clause to exclude status of 'D'. You modify delete so that it just changes the status and re-commits. All is right with the world. |
3437
|
|
|
|
|
|
|
|
3438
|
|
|
|
|
|
|
A month later, I release a new version of Basset, you forget about the modifications, upgrade, and start getting calls from angry |
3439
|
|
|
|
|
|
|
users. You need to re-hack the system. |
3440
|
|
|
|
|
|
|
|
3441
|
|
|
|
|
|
|
So, you realize, this isn't the best way to go. Instead, you write a new object - WidgetTech::Object::Persistent. |
3442
|
|
|
|
|
|
|
WidgetTech::Object::Persistent inherits from Basset::Object::Persistent. You then do a search and replace on your 50 modules to |
3443
|
|
|
|
|
|
|
change occurances of Basset::Object::Persistent to WidgetTech::Object::Persistent. You put your modified load_all and delete methods |
3444
|
|
|
|
|
|
|
in WidgetTech::Object::Persistent and all is right with the world. I release a new version of Basset a week later, you drop it into |
3445
|
|
|
|
|
|
|
place, there are no issues. |
3446
|
|
|
|
|
|
|
|
3447
|
|
|
|
|
|
|
Two months later, you decide that you need to override a method in Basset::Object. Or, you want a new method accessible to all of |
3448
|
|
|
|
|
|
|
your objects. Easy - put it in the root class. Now, you've learned enough not to hack up Basset::Object, so you create WidgetTech::Object |
3449
|
|
|
|
|
|
|
and add in your new method to there. Anything that did inherit from Basset::Object should now inherit WidgetTech::Object and everything's |
3450
|
|
|
|
|
|
|
fine. |
3451
|
|
|
|
|
|
|
|
3452
|
|
|
|
|
|
|
Whoops. Except for WidgetTech::Object::Persistent. You have an inheritance tree like this: |
3453
|
|
|
|
|
|
|
|
3454
|
|
|
|
|
|
|
Basset::Object |
3455
|
|
|
|
|
|
|
^ ^ |
3456
|
|
|
|
|
|
|
| | |
3457
|
|
|
|
|
|
|
| WidgetTech::Object |
3458
|
|
|
|
|
|
|
| |
3459
|
|
|
|
|
|
|
Basset::Object::Persistent |
3460
|
|
|
|
|
|
|
^ |
3461
|
|
|
|
|
|
|
| |
3462
|
|
|
|
|
|
|
WidgetTech::Object::Persistent |
3463
|
|
|
|
|
|
|
|
3464
|
|
|
|
|
|
|
But you need this: |
3465
|
|
|
|
|
|
|
|
3466
|
|
|
|
|
|
|
Basset::Object |
3467
|
|
|
|
|
|
|
^ |
3468
|
|
|
|
|
|
|
| |
3469
|
|
|
|
|
|
|
WidgetTech::Object |
3470
|
|
|
|
|
|
|
^ |
3471
|
|
|
|
|
|
|
| |
3472
|
|
|
|
|
|
|
Basset::Object::Persistent |
3473
|
|
|
|
|
|
|
^ |
3474
|
|
|
|
|
|
|
| |
3475
|
|
|
|
|
|
|
WidgetTech::Object::Persistent |
3476
|
|
|
|
|
|
|
|
3477
|
|
|
|
|
|
|
Your W::O::P inherit B::O::P which inherits B::O. And this all bypasses WidgetTech::Object. You don't want to stick the methods |
3478
|
|
|
|
|
|
|
into WidgetTech::Object::Persistent, since they need to be accessible to all classes, not just persistent ones. You (obviously) |
3479
|
|
|
|
|
|
|
know better than to hack Basset::Object::Persistent to inherit from WidgetTech::Object instead of Basset::Object. So what do you |
3480
|
|
|
|
|
|
|
do? |
3481
|
|
|
|
|
|
|
|
3482
|
|
|
|
|
|
|
And all of this long expository setup brings us to the inherits method. Inheritance in Basset does not usually directly use @ISA. |
3483
|
|
|
|
|
|
|
Instead, it uses the inherits class method and a classtype. |
3484
|
|
|
|
|
|
|
|
3485
|
|
|
|
|
|
|
package Basset::Object::Persistent; |
3486
|
|
|
|
|
|
|
|
3487
|
|
|
|
|
|
|
use Basset::Object; |
3488
|
|
|
|
|
|
|
#deprecated old way: |
3489
|
|
|
|
|
|
|
#Basset::Object->inherits(__PACKAGE__, 'object'); |
3490
|
|
|
|
|
|
|
#fancy new way: |
3491
|
|
|
|
|
|
|
@ISA = ( Basset::Object->pkg_for_type('object') ); |
3492
|
|
|
|
|
|
|
|
3493
|
|
|
|
|
|
|
Voila! That's basically equivalent to: |
3494
|
|
|
|
|
|
|
|
3495
|
|
|
|
|
|
|
package Basset::Object::Persistent; |
3496
|
|
|
|
|
|
|
|
3497
|
|
|
|
|
|
|
use Basset::Object; |
3498
|
|
|
|
|
|
|
@ISA = qw(Basset::Object); |
3499
|
|
|
|
|
|
|
|
3500
|
|
|
|
|
|
|
Now, everybody knows that familiar @ISA = ... syntax, so why change it? If you read that story up above, you already know. This |
3501
|
|
|
|
|
|
|
moves inheritance out of the module tree and into B. So now if you want to use WidgetTech::Objects as your root |
3502
|
|
|
|
|
|
|
object, you just change your conf file: |
3503
|
|
|
|
|
|
|
|
3504
|
|
|
|
|
|
|
types %= object=WidgetTech::Object |
3505
|
|
|
|
|
|
|
|
3506
|
|
|
|
|
|
|
And blam-o. You have a new root class. Now, of course, Basset::Object will B be the top level root object in a Basset system. |
3507
|
|
|
|
|
|
|
But you can now pretend that you have a different object instead. This new object sits in between Basset::Object and the rest of the |
3508
|
|
|
|
|
|
|
world. Anything you want to change in Basset::Object is fair game. The only thing that B always be in Basset::Object is the |
3509
|
|
|
|
|
|
|
inherits method. Other modules will expect Basset::Object to call inherits at their start to set up their @ISA for them, so you can't |
3510
|
|
|
|
|
|
|
do away with it entirely. |
3511
|
|
|
|
|
|
|
|
3512
|
|
|
|
|
|
|
B. It's a compilation error, so it's not going to let you off the hook if it can't set up a relationship. |
3513
|
|
|
|
|
|
|
|
3514
|
|
|
|
|
|
|
You'll mostly be fine with using @ISA in your code. |
3515
|
|
|
|
|
|
|
|
3516
|
|
|
|
|
|
|
package WidgetTech::Widget; |
3517
|
|
|
|
|
|
|
@ISA = qw(WidgetTech::Object::Persistent); |
3518
|
|
|
|
|
|
|
|
3519
|
|
|
|
|
|
|
You have control over WidgetTech::Widget and WidgetTech::Object::Persistent, and it's highly unlikely that you'll need to |
3520
|
|
|
|
|
|
|
change your inheritance tree. Modifications can go in your super class or your subclass as needed and nobody cares about re-wiring |
3521
|
|
|
|
|
|
|
it. |
3522
|
|
|
|
|
|
|
|
3523
|
|
|
|
|
|
|
=cut |
3524
|
|
|
|
|
|
|
|
3525
|
|
|
|
|
|
|
sub inherits { |
3526
|
1
|
|
|
1
|
1
|
547
|
my $self = shift; |
3527
|
1
|
|
|
|
|
2
|
my $pkg = shift; |
3528
|
1
|
|
|
|
|
3
|
my @types = @_; |
3529
|
|
|
|
|
|
|
|
3530
|
8
|
|
|
8
|
|
66
|
no strict 'refs'; |
|
8
|
|
|
|
|
22
|
|
|
8
|
|
|
|
|
9336
|
|
3531
|
|
|
|
|
|
|
|
3532
|
1
|
|
|
|
|
3
|
foreach my $type (@types) { |
3533
|
1
|
|
50
|
|
|
5
|
my $parent = $self->pkg_for_type($type) || die $self->errstring; |
3534
|
|
|
|
|
|
|
|
3535
|
1
|
|
|
|
|
4
|
push @{$pkg . "::ISA"}, $parent; |
|
1
|
|
|
|
|
22
|
|
3536
|
|
|
|
|
|
|
} |
3537
|
|
|
|
|
|
|
|
3538
|
1
|
|
|
|
|
4
|
return 1; |
3539
|
|
|
|
|
|
|
} |
3540
|
|
|
|
|
|
|
|
3541
|
|
|
|
|
|
|
=pod |
3542
|
|
|
|
|
|
|
|
3543
|
|
|
|
|
|
|
=begin btest(inherits) |
3544
|
|
|
|
|
|
|
|
3545
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::inherits::Subclass1; |
3546
|
|
|
|
|
|
|
__PACKAGE__->inherits('Basset::Test::Testing::__PACKAGE__::inherits::Subclass1', 'object'); |
3547
|
|
|
|
|
|
|
|
3548
|
|
|
|
|
|
|
package __PACKAGE__; |
3549
|
|
|
|
|
|
|
|
3550
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::inherits::Subclass1->isa('Basset::Object'), 'subclass inherits from root'); |
3551
|
|
|
|
|
|
|
|
3552
|
|
|
|
|
|
|
=end btest(inherits) |
3553
|
|
|
|
|
|
|
|
3554
|
|
|
|
|
|
|
=cut |
3555
|
|
|
|
|
|
|
|
3556
|
|
|
|
|
|
|
=pod |
3557
|
|
|
|
|
|
|
|
3558
|
|
|
|
|
|
|
=item isa_path |
3559
|
|
|
|
|
|
|
|
3560
|
|
|
|
|
|
|
This is mainly used by the conf reader, but I wanted to make it publicly accessible. Given a class, it |
3561
|
|
|
|
|
|
|
will return an arrayref containing all of the superclasses of that class, in inheritence order. |
3562
|
|
|
|
|
|
|
|
3563
|
|
|
|
|
|
|
Note that once a path is looked up for a class, it is cached. So if you dynamically change @ISA, it won't be reflected in the return of isa_path. |
3564
|
|
|
|
|
|
|
Obviously, dynamically changing @ISA is frowned upon as a result. |
3565
|
|
|
|
|
|
|
|
3566
|
|
|
|
|
|
|
=cut |
3567
|
|
|
|
|
|
|
|
3568
|
|
|
|
|
|
|
=pod |
3569
|
|
|
|
|
|
|
|
3570
|
|
|
|
|
|
|
=begin btest(isa_path) |
3571
|
|
|
|
|
|
|
|
3572
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->isa_path, "Can get an isa_path for root"); |
3573
|
|
|
|
|
|
|
my $path = __PACKAGE__->isa_path; |
3574
|
|
|
|
|
|
|
$test->is($path->[-1], '__PACKAGE__', 'Class has self at end of path'); |
3575
|
|
|
|
|
|
|
|
3576
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::isa_path::subclass1; |
3577
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3578
|
|
|
|
|
|
|
|
3579
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::isa_path::subclass2; |
3580
|
|
|
|
|
|
|
our @ISA = qw(Basset::Test::Testing::__PACKAGE__::isa_path::subclass1); |
3581
|
|
|
|
|
|
|
|
3582
|
|
|
|
|
|
|
package __PACKAGE__; |
3583
|
|
|
|
|
|
|
|
3584
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::isa_path::subclass1->isa('__PACKAGE__'), 'Subclass of __PACKAGE__'); |
3585
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::isa_path::subclass2->isa('__PACKAGE__'), 'Sub-subclass of __PACKAGE__'); |
3586
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::isa_path::subclass1->isa('Basset::Test::Testing::__PACKAGE__::isa_path::subclass1'), 'Sub-subclass of subclass'); |
3587
|
|
|
|
|
|
|
|
3588
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::isa_path::subclass1->isa_path, "We have a path"); |
3589
|
|
|
|
|
|
|
my $subpath = Basset::Test::Testing::__PACKAGE__::isa_path::subclass1->isa_path; |
3590
|
|
|
|
|
|
|
$test->is($subpath->[-2], '__PACKAGE__', 'Next to last entry is parent'); |
3591
|
|
|
|
|
|
|
$test->is($subpath->[-1], 'Basset::Test::Testing::__PACKAGE__::isa_path::subclass1', 'Last entry is self'); |
3592
|
|
|
|
|
|
|
|
3593
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::isa_path::subclass2->isa_path, "We have a sub path"); |
3594
|
|
|
|
|
|
|
my $subsubpath = Basset::Test::Testing::__PACKAGE__::isa_path::subclass2->isa_path; |
3595
|
|
|
|
|
|
|
|
3596
|
|
|
|
|
|
|
$test->is($subsubpath->[-3], '__PACKAGE__', 'Third to last entry is grandparent'); |
3597
|
|
|
|
|
|
|
$test->is($subsubpath->[-2], 'Basset::Test::Testing::__PACKAGE__::isa_path::subclass1', 'Second to last entry is parent'); |
3598
|
|
|
|
|
|
|
$test->is($subsubpath->[-1], 'Basset::Test::Testing::__PACKAGE__::isa_path::subclass2', 'Last entry is self'); |
3599
|
|
|
|
|
|
|
|
3600
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::isa_path::Subclass3; |
3601
|
|
|
|
|
|
|
|
3602
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__ __PACKAGE__); |
3603
|
|
|
|
|
|
|
|
3604
|
|
|
|
|
|
|
package __PACKAGE__; |
3605
|
|
|
|
|
|
|
|
3606
|
|
|
|
|
|
|
my $isa = Basset::Test::Testing::__PACKAGE__::isa_path::Subclass3->isa_path; |
3607
|
|
|
|
|
|
|
$test->ok($isa, "Got isa path"); |
3608
|
|
|
|
|
|
|
|
3609
|
|
|
|
|
|
|
#$test->is(scalar(@$isa), 2, 'two entries in isa_path'); |
3610
|
|
|
|
|
|
|
$test->is($isa->[-2], '__PACKAGE__', 'Second to last entry is parent'); |
3611
|
|
|
|
|
|
|
$test->is($isa->[-1], 'Basset::Test::Testing::__PACKAGE__::isa_path::Subclass3', 'Last entry is self'); |
3612
|
|
|
|
|
|
|
|
3613
|
|
|
|
|
|
|
=end btest(isa_path) |
3614
|
|
|
|
|
|
|
|
3615
|
|
|
|
|
|
|
=cut |
3616
|
|
|
|
|
|
|
|
3617
|
|
|
|
|
|
|
our $paths = {}; |
3618
|
|
|
|
|
|
|
|
3619
|
|
|
|
|
|
|
sub isa_path { |
3620
|
|
|
|
|
|
|
|
3621
|
138
|
50
|
|
138
|
1
|
8676
|
my $class = $_[0]->can('pkg') ? shift->pkg() : shift; |
3622
|
138
|
|
33
|
|
|
653
|
$class = ref $class || $class; |
3623
|
138
|
|
100
|
|
|
19801
|
my $seen = shift || {}; |
3624
|
|
|
|
|
|
|
|
3625
|
138
|
50
|
|
|
|
674
|
return if $seen->{$class}++; |
3626
|
|
|
|
|
|
|
|
3627
|
138
|
100
|
|
|
|
666
|
return $paths->{$class} if defined $paths->{$class}; |
3628
|
|
|
|
|
|
|
|
3629
|
8
|
|
|
8
|
|
53
|
no strict 'refs'; |
|
8
|
|
|
|
|
18
|
|
|
8
|
|
|
|
|
4680
|
|
3630
|
39
|
|
|
|
|
59
|
my @i = @{$class . "::ISA"}; |
|
39
|
|
|
|
|
252
|
|
3631
|
|
|
|
|
|
|
|
3632
|
39
|
|
|
|
|
83
|
my @s = (); |
3633
|
|
|
|
|
|
|
|
3634
|
39
|
|
|
|
|
82
|
foreach my $super (@i){ |
3635
|
|
|
|
|
|
|
|
3636
|
36
|
100
|
|
|
|
135
|
next if $seen->{$super}; |
3637
|
|
|
|
|
|
|
|
3638
|
|
|
|
|
|
|
#the method invocation is more consistent, but bonks on modules that aren't |
3639
|
|
|
|
|
|
|
#subclasses of Basset::Object. So we call it as a function to display all modules |
3640
|
|
|
|
|
|
|
#my $super_isa = $super->can('isa_path') ? $super->isa_path($seen) : []; |
3641
|
|
|
|
|
|
|
|
3642
|
35
|
|
|
|
|
143
|
my $super_isa = isa_path($super, $seen); |
3643
|
35
|
|
|
|
|
148
|
push @s, @$super_isa; |
3644
|
|
|
|
|
|
|
}; |
3645
|
|
|
|
|
|
|
|
3646
|
39
|
|
|
|
|
90
|
push @s, $class; |
3647
|
|
|
|
|
|
|
|
3648
|
39
|
|
|
|
|
104
|
$paths->{$class} = \@s; |
3649
|
|
|
|
|
|
|
|
3650
|
39
|
|
|
|
|
152
|
return \@s; |
3651
|
|
|
|
|
|
|
|
3652
|
|
|
|
|
|
|
}; |
3653
|
|
|
|
|
|
|
|
3654
|
|
|
|
|
|
|
=pod |
3655
|
|
|
|
|
|
|
|
3656
|
|
|
|
|
|
|
=item module_for_class |
3657
|
|
|
|
|
|
|
|
3658
|
|
|
|
|
|
|
Used mainly internally. Converts a perl package name to its file system equivalent. So, |
3659
|
|
|
|
|
|
|
Basset::Object -> Basset/Object.pm and so on. |
3660
|
|
|
|
|
|
|
|
3661
|
|
|
|
|
|
|
=cut |
3662
|
|
|
|
|
|
|
|
3663
|
|
|
|
|
|
|
=pod |
3664
|
|
|
|
|
|
|
|
3665
|
|
|
|
|
|
|
=begin btest(module_for_class) |
3666
|
|
|
|
|
|
|
|
3667
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->module_for_class), undef, "Could not get module_for_class w/o package"); |
3668
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-20", 'proper error code'); |
3669
|
|
|
|
|
|
|
$test->is(__PACKAGE__->module_for_class('Basset::Object'), 'Basset/Object.pm', 'proper pkg -> file name'); |
3670
|
|
|
|
|
|
|
$test->is(__PACKAGE__->module_for_class('Basset::Object::Persistent'), 'Basset/Object/Persistent.pm', 'proper pkg -> file name'); |
3671
|
|
|
|
|
|
|
$test->is(__PACKAGE__->module_for_class('Basset::DB::Table'), 'Basset/DB/Table.pm', 'proper pkg -> file name'); |
3672
|
|
|
|
|
|
|
|
3673
|
|
|
|
|
|
|
=end btest(module_for_class) |
3674
|
|
|
|
|
|
|
|
3675
|
|
|
|
|
|
|
=cut |
3676
|
|
|
|
|
|
|
|
3677
|
|
|
|
|
|
|
sub module_for_class { |
3678
|
194
|
|
|
194
|
1
|
3459
|
my $self = shift; |
3679
|
194
|
100
|
|
|
|
582
|
my $pkg = shift or return $self->error("Cannot check for included-ness w/o package", "BO-20"); |
3680
|
|
|
|
|
|
|
|
3681
|
193
|
|
|
|
|
936
|
$pkg =~ s!::!/!g; |
3682
|
193
|
|
|
|
|
396
|
$pkg .= '.pm'; |
3683
|
|
|
|
|
|
|
|
3684
|
193
|
|
|
|
|
2108
|
return $pkg; |
3685
|
|
|
|
|
|
|
}; |
3686
|
|
|
|
|
|
|
|
3687
|
|
|
|
|
|
|
=pod |
3688
|
|
|
|
|
|
|
|
3689
|
|
|
|
|
|
|
=item conf |
3690
|
|
|
|
|
|
|
|
3691
|
|
|
|
|
|
|
conf is just a convenience wrapper around read_conf_file. |
3692
|
|
|
|
|
|
|
|
3693
|
|
|
|
|
|
|
$obj->conf === Basset::Object::Conf->read_conf_file; |
3694
|
|
|
|
|
|
|
|
3695
|
|
|
|
|
|
|
=cut |
3696
|
|
|
|
|
|
|
|
3697
|
|
|
|
|
|
|
=pod |
3698
|
|
|
|
|
|
|
|
3699
|
|
|
|
|
|
|
=begin btest(conf) |
3700
|
|
|
|
|
|
|
|
3701
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->conf, "Class accessed conf file"); |
3702
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
3703
|
|
|
|
|
|
|
$test->ok(scalar $o, "Got object"); |
3704
|
|
|
|
|
|
|
$test->ok(scalar $o->conf, "Object accessed conf file"); |
3705
|
|
|
|
|
|
|
|
3706
|
|
|
|
|
|
|
=end btest(conf) |
3707
|
|
|
|
|
|
|
|
3708
|
|
|
|
|
|
|
=cut |
3709
|
|
|
|
|
|
|
|
3710
|
|
|
|
|
|
|
sub conf { |
3711
|
706
|
|
|
706
|
1
|
3416
|
my $self = shift->pkg; |
3712
|
706
|
|
100
|
|
|
2434
|
my $local = shift || 0; |
3713
|
|
|
|
|
|
|
|
3714
|
706
|
50
|
|
|
|
10127
|
my $conf = $self->_conf_class->read_conf_file |
3715
|
|
|
|
|
|
|
or return $self->error($self->_conf_class->errvals); |
3716
|
|
|
|
|
|
|
|
3717
|
706
|
100
|
100
|
|
|
3107
|
if ($local && defined $conf->{$self}) { |
|
|
100
|
|
|
|
|
|
3718
|
118
|
|
|
|
|
354
|
return $conf->{$self}; |
3719
|
|
|
|
|
|
|
} |
3720
|
|
|
|
|
|
|
elsif ($local) { |
3721
|
40
|
|
|
|
|
132
|
return {}; |
3722
|
|
|
|
|
|
|
} |
3723
|
|
|
|
|
|
|
else { |
3724
|
548
|
|
|
|
|
1703
|
return $conf; |
3725
|
|
|
|
|
|
|
} |
3726
|
|
|
|
|
|
|
}; |
3727
|
|
|
|
|
|
|
|
3728
|
|
|
|
|
|
|
=pod |
3729
|
|
|
|
|
|
|
|
3730
|
|
|
|
|
|
|
=item today |
3731
|
|
|
|
|
|
|
|
3732
|
|
|
|
|
|
|
Convenience method. Returns today's date in a YYYY-MM-DD formatted string |
3733
|
|
|
|
|
|
|
|
3734
|
|
|
|
|
|
|
=cut |
3735
|
|
|
|
|
|
|
|
3736
|
|
|
|
|
|
|
=pod |
3737
|
|
|
|
|
|
|
|
3738
|
|
|
|
|
|
|
=begin btest(today) |
3739
|
|
|
|
|
|
|
|
3740
|
|
|
|
|
|
|
$test->like(__PACKAGE__->today, qr/^\d\d\d\d-\d\d-\d\d$/, 'matches date regex'); |
3741
|
|
|
|
|
|
|
$test->like(__PACKAGE__->today('abc'), qr/^\d\d\d\d-\d\d-\d\d$/, 'matches date regex despite input'); |
3742
|
|
|
|
|
|
|
|
3743
|
|
|
|
|
|
|
=end btest(today) |
3744
|
|
|
|
|
|
|
|
3745
|
|
|
|
|
|
|
=cut |
3746
|
|
|
|
|
|
|
|
3747
|
|
|
|
|
|
|
sub today { |
3748
|
2
|
|
|
2
|
1
|
774
|
my @today = localtime; |
3749
|
2
|
|
|
|
|
34
|
sprintf("%04d-%02d-%02d", $today[5] + 1900, $today[4] + 1, $today[3]); |
3750
|
|
|
|
|
|
|
} |
3751
|
|
|
|
|
|
|
|
3752
|
|
|
|
|
|
|
=pod |
3753
|
|
|
|
|
|
|
|
3754
|
|
|
|
|
|
|
=item now |
3755
|
|
|
|
|
|
|
|
3756
|
|
|
|
|
|
|
Convenience method. Returns a timestamp in a YYYY-MM-DD HH:MM:SS formatted string |
3757
|
|
|
|
|
|
|
|
3758
|
|
|
|
|
|
|
=cut |
3759
|
|
|
|
|
|
|
|
3760
|
|
|
|
|
|
|
=pod |
3761
|
|
|
|
|
|
|
|
3762
|
|
|
|
|
|
|
=begin btest(now) |
3763
|
|
|
|
|
|
|
|
3764
|
|
|
|
|
|
|
$test->like(__PACKAGE__->now, qr/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$/, 'matches timestamp regex'); |
3765
|
|
|
|
|
|
|
$test->like(__PACKAGE__->now('def'), qr/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$/, 'matches timestamp regex despite input'); |
3766
|
|
|
|
|
|
|
|
3767
|
|
|
|
|
|
|
=end btest(now) |
3768
|
|
|
|
|
|
|
|
3769
|
|
|
|
|
|
|
=cut |
3770
|
|
|
|
|
|
|
|
3771
|
|
|
|
|
|
|
sub now { |
3772
|
2
|
|
|
2
|
1
|
594
|
my @today = localtime; |
3773
|
2
|
|
|
|
|
28
|
sprintf("%04d-%02d-%02d %02d:%02d:%02d", $today[5] + 1900, $today[4] + 1, $today[3], @today[2,1,0]); |
3774
|
|
|
|
|
|
|
} |
3775
|
|
|
|
|
|
|
|
3776
|
|
|
|
|
|
|
=pod |
3777
|
|
|
|
|
|
|
|
3778
|
|
|
|
|
|
|
=item gen_handle |
3779
|
|
|
|
|
|
|
|
3780
|
|
|
|
|
|
|
returns a filehandle in a different package. Useful for when you need to open filehandles and pass 'em around. |
3781
|
|
|
|
|
|
|
|
3782
|
|
|
|
|
|
|
my $handle = Basset::Object->gen_handle(); |
3783
|
|
|
|
|
|
|
open ($handle, "/path/to/my/list"); |
3784
|
|
|
|
|
|
|
|
3785
|
|
|
|
|
|
|
All but identical to gensym in Symbol by this point. |
3786
|
|
|
|
|
|
|
|
3787
|
|
|
|
|
|
|
=cut |
3788
|
|
|
|
|
|
|
|
3789
|
|
|
|
|
|
|
=pod |
3790
|
|
|
|
|
|
|
|
3791
|
|
|
|
|
|
|
=begin btest(gen_handle) |
3792
|
|
|
|
|
|
|
|
3793
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->gen_handle, "Generated handle"); |
3794
|
|
|
|
|
|
|
my $h = __PACKAGE__->gen_handle; |
3795
|
|
|
|
|
|
|
$test->ok($h, "Generated second handle"); |
3796
|
|
|
|
|
|
|
$test->is(ref $h, "GLOB", "And it's a globref"); |
3797
|
|
|
|
|
|
|
|
3798
|
|
|
|
|
|
|
=end btest(gen_handle) |
3799
|
|
|
|
|
|
|
|
3800
|
|
|
|
|
|
|
=cut |
3801
|
|
|
|
|
|
|
|
3802
|
|
|
|
|
|
|
our $handle = 0; |
3803
|
|
|
|
|
|
|
|
3804
|
|
|
|
|
|
|
sub gen_handle { |
3805
|
8
|
|
|
8
|
|
53
|
no strict 'refs'; |
|
8
|
|
|
|
|
19
|
|
|
8
|
|
|
|
|
20130
|
|
3806
|
22
|
|
|
22
|
1
|
629
|
my $self = shift; |
3807
|
22
|
|
|
|
|
74
|
my $name = "HANDLE" . $handle++; |
3808
|
|
|
|
|
|
|
|
3809
|
22
|
|
|
|
|
36
|
my $h = \*{"Basset::Object::Handle::" . $name}; #You'll note that I don't want my |
|
22
|
|
|
|
|
146
|
|
3810
|
|
|
|
|
|
|
#namespace polluted either |
3811
|
22
|
|
|
|
|
66
|
delete $Basset::Object::Handle::{$name}; |
3812
|
22
|
|
|
|
|
71
|
return $h; |
3813
|
|
|
|
|
|
|
}; |
3814
|
|
|
|
|
|
|
|
3815
|
|
|
|
|
|
|
=pod |
3816
|
|
|
|
|
|
|
|
3817
|
|
|
|
|
|
|
=item perform |
3818
|
|
|
|
|
|
|
|
3819
|
|
|
|
|
|
|
if I were writing this in objective-C, I'd call it performSelectors:withObjects: Ho hum. I've really grown fond of the objective-C |
3820
|
|
|
|
|
|
|
syntax. Anyway, since I can't do that, it's just called perform. |
3821
|
|
|
|
|
|
|
|
3822
|
|
|
|
|
|
|
$object->perform( |
3823
|
|
|
|
|
|
|
'methods' => [qw(name password address)], |
3824
|
|
|
|
|
|
|
'values' => ['Jim', 'password', 'Chew St'] |
3825
|
|
|
|
|
|
|
) || die $object->errstring; |
3826
|
|
|
|
|
|
|
|
3827
|
|
|
|
|
|
|
Given a list of methods and values, it calls each method in turn with each value passed. If anything fails, it an error and stops |
3828
|
|
|
|
|
|
|
proceeding through the list. |
3829
|
|
|
|
|
|
|
|
3830
|
|
|
|
|
|
|
Optionally, you may pass in a dereference hash to dereference an arrayref or hashref. |
3831
|
|
|
|
|
|
|
|
3832
|
|
|
|
|
|
|
$object->perform( |
3833
|
|
|
|
|
|
|
'methods' => [qw(name password address permission)], |
3834
|
|
|
|
|
|
|
'values' => ['Jim', 'password', 'Chew St', ['PT07', 'AB']], |
3835
|
|
|
|
|
|
|
'dereference' => [qw(permission)], |
3836
|
|
|
|
|
|
|
) || die $object->errstring; |
3837
|
|
|
|
|
|
|
|
3838
|
|
|
|
|
|
|
With the dereference value, it calls |
3839
|
|
|
|
|
|
|
|
3840
|
|
|
|
|
|
|
$object->permission('PT07', 'AB'); |
3841
|
|
|
|
|
|
|
|
3842
|
|
|
|
|
|
|
Without the dereference value, it calls |
3843
|
|
|
|
|
|
|
|
3844
|
|
|
|
|
|
|
$object->permission(['PT07', 'AB']); |
3845
|
|
|
|
|
|
|
|
3846
|
|
|
|
|
|
|
This can (obviously) even be called with a single method. This is preferrable to just calling $obj->$method(@args) in the code |
3847
|
|
|
|
|
|
|
if $method is not guaranteed to be callable since perform automatically does a 'can' check on the method for you. |
3848
|
|
|
|
|
|
|
|
3849
|
|
|
|
|
|
|
Optionally, you may also pass in a continue parameter. |
3850
|
|
|
|
|
|
|
|
3851
|
|
|
|
|
|
|
$object->perform( |
3852
|
|
|
|
|
|
|
'methods' => [qw(name password address permission)], |
3853
|
|
|
|
|
|
|
'values' => ['Jim', 'password', 'Chew St', ['PT07', 'AB']], |
3854
|
|
|
|
|
|
|
'dereference' => [qw(permission)], |
3855
|
|
|
|
|
|
|
'continue' => 1 |
3856
|
|
|
|
|
|
|
) || die $object->errstring; |
3857
|
|
|
|
|
|
|
|
3858
|
|
|
|
|
|
|
continue should be used with great caution. continue will cause execution to continue even if an error occurs. At the end, you'll |
3859
|
|
|
|
|
|
|
still get an undef back, and your error message will be a list of \n delimited error messages, your error code will be a list of \n |
3860
|
|
|
|
|
|
|
delimited error codes. This is appropriate if you want to set multiple attributes at once (or other methods that are indpendent of each |
3861
|
|
|
|
|
|
|
other) and want to report all errors en masse at the end. |
3862
|
|
|
|
|
|
|
|
3863
|
|
|
|
|
|
|
=cut |
3864
|
|
|
|
|
|
|
|
3865
|
|
|
|
|
|
|
=pod |
3866
|
|
|
|
|
|
|
|
3867
|
|
|
|
|
|
|
=begin btest(perform) |
3868
|
|
|
|
|
|
|
|
3869
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::perform::Subclass; |
3870
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
3871
|
|
|
|
|
|
|
|
3872
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::perform::Subclass->add_attr('attr1'); |
3873
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::perform::Subclass->add_attr('attr2'); |
3874
|
|
|
|
|
|
|
Basset::Test::Testing::__PACKAGE__::perform::Subclass->add_attr('attr3'); |
3875
|
|
|
|
|
|
|
|
3876
|
|
|
|
|
|
|
sub method1 { |
3877
|
|
|
|
|
|
|
return 77; |
3878
|
|
|
|
|
|
|
} |
3879
|
|
|
|
|
|
|
|
3880
|
|
|
|
|
|
|
sub method2 { |
3881
|
|
|
|
|
|
|
my $self = shift; |
3882
|
|
|
|
|
|
|
return scalar @_; |
3883
|
|
|
|
|
|
|
}; |
3884
|
|
|
|
|
|
|
|
3885
|
|
|
|
|
|
|
package __PACKAGE__; |
3886
|
|
|
|
|
|
|
|
3887
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::perform::Subclass->isa('__PACKAGE__'), 'we have a subclass'); |
3888
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::perform::Subclass->can('attr1'), 'subclass has attr1'); |
3889
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::perform::Subclass->can('attr2'), 'subclass has attr2'); |
3890
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::perform::Subclass->can('attr2'), 'subclass has attr3'); |
3891
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::perform::Subclass->can('method1'), 'subclass has method1'); |
3892
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::perform::Subclass->can('method2'), 'subclass has method2'); |
3893
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::perform::Subclass->method1, 77, 'method1 returns 77'); |
3894
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::perform::Subclass->method2, 0, 'method2 behaves as expected'); |
3895
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::perform::Subclass->method2('a'), 1, 'method2 behaves as expected'); |
3896
|
|
|
|
|
|
|
$test->is(scalar Basset::Test::Testing::__PACKAGE__::perform::Subclass->method2(0,0), 2, 'method2 behaves as expected'); |
3897
|
|
|
|
|
|
|
|
3898
|
|
|
|
|
|
|
my $o = Basset::Test::Testing::__PACKAGE__::perform::Subclass->new(); |
3899
|
|
|
|
|
|
|
|
3900
|
|
|
|
|
|
|
$test->ok($o, "Instantiated object"); |
3901
|
|
|
|
|
|
|
|
3902
|
|
|
|
|
|
|
my $class = 'Basset::Test::Testing::__PACKAGE__::perform::Subclass'; |
3903
|
|
|
|
|
|
|
|
3904
|
|
|
|
|
|
|
$test->is(scalar($class->perform), undef, "Cannot perform w/o method"); |
3905
|
|
|
|
|
|
|
$test->is($class->errcode, 'BO-04', 'proper error code'); |
3906
|
|
|
|
|
|
|
$test->is(scalar($class->perform('methods' => 'able')), undef, "Cannot perform w/o values"); |
3907
|
|
|
|
|
|
|
$test->is($class->errcode, 'BO-05', 'proper error code'); |
3908
|
|
|
|
|
|
|
$test->is(scalar($class->perform('methods' => 'able', 'values' => 'baker')), undef, "methods must be arrayref"); |
3909
|
|
|
|
|
|
|
$test->is($class->errcode, 'BO-11', 'proper error code'); |
3910
|
|
|
|
|
|
|
$test->is(scalar($class->perform('methods' => ['able'], 'values' => 'baker')), undef, "values must be arrayref"); |
3911
|
|
|
|
|
|
|
$test->is($class->errcode, 'BO-12', 'proper error code'); |
3912
|
|
|
|
|
|
|
|
3913
|
|
|
|
|
|
|
$test->ok( |
3914
|
|
|
|
|
|
|
scalar Basset::Test::Testing::__PACKAGE__::perform::Subclass->perform( |
3915
|
|
|
|
|
|
|
'methods' => ['method1'], |
3916
|
|
|
|
|
|
|
'values' => ['a'], |
3917
|
|
|
|
|
|
|
), |
3918
|
|
|
|
|
|
|
"Class performs method1"); |
3919
|
|
|
|
|
|
|
|
3920
|
|
|
|
|
|
|
$test->ok( |
3921
|
|
|
|
|
|
|
scalar $o->perform( |
3922
|
|
|
|
|
|
|
'methods' => ['method1'], |
3923
|
|
|
|
|
|
|
'values' => ['a'], |
3924
|
|
|
|
|
|
|
), |
3925
|
|
|
|
|
|
|
"Object performs method1"); |
3926
|
|
|
|
|
|
|
|
3927
|
|
|
|
|
|
|
$test->ok(! |
3928
|
|
|
|
|
|
|
scalar Basset::Test::Testing::__PACKAGE__::perform::Subclass->perform( |
3929
|
|
|
|
|
|
|
'methods' => ['method2'], |
3930
|
|
|
|
|
|
|
'values' => [], |
3931
|
|
|
|
|
|
|
), |
3932
|
|
|
|
|
|
|
"Class cannot perform method2 w/o args"); |
3933
|
|
|
|
|
|
|
|
3934
|
|
|
|
|
|
|
$test->ok( |
3935
|
|
|
|
|
|
|
scalar Basset::Test::Testing::__PACKAGE__::perform::Subclass->perform( |
3936
|
|
|
|
|
|
|
'methods' => ['method2'], |
3937
|
|
|
|
|
|
|
'values' => ['a'] |
3938
|
|
|
|
|
|
|
), |
3939
|
|
|
|
|
|
|
"Class performs method2 w/1 arg"); |
3940
|
|
|
|
|
|
|
|
3941
|
|
|
|
|
|
|
$test->ok( |
3942
|
|
|
|
|
|
|
scalar Basset::Test::Testing::__PACKAGE__::perform::Subclass->perform( |
3943
|
|
|
|
|
|
|
'methods' => ['method2'], |
3944
|
|
|
|
|
|
|
'values' => ['b'], |
3945
|
|
|
|
|
|
|
), |
3946
|
|
|
|
|
|
|
"Class performs method2 w/1 arg in arrayref"); |
3947
|
|
|
|
|
|
|
|
3948
|
|
|
|
|
|
|
$test->ok(! |
3949
|
|
|
|
|
|
|
scalar $o->perform( |
3950
|
|
|
|
|
|
|
'methods' => ['attr1'], |
3951
|
|
|
|
|
|
|
'values' => [] |
3952
|
|
|
|
|
|
|
), |
3953
|
|
|
|
|
|
|
"object cannot access attribute w/o args" |
3954
|
|
|
|
|
|
|
); |
3955
|
|
|
|
|
|
|
|
3956
|
|
|
|
|
|
|
$test->is(scalar $o->attr1, undef, 'attr1 is undefined'); |
3957
|
|
|
|
|
|
|
$test->is(scalar $o->attr2, undef, 'attr2 is undefined'); |
3958
|
|
|
|
|
|
|
$test->is(scalar $o->attr3, undef, 'attr3 is undefined'); |
3959
|
|
|
|
|
|
|
|
3960
|
|
|
|
|
|
|
$test->ok( |
3961
|
|
|
|
|
|
|
scalar $o->perform( |
3962
|
|
|
|
|
|
|
'methods' => ['attr1'], |
3963
|
|
|
|
|
|
|
'values' => ['attr1_val'] |
3964
|
|
|
|
|
|
|
), |
3965
|
|
|
|
|
|
|
"object performed attr1" |
3966
|
|
|
|
|
|
|
); |
3967
|
|
|
|
|
|
|
|
3968
|
|
|
|
|
|
|
$test->is(scalar $o->attr1(), 'attr1_val', 'attr1 set via perform'); |
3969
|
|
|
|
|
|
|
|
3970
|
|
|
|
|
|
|
$test->ok( |
3971
|
|
|
|
|
|
|
scalar $o->perform( |
3972
|
|
|
|
|
|
|
'methods' => ['attr2', 'attr3'], |
3973
|
|
|
|
|
|
|
'values' => ['attr2_val', 'attr3_val'] |
3974
|
|
|
|
|
|
|
), |
3975
|
|
|
|
|
|
|
"object performed attr2, attr3" |
3976
|
|
|
|
|
|
|
); |
3977
|
|
|
|
|
|
|
|
3978
|
|
|
|
|
|
|
$test->is(scalar $o->attr2(), 'attr2_val', 'attr2 set via perform'); |
3979
|
|
|
|
|
|
|
$test->is(scalar $o->attr3(), 'attr3_val', 'attr3 set via perform'); |
3980
|
|
|
|
|
|
|
|
3981
|
|
|
|
|
|
|
$test->ok(! |
3982
|
|
|
|
|
|
|
scalar $o->perform( |
3983
|
|
|
|
|
|
|
'methods' => ['attr4'], |
3984
|
|
|
|
|
|
|
'values' => ['attr4_val'] |
3985
|
|
|
|
|
|
|
), |
3986
|
|
|
|
|
|
|
"object cannot perform unknown method" |
3987
|
|
|
|
|
|
|
); |
3988
|
|
|
|
|
|
|
|
3989
|
|
|
|
|
|
|
$test->ok(! |
3990
|
|
|
|
|
|
|
scalar $o->perform( |
3991
|
|
|
|
|
|
|
'methods' => ['attr4', 'attr2'], |
3992
|
|
|
|
|
|
|
'values' => ['attr4_val', 'attr2_val_2'], |
3993
|
|
|
|
|
|
|
), |
3994
|
|
|
|
|
|
|
'object cannot perform unknown method w/known method' |
3995
|
|
|
|
|
|
|
); |
3996
|
|
|
|
|
|
|
|
3997
|
|
|
|
|
|
|
$test->is(scalar $o->attr2, 'attr2_val', 'attr2 unchanged'); |
3998
|
|
|
|
|
|
|
|
3999
|
|
|
|
|
|
|
$test->ok(! |
4000
|
|
|
|
|
|
|
scalar $o->perform( |
4001
|
|
|
|
|
|
|
'methods' => ['attr1'], |
4002
|
|
|
|
|
|
|
'values' => [undef] |
4003
|
|
|
|
|
|
|
), |
4004
|
|
|
|
|
|
|
"object failed trying to perform attr1" |
4005
|
|
|
|
|
|
|
); |
4006
|
|
|
|
|
|
|
|
4007
|
|
|
|
|
|
|
$test->ok(! |
4008
|
|
|
|
|
|
|
scalar $o->perform( |
4009
|
|
|
|
|
|
|
'methods' => ['attr1', 'attr2'], |
4010
|
|
|
|
|
|
|
'values' => [undef, 'attr2_val_2'], |
4011
|
|
|
|
|
|
|
), |
4012
|
|
|
|
|
|
|
'object failed trying to perform attr1' |
4013
|
|
|
|
|
|
|
); |
4014
|
|
|
|
|
|
|
|
4015
|
|
|
|
|
|
|
$test->is(scalar $o->attr2, 'attr2_val', 'attr2 unchanged'); |
4016
|
|
|
|
|
|
|
|
4017
|
|
|
|
|
|
|
$test->ok(! |
4018
|
|
|
|
|
|
|
scalar $o->perform( |
4019
|
|
|
|
|
|
|
'methods' => ['attr1', 'attr2'], |
4020
|
|
|
|
|
|
|
'values' => [undef, 'attr2_val_2'], |
4021
|
|
|
|
|
|
|
'continue' => 1, |
4022
|
|
|
|
|
|
|
), |
4023
|
|
|
|
|
|
|
'object failed trying to perform attr1' |
4024
|
|
|
|
|
|
|
); |
4025
|
|
|
|
|
|
|
|
4026
|
|
|
|
|
|
|
$test->is(scalar $o->attr2, 'attr2_val_2', 'attr2 changed due to continue'); |
4027
|
|
|
|
|
|
|
|
4028
|
|
|
|
|
|
|
my $arr = ['a', 'b']; |
4029
|
|
|
|
|
|
|
$test->ok($arr, "Have an arrayref"); |
4030
|
|
|
|
|
|
|
|
4031
|
|
|
|
|
|
|
$test->ok( |
4032
|
|
|
|
|
|
|
scalar $o->perform( |
4033
|
|
|
|
|
|
|
'methods' => ['attr3'], |
4034
|
|
|
|
|
|
|
'values' => [$arr], |
4035
|
|
|
|
|
|
|
), |
4036
|
|
|
|
|
|
|
"Performed attr3" |
4037
|
|
|
|
|
|
|
); |
4038
|
|
|
|
|
|
|
|
4039
|
|
|
|
|
|
|
$test->is($o->attr3, $arr, "attr3 contains arrayref"); |
4040
|
|
|
|
|
|
|
|
4041
|
|
|
|
|
|
|
$test->ok( |
4042
|
|
|
|
|
|
|
scalar $o->perform( |
4043
|
|
|
|
|
|
|
'methods' => ['attr3'], |
4044
|
|
|
|
|
|
|
'values' => [$arr], |
4045
|
|
|
|
|
|
|
'dereference' => ['attr3'], |
4046
|
|
|
|
|
|
|
), |
4047
|
|
|
|
|
|
|
"Performed attr3 with de-reference" |
4048
|
|
|
|
|
|
|
); |
4049
|
|
|
|
|
|
|
|
4050
|
|
|
|
|
|
|
$test->is($o->attr3, 'a', "attr3 contains first element of arrayref"); |
4051
|
|
|
|
|
|
|
|
4052
|
|
|
|
|
|
|
$test->ok( |
4053
|
|
|
|
|
|
|
scalar $o->perform( |
4054
|
|
|
|
|
|
|
'methods' => ['attr2', 'attr3'], |
4055
|
|
|
|
|
|
|
'values' => [$arr, $arr], |
4056
|
|
|
|
|
|
|
'dereference' => ['attr2'], |
4057
|
|
|
|
|
|
|
), |
4058
|
|
|
|
|
|
|
"Performed attr3 with de-reference" |
4059
|
|
|
|
|
|
|
); |
4060
|
|
|
|
|
|
|
|
4061
|
|
|
|
|
|
|
$test->is($o->attr2, 'a', "attr2 contains first element of arrayref"); |
4062
|
|
|
|
|
|
|
$test->is($o->attr3, $arr, "attr3 contains arrayref"); |
4063
|
|
|
|
|
|
|
|
4064
|
|
|
|
|
|
|
=end btest(perform) |
4065
|
|
|
|
|
|
|
|
4066
|
|
|
|
|
|
|
=cut |
4067
|
|
|
|
|
|
|
|
4068
|
|
|
|
|
|
|
sub perform { |
4069
|
20
|
|
|
20
|
1
|
562
|
my $self = shift; |
4070
|
|
|
|
|
|
|
|
4071
|
20
|
|
|
|
|
81
|
my %args = @_; |
4072
|
|
|
|
|
|
|
|
4073
|
20
|
100
|
|
|
|
81
|
my $methods = $args{'methods'} or return $self->error("Cannot perform w/o methods", "BO-04"); |
4074
|
19
|
100
|
|
|
|
61
|
my $values = $args{'values'} or return $self->error("Cannot perform w/o values", "BO-05"); |
4075
|
18
|
100
|
|
|
|
28
|
my $deref = {map {$_, 1} @{$args{'dereference'} || []}}; |
|
2
|
|
|
|
|
10
|
|
|
18
|
|
|
|
|
102
|
|
4076
|
18
|
|
100
|
|
|
82
|
my $continue= $args{'continue'} || 0; |
4077
|
|
|
|
|
|
|
|
4078
|
18
|
100
|
|
|
|
61
|
return $self->error("methods must be arrayref", "BO-11") unless ref $methods eq 'ARRAY'; |
4079
|
17
|
100
|
|
|
|
47
|
return $self->error("values must be arrayref", "BO-12") unless ref $values eq 'ARRAY'; |
4080
|
|
|
|
|
|
|
|
4081
|
16
|
100
|
|
|
|
57
|
return $self->error('Cannot perform. Different number of methods and values', 'BO-07') unless @$methods == @$values; |
4082
|
|
|
|
|
|
|
|
4083
|
14
|
|
|
|
|
22
|
my @errors = (); |
4084
|
14
|
|
|
|
|
22
|
my @codes = (); |
4085
|
|
|
|
|
|
|
|
4086
|
|
|
|
|
|
|
#non destructive copies |
4087
|
14
|
|
|
|
|
48
|
($methods, $values) = ([@$methods], [@$values]); |
4088
|
|
|
|
|
|
|
|
4089
|
14
|
|
|
|
|
42
|
while (@$methods) { |
4090
|
17
|
|
|
|
|
27
|
my $method = shift @$methods; |
4091
|
17
|
|
|
|
|
29
|
my $value = shift @$values; |
4092
|
|
|
|
|
|
|
|
4093
|
17
|
|
|
|
|
25
|
my @args = ($value); |
4094
|
|
|
|
|
|
|
|
4095
|
17
|
100
|
100
|
|
|
112
|
if (ref $value eq 'ARRAY' && $deref->{$method}) { |
|
|
50
|
33
|
|
|
|
|
4096
|
2
|
|
|
|
|
8
|
@args = @$value; |
4097
|
|
|
|
|
|
|
} elsif (ref $value eq 'HASH' && $deref->{$method}) { |
4098
|
0
|
|
|
|
|
0
|
@args = %$value; |
4099
|
|
|
|
|
|
|
}; |
4100
|
|
|
|
|
|
|
|
4101
|
17
|
100
|
|
|
|
96
|
if ($self->can($method)) { |
4102
|
15
|
100
|
|
|
|
49
|
unless (defined $self->$method(@args)) { |
4103
|
3
|
100
|
|
|
|
9
|
if ($args{'continue'}) { |
4104
|
1
|
|
|
|
|
3
|
push @errors, $self->error(); |
4105
|
1
|
|
50
|
|
|
5
|
push @codes, $self->errcode || "BO-06"; |
4106
|
|
|
|
|
|
|
} else { |
4107
|
2
|
50
|
|
|
|
7
|
$value = defined $value ? $value : 'value is undefined'; |
4108
|
2
|
|
50
|
|
|
10
|
return $self->error("Could not perform method ($method) with value ($value) : " . $self->error(), $self->errcode || "BO-06"); |
4109
|
|
|
|
|
|
|
} |
4110
|
|
|
|
|
|
|
} |
4111
|
|
|
|
|
|
|
} else { |
4112
|
2
|
|
|
|
|
14
|
return $self->error("Object cannot perform method ($method)", "BO-10"); |
4113
|
|
|
|
|
|
|
}; |
4114
|
|
|
|
|
|
|
}; |
4115
|
|
|
|
|
|
|
|
4116
|
10
|
100
|
|
|
|
54
|
if (@errors) { |
4117
|
1
|
|
|
|
|
6
|
return $self->error(join("\n", @errors), join("\n", @codes)); |
4118
|
|
|
|
|
|
|
} else { |
4119
|
9
|
|
|
|
|
68
|
return 1; |
4120
|
|
|
|
|
|
|
}; |
4121
|
|
|
|
|
|
|
|
4122
|
|
|
|
|
|
|
}; |
4123
|
|
|
|
|
|
|
|
4124
|
|
|
|
|
|
|
=pod |
4125
|
|
|
|
|
|
|
|
4126
|
|
|
|
|
|
|
=item stack_trace |
4127
|
|
|
|
|
|
|
|
4128
|
|
|
|
|
|
|
A method useful for debugging. When called, returns a stack trace. |
4129
|
|
|
|
|
|
|
|
4130
|
|
|
|
|
|
|
sub some_method { |
4131
|
|
|
|
|
|
|
my $self = shift; |
4132
|
|
|
|
|
|
|
#you know something weird happens here. |
4133
|
|
|
|
|
|
|
print STDERR $self->stack_trace(); |
4134
|
|
|
|
|
|
|
}; |
4135
|
|
|
|
|
|
|
|
4136
|
|
|
|
|
|
|
=cut |
4137
|
|
|
|
|
|
|
|
4138
|
|
|
|
|
|
|
=pod |
4139
|
|
|
|
|
|
|
|
4140
|
|
|
|
|
|
|
=begin btest(stack_trace) |
4141
|
|
|
|
|
|
|
|
4142
|
|
|
|
|
|
|
sub tracer { |
4143
|
|
|
|
|
|
|
return __PACKAGE__->stack_trace; |
4144
|
|
|
|
|
|
|
}; |
4145
|
|
|
|
|
|
|
|
4146
|
|
|
|
|
|
|
$test->ok(tracer(), "Got a stack trace"); |
4147
|
|
|
|
|
|
|
my $trace = tracer(); |
4148
|
|
|
|
|
|
|
$test->ok($trace, "Has a stack trace"); |
4149
|
|
|
|
|
|
|
$test->like($trace, qr/Package:/, "Contains word: 'Package:'"); |
4150
|
|
|
|
|
|
|
$test->like($trace, qr/Filename:/, "Contains word: 'Filename:'"); |
4151
|
|
|
|
|
|
|
$test->like($trace, qr/Line number:/, "Contains word: 'Line number:'"); |
4152
|
|
|
|
|
|
|
$test->like($trace, qr/Subroutine:/, "Contains word: 'Subroutine:'"); |
4153
|
|
|
|
|
|
|
$test->like($trace, qr/Has Args\? :/, "Contains word: 'Has Args:'"); |
4154
|
|
|
|
|
|
|
$test->like($trace, qr/Want array\? :/, "Contains word: 'Want array:'"); |
4155
|
|
|
|
|
|
|
$test->like($trace, qr/Evaltext:/, "Contains word: 'Evaltext:'"); |
4156
|
|
|
|
|
|
|
$test->like($trace, qr/Is require\? :/, "Contains word: 'Is require:'"); |
4157
|
|
|
|
|
|
|
|
4158
|
|
|
|
|
|
|
=end btest(stack_trace) |
4159
|
|
|
|
|
|
|
|
4160
|
|
|
|
|
|
|
=cut |
4161
|
|
|
|
|
|
|
|
4162
|
|
|
|
|
|
|
sub stack_trace { |
4163
|
2
|
|
|
2
|
1
|
375
|
my $caller_count = 1; |
4164
|
2
|
|
|
|
|
3
|
my $caller_stack = undef; |
4165
|
2
|
|
|
|
|
8
|
my @verbose_caller = ("Package: ", "Filename: ", "Line number: ", "Subroutine: ", "Has Args? : ", |
4166
|
|
|
|
|
|
|
"Want array? : ", "Evaltext: ", "Is require? : "); |
4167
|
|
|
|
|
|
|
|
4168
|
2
|
50
|
|
|
|
10
|
push @verbose_caller, ("Hints: ", "Bitmask: ") if $] >= 5.006; #5.6 has a more verbose caller stack. |
4169
|
|
|
|
|
|
|
|
4170
|
2
|
|
|
|
|
17
|
while (my @caller = caller($caller_count++)){ |
4171
|
2
|
|
|
|
|
4
|
$caller_stack .= "\t---------\n"; |
4172
|
2
|
|
|
|
|
8
|
foreach (0..$#caller){ |
4173
|
22
|
100
|
|
|
|
37
|
my $callvalue = defined $caller[$_] ? $caller[$_] : ''; |
4174
|
22
|
|
|
|
|
100
|
$caller_stack .= "\t\t$verbose_caller[$_]$callvalue\n";# if $caller[$_]; |
4175
|
|
|
|
|
|
|
}; |
4176
|
|
|
|
|
|
|
}; |
4177
|
|
|
|
|
|
|
|
4178
|
2
|
|
|
|
|
4
|
$caller_stack .= "\t---------\n"; |
4179
|
2
|
|
|
|
|
12
|
return $caller_stack; |
4180
|
|
|
|
|
|
|
}; |
4181
|
|
|
|
|
|
|
|
4182
|
|
|
|
|
|
|
=pod |
4183
|
|
|
|
|
|
|
|
4184
|
|
|
|
|
|
|
=item no_op |
4185
|
|
|
|
|
|
|
|
4186
|
|
|
|
|
|
|
no_op is a simple little method that just always returns 1, no matter what. Useful for cases where |
4187
|
|
|
|
|
|
|
you want to be able to call a method and have it succeed, such as a generic place holder. |
4188
|
|
|
|
|
|
|
|
4189
|
|
|
|
|
|
|
=cut |
4190
|
|
|
|
|
|
|
|
4191
|
|
|
|
|
|
|
=pod |
4192
|
|
|
|
|
|
|
|
4193
|
|
|
|
|
|
|
=begin btest(no_op) |
4194
|
|
|
|
|
|
|
|
4195
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->no_op, "No op"); |
4196
|
|
|
|
|
|
|
$test->is(__PACKAGE__->no_op, 1, "No op is 1"); |
4197
|
|
|
|
|
|
|
my $obj = __PACKAGE__->new(); |
4198
|
|
|
|
|
|
|
$test->ok($obj, "Got object"); |
4199
|
|
|
|
|
|
|
$test->ok($obj->no_op, "Object no ops"); |
4200
|
|
|
|
|
|
|
$test->is($obj->no_op, 1, "Object no op is 1"); |
4201
|
|
|
|
|
|
|
|
4202
|
|
|
|
|
|
|
=end btest(no_op) |
4203
|
|
|
|
|
|
|
|
4204
|
|
|
|
|
|
|
=cut |
4205
|
|
|
|
|
|
|
|
4206
|
32
|
|
|
32
|
1
|
14395
|
sub no_op { return 1 }; |
4207
|
|
|
|
|
|
|
|
4208
|
|
|
|
|
|
|
=pod |
4209
|
|
|
|
|
|
|
|
4210
|
|
|
|
|
|
|
=item system_prefix |
4211
|
|
|
|
|
|
|
|
4212
|
|
|
|
|
|
|
Returns the prefix used by the system for internal methods as generated by add_attr and the like. |
4213
|
|
|
|
|
|
|
|
4214
|
|
|
|
|
|
|
=cut |
4215
|
|
|
|
|
|
|
|
4216
|
|
|
|
|
|
|
=pod |
4217
|
|
|
|
|
|
|
|
4218
|
|
|
|
|
|
|
=begin btest(system_prefix) |
4219
|
|
|
|
|
|
|
|
4220
|
|
|
|
|
|
|
$test->is(__PACKAGE__->system_prefix(), '__b_', 'expected system prefix'); |
4221
|
|
|
|
|
|
|
|
4222
|
|
|
|
|
|
|
=end btest(system_prefix) |
4223
|
|
|
|
|
|
|
|
4224
|
|
|
|
|
|
|
=cut |
4225
|
|
|
|
|
|
|
|
4226
|
160
|
|
|
160
|
1
|
1364
|
sub system_prefix { return '__b_'}; |
4227
|
|
|
|
|
|
|
|
4228
|
|
|
|
|
|
|
=pod |
4229
|
|
|
|
|
|
|
|
4230
|
|
|
|
|
|
|
=item privatize |
4231
|
|
|
|
|
|
|
|
4232
|
|
|
|
|
|
|
Returns a method prepended with the system prefix, useful for making private methods. |
4233
|
|
|
|
|
|
|
|
4234
|
|
|
|
|
|
|
Some::Class->privatize('foo'); #returns Some::Class->system_prefix . 'foo'; |
4235
|
|
|
|
|
|
|
|
4236
|
|
|
|
|
|
|
=cut |
4237
|
|
|
|
|
|
|
|
4238
|
|
|
|
|
|
|
sub privatize { |
4239
|
56
|
|
|
56
|
1
|
882
|
my $class = shift; |
4240
|
56
|
100
|
|
|
|
145
|
my $method = shift or return $class->error("Cannot privatize w/o method", "BO-24"); |
4241
|
|
|
|
|
|
|
|
4242
|
55
|
|
|
|
|
177
|
my $prefix = $class->system_prefix; |
4243
|
55
|
100
|
|
|
|
208
|
return index($method, $prefix) >= 0 |
4244
|
|
|
|
|
|
|
? $method |
4245
|
|
|
|
|
|
|
: $class->system_prefix . $method; |
4246
|
|
|
|
|
|
|
} |
4247
|
|
|
|
|
|
|
|
4248
|
|
|
|
|
|
|
=pod |
4249
|
|
|
|
|
|
|
|
4250
|
|
|
|
|
|
|
=begin btest(privatize) |
4251
|
|
|
|
|
|
|
|
4252
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->privatize, 'Cannot privatize w/o method'); |
4253
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-24", "proper error code"); |
4254
|
|
|
|
|
|
|
|
4255
|
|
|
|
|
|
|
$test->is(__PACKAGE__->privatize('foo'), '__b_foo', "privatized foo"); |
4256
|
|
|
|
|
|
|
$test->is(__PACKAGE__->privatize('__b_foo'), '__b_foo', "__b_foo remains __b_foo"); |
4257
|
|
|
|
|
|
|
|
4258
|
|
|
|
|
|
|
=end btest(privatize) |
4259
|
|
|
|
|
|
|
|
4260
|
|
|
|
|
|
|
=cut |
4261
|
|
|
|
|
|
|
|
4262
|
|
|
|
|
|
|
=pod |
4263
|
|
|
|
|
|
|
|
4264
|
|
|
|
|
|
|
=item deprivatize |
4265
|
|
|
|
|
|
|
|
4266
|
|
|
|
|
|
|
Returns a method with the system prefix removed, useful for unmaking private methods. |
4267
|
|
|
|
|
|
|
|
4268
|
|
|
|
|
|
|
Some::Class->deprivatize('__b_foo'); #returns 'foo'; |
4269
|
|
|
|
|
|
|
|
4270
|
|
|
|
|
|
|
=cut |
4271
|
|
|
|
|
|
|
|
4272
|
|
|
|
|
|
|
sub deprivatize { |
4273
|
3
|
|
|
3
|
1
|
948
|
my $class = shift; |
4274
|
3
|
100
|
|
|
|
17
|
my $method = shift or return $class->error("Cannot deprivatize w/o method", "BO-25"); |
4275
|
|
|
|
|
|
|
|
4276
|
2
|
|
|
|
|
12
|
my $prefix = $class->system_prefix; |
4277
|
|
|
|
|
|
|
|
4278
|
2
|
100
|
|
|
|
10
|
if (index($method, $prefix) == 0) { |
4279
|
1
|
|
|
|
|
4
|
$method = substr($method, length $prefix); |
4280
|
|
|
|
|
|
|
} |
4281
|
|
|
|
|
|
|
|
4282
|
2
|
|
|
|
|
9
|
return $method; |
4283
|
|
|
|
|
|
|
} |
4284
|
|
|
|
|
|
|
|
4285
|
|
|
|
|
|
|
=pod |
4286
|
|
|
|
|
|
|
|
4287
|
|
|
|
|
|
|
=begin btest(deprivatize) |
4288
|
|
|
|
|
|
|
|
4289
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->deprivatize, 'Cannot deprivatize w/o method'); |
4290
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-25", "proper error code"); |
4291
|
|
|
|
|
|
|
|
4292
|
|
|
|
|
|
|
$test->is(__PACKAGE__->deprivatize('foo'), 'foo', "deprivatized foo"); |
4293
|
|
|
|
|
|
|
$test->is(__PACKAGE__->deprivatize('__b_foo'), 'foo', "deprivatized __b_foo"); |
4294
|
|
|
|
|
|
|
|
4295
|
|
|
|
|
|
|
=end btest(deprivatize) |
4296
|
|
|
|
|
|
|
|
4297
|
|
|
|
|
|
|
=cut |
4298
|
|
|
|
|
|
|
|
4299
|
|
|
|
|
|
|
=pod |
4300
|
|
|
|
|
|
|
|
4301
|
|
|
|
|
|
|
=item is_private |
4302
|
|
|
|
|
|
|
|
4303
|
|
|
|
|
|
|
Returns a true value if the method is private (starts with system prefix), and false otherwise. |
4304
|
|
|
|
|
|
|
|
4305
|
|
|
|
|
|
|
Some::Class->is_private('__b_foo'); #returns true; |
4306
|
|
|
|
|
|
|
Some::Class->is_private('foo'); #returns false; |
4307
|
|
|
|
|
|
|
|
4308
|
|
|
|
|
|
|
=cut |
4309
|
|
|
|
|
|
|
|
4310
|
|
|
|
|
|
|
sub is_private { |
4311
|
3
|
|
|
3
|
1
|
646
|
my $class = shift; |
4312
|
3
|
100
|
|
|
|
14
|
my $method = shift or return $class->error("Cannot determine is_private w/o method", "BO-26"); |
4313
|
|
|
|
|
|
|
|
4314
|
2
|
|
|
|
|
9
|
return index($method, $class->system_prefix) == 0; |
4315
|
|
|
|
|
|
|
} |
4316
|
|
|
|
|
|
|
|
4317
|
|
|
|
|
|
|
=pod |
4318
|
|
|
|
|
|
|
|
4319
|
|
|
|
|
|
|
=begin btest(deprivatize) |
4320
|
|
|
|
|
|
|
|
4321
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->is_private, 'Cannot is_private w/o method'); |
4322
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-26", "proper error code"); |
4323
|
|
|
|
|
|
|
|
4324
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->is_private('foo'), 'foo is not private'); |
4325
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->is_private('__b_foo'), '__b_foo is private'); |
4326
|
|
|
|
|
|
|
|
4327
|
|
|
|
|
|
|
=end btest(deprivatize) |
4328
|
|
|
|
|
|
|
|
4329
|
|
|
|
|
|
|
=cut |
4330
|
|
|
|
|
|
|
|
4331
|
|
|
|
|
|
|
=pod |
4332
|
|
|
|
|
|
|
|
4333
|
|
|
|
|
|
|
=item cast |
4334
|
|
|
|
|
|
|
|
4335
|
|
|
|
|
|
|
Returns the object casted to the given class. |
4336
|
|
|
|
|
|
|
|
4337
|
|
|
|
|
|
|
my $object = Some::Class->new(); |
4338
|
|
|
|
|
|
|
my $casted = $object->cast('Some::Class::Subclass'); |
4339
|
|
|
|
|
|
|
|
4340
|
|
|
|
|
|
|
If passed a second true argument, returns a copy of the object casted. |
4341
|
|
|
|
|
|
|
|
4342
|
|
|
|
|
|
|
my $object = Some::Class->new(); |
4343
|
|
|
|
|
|
|
my $castedCopy = $object->cast('Some::Class::Subclass', 'copy'); |
4344
|
|
|
|
|
|
|
|
4345
|
|
|
|
|
|
|
=cut |
4346
|
|
|
|
|
|
|
|
4347
|
|
|
|
|
|
|
sub cast { |
4348
|
5
|
|
|
5
|
1
|
2434
|
my $self = shift; |
4349
|
|
|
|
|
|
|
|
4350
|
5
|
100
|
|
|
|
32
|
return $self->error("Can only cast objects", "BO-21") unless ref $self; |
4351
|
|
|
|
|
|
|
|
4352
|
4
|
100
|
|
|
|
25
|
my $class = shift or return $self->error("Cannot cast w/o class", "BO-22"); |
4353
|
2
|
|
50
|
|
|
9
|
my $should_copy = shift || 0; |
4354
|
|
|
|
|
|
|
|
4355
|
2
|
|
|
|
|
5
|
my $cast = undef; |
4356
|
|
|
|
|
|
|
|
4357
|
2
|
50
|
|
|
|
8
|
if ($should_copy) { |
4358
|
2
|
50
|
|
|
|
9
|
$cast = $self->copy or return; |
4359
|
|
|
|
|
|
|
} else { |
4360
|
0
|
|
|
|
|
0
|
$cast = $self; |
4361
|
|
|
|
|
|
|
} |
4362
|
|
|
|
|
|
|
|
4363
|
2
|
50
|
|
|
|
15
|
$self->load_pkg($class) or return; |
4364
|
|
|
|
|
|
|
|
4365
|
2
|
|
|
|
|
16
|
return bless $cast, $class; |
4366
|
|
|
|
|
|
|
|
4367
|
|
|
|
|
|
|
} |
4368
|
|
|
|
|
|
|
|
4369
|
|
|
|
|
|
|
=pod |
4370
|
|
|
|
|
|
|
|
4371
|
|
|
|
|
|
|
=begin btest(cast) |
4372
|
|
|
|
|
|
|
|
4373
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::cast::Subclass1; |
4374
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
4375
|
|
|
|
|
|
|
|
4376
|
|
|
|
|
|
|
package __PACKAGE__; |
4377
|
|
|
|
|
|
|
|
4378
|
|
|
|
|
|
|
#pretend it was loaded normally |
4379
|
|
|
|
|
|
|
$INC{__PACKAGE__->module_for_class("Basset::Test::Testing::__PACKAGE__::cast::Subclass1")}++; |
4380
|
|
|
|
|
|
|
|
4381
|
|
|
|
|
|
|
my $subclass = "Basset::Test::Testing::__PACKAGE__::cast::Subclass1"; |
4382
|
|
|
|
|
|
|
|
4383
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->cast, "Cannot cast classes"); |
4384
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-21", "proper error code"); |
4385
|
|
|
|
|
|
|
|
4386
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
4387
|
|
|
|
|
|
|
$test->ok($o, "got object"); |
4388
|
|
|
|
|
|
|
|
4389
|
|
|
|
|
|
|
$test->ok(! $o->cast, "Cannot cast w/o class"); |
4390
|
|
|
|
|
|
|
$test->is($o->errcode, "BO-22", "proper error code"); |
4391
|
|
|
|
|
|
|
my $c = $o->cast($subclass, 'copy'); |
4392
|
|
|
|
|
|
|
$test->ok($c, "casted object"); |
4393
|
|
|
|
|
|
|
$test->is($o->pkg, "__PACKAGE__", "original part of super package"); |
4394
|
|
|
|
|
|
|
$test->is($c->pkg, $subclass, "casted object part of sub package"); |
4395
|
|
|
|
|
|
|
$test->is($c->errcode, $o->errcode, "error codes match, rest is assumed"); |
4396
|
|
|
|
|
|
|
|
4397
|
|
|
|
|
|
|
my $o2 = __PACKAGE__->new(); |
4398
|
|
|
|
|
|
|
$test->ok($o2, "got object"); |
4399
|
|
|
|
|
|
|
|
4400
|
|
|
|
|
|
|
$test->ok(! $o2->cast, "Cannot cast w/o class"); |
4401
|
|
|
|
|
|
|
$test->is($o2->errcode, "BO-22", "proper error code"); |
4402
|
|
|
|
|
|
|
my $c2 = $o2->cast($subclass, 'copy'); |
4403
|
|
|
|
|
|
|
$test->ok($c2, "casted object"); |
4404
|
|
|
|
|
|
|
$test->is($o2->pkg, "__PACKAGE__", "original part of super package"); |
4405
|
|
|
|
|
|
|
$test->is($c2->pkg, $subclass, "casted object part of sub package"); |
4406
|
|
|
|
|
|
|
$test->is($c2->errcode, $o->errcode, "error codes match, rest is assumed"); |
4407
|
|
|
|
|
|
|
|
4408
|
|
|
|
|
|
|
=end btest(cast) |
4409
|
|
|
|
|
|
|
|
4410
|
|
|
|
|
|
|
=cut |
4411
|
|
|
|
|
|
|
|
4412
|
|
|
|
|
|
|
#used for introspection. |
4413
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('_class_attributes', {}); |
4414
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('_instance_attributes', {}); |
4415
|
|
|
|
|
|
|
|
4416
|
|
|
|
|
|
|
# _obj_error is the object attribute slot for storing the most recent error that occurred. It is |
4417
|
|
|
|
|
|
|
# set via the first argument to the ->error method when called with an object. |
4418
|
|
|
|
|
|
|
# i.e., $obj->error('foo', 'bar'); #_obj_error is 'foo' |
4419
|
|
|
|
|
|
|
__PACKAGE__->add_attr('_obj_error'); |
4420
|
|
|
|
|
|
|
|
4421
|
|
|
|
|
|
|
# _obj_errcode is the object attribute slot for storing the most recent error code that occurred. It is |
4422
|
|
|
|
|
|
|
# set via the second argument to the ->error method when called with an object. |
4423
|
|
|
|
|
|
|
# i.e., $obj->error('foo', 'bar'); #_obj_errcode is 'bar' |
4424
|
|
|
|
|
|
|
__PACKAGE__->add_attr('_obj_errcode'); |
4425
|
|
|
|
|
|
|
|
4426
|
|
|
|
|
|
|
# _pkg_error is the class attribute slot for storing the most recent error that occurred. It is |
4427
|
|
|
|
|
|
|
# set via the first argument to the ->error method when called with a class. |
4428
|
|
|
|
|
|
|
# i.e., $class->error('foo', 'bar'); #_pkg_error is 'foo' |
4429
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('_pkg_error'); |
4430
|
|
|
|
|
|
|
|
4431
|
|
|
|
|
|
|
# _pkg_errcode is the class attribute slot for storing the most recent error code that occurred. It is |
4432
|
|
|
|
|
|
|
# set via the second argument to the ->error method when called with a class. |
4433
|
|
|
|
|
|
|
# i.e., $class->error('foo', 'bar'); #_pkg_errcode is 'bar' |
4434
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('_pkg_errcode'); |
4435
|
|
|
|
|
|
|
|
4436
|
|
|
|
|
|
|
=pod |
4437
|
|
|
|
|
|
|
|
4438
|
|
|
|
|
|
|
=back |
4439
|
|
|
|
|
|
|
|
4440
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
4441
|
|
|
|
|
|
|
|
4442
|
|
|
|
|
|
|
=over |
4443
|
|
|
|
|
|
|
|
4444
|
|
|
|
|
|
|
=item errortranslator |
4445
|
|
|
|
|
|
|
|
4446
|
|
|
|
|
|
|
The errortranslator needs to be set to a hashref, and it translates programmer |
4447
|
|
|
|
|
|
|
readable errors into user readable errors. It's clunky and a mess and a hack, but it works. |
4448
|
|
|
|
|
|
|
|
4449
|
|
|
|
|
|
|
__PACKAGE__->errortranslator( |
4450
|
|
|
|
|
|
|
{ |
4451
|
|
|
|
|
|
|
'violation of key constraint foo: Cannot INSERT' => 'Please specify a value for foo' |
4452
|
|
|
|
|
|
|
} |
4453
|
|
|
|
|
|
|
); |
4454
|
|
|
|
|
|
|
|
4455
|
|
|
|
|
|
|
$obj->do_something || die $obj->error(); # dies 'violation of key constraint foo: Cannot INSERT' |
4456
|
|
|
|
|
|
|
$obj->do_something || die $obj->usererror();# dies 'Please specify a value for foo' |
4457
|
|
|
|
|
|
|
|
4458
|
|
|
|
|
|
|
The error translator looks at the error values, and if a more friendly user error exists, it returns that one instead. |
4459
|
|
|
|
|
|
|
errortranslator looks at and returns (in order): |
4460
|
|
|
|
|
|
|
|
4461
|
|
|
|
|
|
|
the actual error, |
4462
|
|
|
|
|
|
|
the raw error, |
4463
|
|
|
|
|
|
|
the error code, |
4464
|
|
|
|
|
|
|
a '*' wildcard, |
4465
|
|
|
|
|
|
|
and then just returns the original error w/o modification. |
4466
|
|
|
|
|
|
|
|
4467
|
|
|
|
|
|
|
Be careful using the '*' wildcard. This will translate -any- error message that doesn't have a friendlier version. |
4468
|
|
|
|
|
|
|
|
4469
|
|
|
|
|
|
|
=cut |
4470
|
|
|
|
|
|
|
|
4471
|
|
|
|
|
|
|
=pod |
4472
|
|
|
|
|
|
|
|
4473
|
|
|
|
|
|
|
=begin btest(errortranslator) |
4474
|
|
|
|
|
|
|
|
4475
|
|
|
|
|
|
|
my $uses_real = __PACKAGE__->use_real_errors(); |
4476
|
|
|
|
|
|
|
$test->is(__PACKAGE__->use_real_errors(0), 0, "Uses real errors"); |
4477
|
|
|
|
|
|
|
|
4478
|
|
|
|
|
|
|
my $translator = { |
4479
|
|
|
|
|
|
|
'test error' => 'test message' |
4480
|
|
|
|
|
|
|
}; |
4481
|
|
|
|
|
|
|
|
4482
|
|
|
|
|
|
|
$test->ok($translator, "Created translator"); |
4483
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errortranslator($translator), $translator, "Set translator"); |
4484
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error('test error', 'test code'), undef, "Set error"); |
4485
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), 'test message', 'Re-wrote error message'); |
4486
|
|
|
|
|
|
|
|
4487
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errortranslator($uses_real), $uses_real, 'Class reset uses real error'); |
4488
|
|
|
|
|
|
|
|
4489
|
|
|
|
|
|
|
=end btest(errortranslator) |
4490
|
|
|
|
|
|
|
|
4491
|
|
|
|
|
|
|
=cut |
4492
|
|
|
|
|
|
|
|
4493
|
|
|
|
|
|
|
# The error translator turns system defined error messages into user readable error messages. |
4494
|
|
|
|
|
|
|
# It's clunky, but it's the best we've got for now. |
4495
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('errortranslator'); |
4496
|
|
|
|
|
|
|
|
4497
|
|
|
|
|
|
|
=pod |
4498
|
|
|
|
|
|
|
|
4499
|
|
|
|
|
|
|
=item use_real_errors |
4500
|
|
|
|
|
|
|
|
4501
|
|
|
|
|
|
|
use_real_errors bypasses the errortranslator and only returns the errstring. This is useful so that your developers can get |
4502
|
|
|
|
|
|
|
back useful information, but your users can get back a friendly message. |
4503
|
|
|
|
|
|
|
|
4504
|
|
|
|
|
|
|
=cut |
4505
|
|
|
|
|
|
|
|
4506
|
|
|
|
|
|
|
=begin btest(use_real_errors) |
4507
|
|
|
|
|
|
|
|
4508
|
|
|
|
|
|
|
my $translator = __PACKAGE__->errortranslator(); |
4509
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->errortranslator( |
4510
|
|
|
|
|
|
|
{ |
4511
|
|
|
|
|
|
|
'test code' => "friendly test message", |
4512
|
|
|
|
|
|
|
'formatted test error %d' => "friendlier test message", |
4513
|
|
|
|
|
|
|
'formatted test error 7' => 'friendliest test message', |
4514
|
|
|
|
|
|
|
'extra error' => 'friendliest test message 2' |
4515
|
|
|
|
|
|
|
}), |
4516
|
|
|
|
|
|
|
'Class set error translator' |
4517
|
|
|
|
|
|
|
); |
4518
|
|
|
|
|
|
|
|
4519
|
|
|
|
|
|
|
my $uses_real = __PACKAGE__->use_real_errors(); |
4520
|
|
|
|
|
|
|
|
4521
|
|
|
|
|
|
|
my $confClass = __PACKAGE__->pkg_for_type('conf'); |
4522
|
|
|
|
|
|
|
$test->ok($confClass, "Got conf"); |
4523
|
|
|
|
|
|
|
|
4524
|
|
|
|
|
|
|
my $cfg = $confClass->conf; |
4525
|
|
|
|
|
|
|
$test->ok($cfg, "Got configuration"); |
4526
|
|
|
|
|
|
|
|
4527
|
|
|
|
|
|
|
$test->ok($cfg->{"Basset::Object"}->{'use_real_errors'} = 1, "enables real errors"); |
4528
|
|
|
|
|
|
|
|
4529
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("extra error", "test code"), undef, "Class sets error"); |
4530
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "extra error...with code (test code)", "Class gets literal error for literal"); |
4531
|
|
|
|
|
|
|
|
4532
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(["formatted test error %d", 7], "test code"), undef, "Class sets formatted error"); |
4533
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "formatted test error 7...with code (test code)", "Class gets literal error for formatted string"); |
4534
|
|
|
|
|
|
|
|
4535
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error(["formatted test error %d", 9], "test code"), undef, "Class sets formatted error"); |
4536
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "formatted test error 9...with code (test code)", "Class gets literal error for string format"); |
4537
|
|
|
|
|
|
|
|
4538
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("Some test error", "test code"), undef, "Class sets standard error"); |
4539
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "Some test error...with code (test code)", "Class gets literal error for error code"); |
4540
|
|
|
|
|
|
|
|
4541
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("Some unknown error", "unknown code"), undef, "Class sets standard error w/o translation"); |
4542
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "Some unknown error...with code (unknown code)", "Class gets no user error"); |
4543
|
|
|
|
|
|
|
|
4544
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->errortranslator( |
4545
|
|
|
|
|
|
|
{ |
4546
|
|
|
|
|
|
|
'test code' => "friendly test message", |
4547
|
|
|
|
|
|
|
'formatted test error %d' => "friendlier test message", |
4548
|
|
|
|
|
|
|
'formatted test error 7' => 'friendliest test message', |
4549
|
|
|
|
|
|
|
'extra error' => 'friendliest test message 2', |
4550
|
|
|
|
|
|
|
'*' => 'star error', |
4551
|
|
|
|
|
|
|
}), |
4552
|
|
|
|
|
|
|
'Class changed error translator' |
4553
|
|
|
|
|
|
|
); |
4554
|
|
|
|
|
|
|
|
4555
|
|
|
|
|
|
|
$test->is(scalar __PACKAGE__->error("Some unknown error", "unknown code"), undef, "Class sets standard error w/o translation"); |
4556
|
|
|
|
|
|
|
$test->is(__PACKAGE__->usererror(), "Some unknown error...with code (unknown code)", "Class gets literal star error"); |
4557
|
|
|
|
|
|
|
|
4558
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errortranslator($translator), $translator, 'Class reset error translator'); |
4559
|
|
|
|
|
|
|
#$test->is(__PACKAGE__->errortranslator($uses_real), $uses_real, 'Class reset uses real error'); |
4560
|
|
|
|
|
|
|
#$test->ok('foo', 'bar'); |
4561
|
|
|
|
|
|
|
$test->is($cfg->{"__PACKAGE__"}->{'use_real_errors'} = $uses_real, $uses_real, "enables reset uses real errors"); |
4562
|
|
|
|
|
|
|
|
4563
|
|
|
|
|
|
|
=end btest(use_real_errors) |
4564
|
|
|
|
|
|
|
|
4565
|
|
|
|
|
|
|
=cut |
4566
|
|
|
|
|
|
|
|
4567
|
|
|
|
|
|
|
__PACKAGE__->add_default_class_attr('use_real_errors'); |
4568
|
|
|
|
|
|
|
|
4569
|
|
|
|
|
|
|
=pod |
4570
|
|
|
|
|
|
|
|
4571
|
|
|
|
|
|
|
=item delegate |
4572
|
|
|
|
|
|
|
|
4573
|
|
|
|
|
|
|
This is borrows from objective-C, because I like it so much. Basically, the delegate is a simple |
4574
|
|
|
|
|
|
|
catch all place for an additional object that operates on your current object. |
4575
|
|
|
|
|
|
|
|
4576
|
|
|
|
|
|
|
sub some_method { |
4577
|
|
|
|
|
|
|
my $self = shift; |
4578
|
|
|
|
|
|
|
#call the delegate when we call some_method |
4579
|
|
|
|
|
|
|
if ($self->delegate && $self->delegate->can('foo')) { |
4580
|
|
|
|
|
|
|
$self->delegate->foo(@useful_arguments); |
4581
|
|
|
|
|
|
|
}; |
4582
|
|
|
|
|
|
|
} |
4583
|
|
|
|
|
|
|
|
4584
|
|
|
|
|
|
|
=cut |
4585
|
|
|
|
|
|
|
|
4586
|
|
|
|
|
|
|
=pod |
4587
|
|
|
|
|
|
|
|
4588
|
|
|
|
|
|
|
=begin btest(delegate) |
4589
|
|
|
|
|
|
|
|
4590
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
4591
|
|
|
|
|
|
|
$test->ok($o, "Set up object"); |
4592
|
|
|
|
|
|
|
my $o2 = __PACKAGE__->new(); |
4593
|
|
|
|
|
|
|
$test->ok($o2, "Set up second object"); |
4594
|
|
|
|
|
|
|
$test->ok(! scalar __PACKAGE__->delegate($o), "Class cannot set delegate"); |
4595
|
|
|
|
|
|
|
$test->is(scalar $o->delegate($o2), $o2, "Object set delegate"); |
4596
|
|
|
|
|
|
|
$test->is(scalar $o->delegate(), $o2, "Object accessed delegate"); |
4597
|
|
|
|
|
|
|
$test->is(scalar $o->delegate(undef), undef, "Object deleted delegate"); |
4598
|
|
|
|
|
|
|
|
4599
|
|
|
|
|
|
|
=end btest(delegate) |
4600
|
|
|
|
|
|
|
|
4601
|
|
|
|
|
|
|
=cut |
4602
|
|
|
|
|
|
|
|
4603
|
|
|
|
|
|
|
__PACKAGE__->add_attr('delegate'); |
4604
|
|
|
|
|
|
|
|
4605
|
|
|
|
|
|
|
=pod |
4606
|
|
|
|
|
|
|
|
4607
|
|
|
|
|
|
|
=item types |
4608
|
|
|
|
|
|
|
|
4609
|
|
|
|
|
|
|
Defined in your conf file. Lists types used by the factory and pkg_for_type. See those methods for more info. |
4610
|
|
|
|
|
|
|
Use a hashref in the conf file: |
4611
|
|
|
|
|
|
|
|
4612
|
|
|
|
|
|
|
types %= user=Basset::User |
4613
|
|
|
|
|
|
|
types %= group=Basset::Group |
4614
|
|
|
|
|
|
|
#etc |
4615
|
|
|
|
|
|
|
|
4616
|
|
|
|
|
|
|
That is, types should be an array of values that are = delimited. type=class. |
4617
|
|
|
|
|
|
|
|
4618
|
|
|
|
|
|
|
=cut |
4619
|
|
|
|
|
|
|
|
4620
|
|
|
|
|
|
|
=pod |
4621
|
|
|
|
|
|
|
|
4622
|
|
|
|
|
|
|
=begin btest(types) |
4623
|
|
|
|
|
|
|
|
4624
|
|
|
|
|
|
|
$test->ok(__PACKAGE__->types, "Got types out of the conf file"); |
4625
|
|
|
|
|
|
|
my $typesbkp = __PACKAGE__->types(); |
4626
|
|
|
|
|
|
|
my $newtypes = {%$typesbkp, 'testtype1' => '__PACKAGE__', 'testtype2' => 'boguspkg'}; |
4627
|
|
|
|
|
|
|
$test->ok($typesbkp, "Backed up the types"); |
4628
|
|
|
|
|
|
|
$test->is(__PACKAGE__->types($newtypes), $newtypes, "Set new types"); |
4629
|
|
|
|
|
|
|
$test->is(__PACKAGE__->pkg_for_type('testtype1'), '__PACKAGE__', "Got class for new type"); |
4630
|
|
|
|
|
|
|
$test->ok(! scalar __PACKAGE__->pkg_for_type('testtype2'), "Could not access invalid type"); |
4631
|
|
|
|
|
|
|
$test->is(__PACKAGE__->types($typesbkp), $typesbkp, "Re-set original types"); |
4632
|
|
|
|
|
|
|
|
4633
|
|
|
|
|
|
|
=end btest(types) |
4634
|
|
|
|
|
|
|
|
4635
|
|
|
|
|
|
|
=cut |
4636
|
|
|
|
|
|
|
|
4637
|
|
|
|
|
|
|
#we're careful not to re-define this one, since it was probably already defined in Basset::Object::Conf, which is necessary due to circular |
4638
|
|
|
|
|
|
|
#inheritance issues. |
4639
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('types', {}) unless __PACKAGE__->can('types'); |
4640
|
|
|
|
|
|
|
|
4641
|
|
|
|
|
|
|
#set up our defaults. Config file? Why bother. |
4642
|
|
|
|
|
|
|
__PACKAGE__->types->{'logger'} ||= 'Basset::Logger'; |
4643
|
|
|
|
|
|
|
__PACKAGE__->types->{'notificationcenter'} ||= 'Basset::NotificationCenter'; |
4644
|
|
|
|
|
|
|
__PACKAGE__->types->{'conf'} ||= 'Basset::Object::Conf'; |
4645
|
|
|
|
|
|
|
__PACKAGE__->types->{'driver'} ||= 'Basset::DB'; |
4646
|
|
|
|
|
|
|
__PACKAGE__->types->{'table'} ||= 'Basset::DB::Table'; |
4647
|
|
|
|
|
|
|
__PACKAGE__->types->{'template'} ||= 'Basset::Template'; |
4648
|
|
|
|
|
|
|
__PACKAGE__->types->{'object'} ||= 'Basset::Object'; |
4649
|
|
|
|
|
|
|
__PACKAGE__->types->{'persistentobject'} ||= 'Basset::Object::Persistent'; |
4650
|
|
|
|
|
|
|
__PACKAGE__->types->{'machine'} ||= 'Basset::Machine'; |
4651
|
|
|
|
|
|
|
__PACKAGE__->types->{'state'} ||= 'Basset::Machine::State'; |
4652
|
|
|
|
|
|
|
__PACKAGE__->types->{'test'} ||= 'Basset::Test'; |
4653
|
|
|
|
|
|
|
|
4654
|
|
|
|
|
|
|
|
4655
|
|
|
|
|
|
|
=pod |
4656
|
|
|
|
|
|
|
|
4657
|
|
|
|
|
|
|
=item restrictions |
4658
|
|
|
|
|
|
|
|
4659
|
|
|
|
|
|
|
This stores the restrictions that B be added to this class, but not necessarily the |
4660
|
|
|
|
|
|
|
ones that are in effect. Add new restrictions with the add_restriction method. |
4661
|
|
|
|
|
|
|
|
4662
|
|
|
|
|
|
|
=cut |
4663
|
|
|
|
|
|
|
|
4664
|
|
|
|
|
|
|
=pod |
4665
|
|
|
|
|
|
|
|
4666
|
|
|
|
|
|
|
=begin btest(restrictions) |
4667
|
|
|
|
|
|
|
|
4668
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::restrictions::subclass1; |
4669
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
4670
|
|
|
|
|
|
|
|
4671
|
|
|
|
|
|
|
package __PACKAGE__; |
4672
|
|
|
|
|
|
|
|
4673
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::restrictions::subclass1->isa('__PACKAGE__'), 'proper subclass'); |
4674
|
|
|
|
|
|
|
my $restrictions = { |
4675
|
|
|
|
|
|
|
'foo' => [ |
4676
|
|
|
|
|
|
|
'a' => 'b' |
4677
|
|
|
|
|
|
|
] |
4678
|
|
|
|
|
|
|
}; |
4679
|
|
|
|
|
|
|
$test->ok($restrictions, 'made restrictions'); |
4680
|
|
|
|
|
|
|
$test->is(Basset::Test::Testing::__PACKAGE__::restrictions::subclass1->restrictions($restrictions), $restrictions, 'added restrictions'); |
4681
|
|
|
|
|
|
|
$test->is(Basset::Test::Testing::__PACKAGE__::restrictions::subclass1->restrictions, $restrictions, 'accessed restrictions'); |
4682
|
|
|
|
|
|
|
|
4683
|
|
|
|
|
|
|
=end btest(restrictions) |
4684
|
|
|
|
|
|
|
|
4685
|
|
|
|
|
|
|
=cut |
4686
|
|
|
|
|
|
|
|
4687
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('restrictions'); |
4688
|
|
|
|
|
|
|
|
4689
|
|
|
|
|
|
|
=pod |
4690
|
|
|
|
|
|
|
|
4691
|
|
|
|
|
|
|
=begin btest(applied_restrictions) |
4692
|
|
|
|
|
|
|
|
4693
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::applied_restrictions::Subclass; |
4694
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
4695
|
|
|
|
|
|
|
|
4696
|
|
|
|
|
|
|
my %restrictions = ( |
4697
|
|
|
|
|
|
|
'specialerror' => [ |
4698
|
|
|
|
|
|
|
'error' => 'error3', |
4699
|
|
|
|
|
|
|
'errcode' => 'errcode3' |
4700
|
|
|
|
|
|
|
], |
4701
|
|
|
|
|
|
|
'invalidrestriction' => [ |
4702
|
|
|
|
|
|
|
'junkymethod' => 'otherjunkymethod' |
4703
|
|
|
|
|
|
|
] |
4704
|
|
|
|
|
|
|
); |
4705
|
|
|
|
|
|
|
|
4706
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('e3'); |
4707
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('c3'); |
4708
|
|
|
|
|
|
|
|
4709
|
|
|
|
|
|
|
$test->is(__PACKAGE__->e3(0), 0, "set e3 to 0"); |
4710
|
|
|
|
|
|
|
$test->is(__PACKAGE__->c3(0), 0, "set c3 to 0"); |
4711
|
|
|
|
|
|
|
|
4712
|
|
|
|
|
|
|
sub error3 { |
4713
|
|
|
|
|
|
|
my $self = shift; |
4714
|
|
|
|
|
|
|
$self->e3($self->e3 + 1); |
4715
|
|
|
|
|
|
|
return $self->SUPER::error(@_); |
4716
|
|
|
|
|
|
|
} |
4717
|
|
|
|
|
|
|
|
4718
|
|
|
|
|
|
|
sub errcode3 { |
4719
|
|
|
|
|
|
|
my $self = shift; |
4720
|
|
|
|
|
|
|
$self->c3($self->c3 + 1); |
4721
|
|
|
|
|
|
|
return $self->SUPER::errcode(@_); |
4722
|
|
|
|
|
|
|
} |
4723
|
|
|
|
|
|
|
|
4724
|
|
|
|
|
|
|
$test->ok(scalar Basset::Test::Testing::__PACKAGE__::applied_restrictions::Subclass->add_restrictions(%restrictions), "Added restrictions to subclass"); |
4725
|
|
|
|
|
|
|
|
4726
|
|
|
|
|
|
|
package __PACKAGE__; |
4727
|
|
|
|
|
|
|
|
4728
|
|
|
|
|
|
|
$test->ok(Basset::Test::Testing::__PACKAGE__::applied_restrictions::Subclass->isa('__PACKAGE__'), 'Proper subclass'); |
4729
|
|
|
|
|
|
|
my $subclass = Basset::Test::Testing::__PACKAGE__::applied_restrictions::Subclass->restrict('specialerror'); |
4730
|
|
|
|
|
|
|
$test->ok($subclass, "Restricted error"); |
4731
|
|
|
|
|
|
|
$test->ok(! scalar $subclass->add_restricted_method('invalidrestriction', 'junkymethod'), "Could not add invalid restriction"); |
4732
|
|
|
|
|
|
|
$test->ok($subclass->restricted, "Subclass is restricted"); |
4733
|
|
|
|
|
|
|
|
4734
|
|
|
|
|
|
|
$test->ok($subclass->applied_restrictions, "Subclass has applied restrictions"); |
4735
|
|
|
|
|
|
|
my $restrictions = $subclass->applied_restrictions; |
4736
|
|
|
|
|
|
|
|
4737
|
|
|
|
|
|
|
$test->ok(ref $restrictions eq 'ARRAY', 'applied restrictions are an array'); |
4738
|
|
|
|
|
|
|
$test->is(scalar @$restrictions, 1, "Subclass has 1 restriction"); |
4739
|
|
|
|
|
|
|
$test->is($restrictions->[0], 'specialerror', 'Correct restriction in place'); |
4740
|
|
|
|
|
|
|
|
4741
|
|
|
|
|
|
|
=end btest(applied_restrictions) |
4742
|
|
|
|
|
|
|
|
4743
|
|
|
|
|
|
|
=cut |
4744
|
|
|
|
|
|
|
|
4745
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('applied_restrictions', []); |
4746
|
|
|
|
|
|
|
|
4747
|
|
|
|
|
|
|
=pod |
4748
|
|
|
|
|
|
|
|
4749
|
|
|
|
|
|
|
=item restricted |
4750
|
|
|
|
|
|
|
|
4751
|
|
|
|
|
|
|
Boolean flag. returns 0 if the class is non-restricted, or 1 if it is restricted. |
4752
|
|
|
|
|
|
|
|
4753
|
|
|
|
|
|
|
=cut |
4754
|
|
|
|
|
|
|
|
4755
|
|
|
|
|
|
|
=pod |
4756
|
|
|
|
|
|
|
|
4757
|
|
|
|
|
|
|
=begin btest(restricted) |
4758
|
|
|
|
|
|
|
|
4759
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::restricted::Subclass1; |
4760
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
4761
|
|
|
|
|
|
|
|
4762
|
|
|
|
|
|
|
package __PACKAGE__; |
4763
|
|
|
|
|
|
|
|
4764
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->restricted, "__PACKAGE__ is not restricted"); |
4765
|
|
|
|
|
|
|
$test->ok(! Basset::Test::Testing::__PACKAGE__::restricted::Subclass1->restricted, "Subclass is not restricted"); |
4766
|
|
|
|
|
|
|
my $subclass = __PACKAGE__->inline_class; |
4767
|
|
|
|
|
|
|
$test->ok($subclass, "Subclassed __PACKAGE__"); |
4768
|
|
|
|
|
|
|
my $subclass2 = Basset::Test::Testing::__PACKAGE__::restricted::Subclass1->inline_class(); |
4769
|
|
|
|
|
|
|
$test->ok($subclass2, "Restricted Basset::Test::Testing::__PACKAGE__::restricted::Subclass1"); |
4770
|
|
|
|
|
|
|
$test->ok($subclass->restricted, "Subclass is restricted"); |
4771
|
|
|
|
|
|
|
$test->ok($subclass2->restricted, "Subclass is restricted"); |
4772
|
|
|
|
|
|
|
|
4773
|
|
|
|
|
|
|
=end btest(restricted) |
4774
|
|
|
|
|
|
|
|
4775
|
|
|
|
|
|
|
=cut |
4776
|
|
|
|
|
|
|
|
4777
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('restricted', 0); |
4778
|
|
|
|
|
|
|
|
4779
|
|
|
|
|
|
|
=pod |
4780
|
|
|
|
|
|
|
|
4781
|
|
|
|
|
|
|
=item exceptions |
4782
|
|
|
|
|
|
|
|
4783
|
|
|
|
|
|
|
boolean flag 1/0. Off by default. Some people, for some silly reason, like to use exceptions. |
4784
|
|
|
|
|
|
|
Personally, I avoid them like the plague. Nonetheless, I'm an agreeable sort and wanted to provide |
4785
|
|
|
|
|
|
|
the option. Standard procedure is to call a method or bubble up an error: |
4786
|
|
|
|
|
|
|
|
4787
|
|
|
|
|
|
|
sub method { |
4788
|
|
|
|
|
|
|
my $self = shift; |
4789
|
|
|
|
|
|
|
|
4790
|
|
|
|
|
|
|
my $obj = shift; |
4791
|
|
|
|
|
|
|
|
4792
|
|
|
|
|
|
|
$obj->trysomething() or return $self->error($obj->errvals); |
4793
|
|
|
|
|
|
|
} |
4794
|
|
|
|
|
|
|
|
4795
|
|
|
|
|
|
|
methods return undef, so if the return is undefined, you bubble it back up until something can |
4796
|
|
|
|
|
|
|
handle it. With exceptions enabled, the error method (called somewhere inside $obj's trysomething |
4797
|
|
|
|
|
|
|
method) would instead die with an error of the errorcode passed. Additionally, the error itself |
4798
|
|
|
|
|
|
|
is set in the last_exception attribute. So you write your method call this way, if exceptions |
4799
|
|
|
|
|
|
|
are enabled: |
4800
|
|
|
|
|
|
|
|
4801
|
|
|
|
|
|
|
sub method { |
4802
|
|
|
|
|
|
|
my $self = shift; |
4803
|
|
|
|
|
|
|
my $obj = shift; |
4804
|
|
|
|
|
|
|
|
4805
|
|
|
|
|
|
|
eval { |
4806
|
|
|
|
|
|
|
$obj->trysomething(); |
4807
|
|
|
|
|
|
|
} |
4808
|
|
|
|
|
|
|
if ($@ =~ /interesting error code/) { |
4809
|
|
|
|
|
|
|
print "We died because of " . $obj->last_exception . "\n"; |
4810
|
|
|
|
|
|
|
} else { |
4811
|
|
|
|
|
|
|
$obj->error($obj->errvals);#re-throw the exception |
4812
|
|
|
|
|
|
|
} |
4813
|
|
|
|
|
|
|
} |
4814
|
|
|
|
|
|
|
|
4815
|
|
|
|
|
|
|
Note that last_exception should be used to find out the error involved, not the ->error method. This |
4816
|
|
|
|
|
|
|
is because you can't know which object actually threw the exception. |
4817
|
|
|
|
|
|
|
|
4818
|
|
|
|
|
|
|
=cut |
4819
|
|
|
|
|
|
|
|
4820
|
|
|
|
|
|
|
=pod |
4821
|
|
|
|
|
|
|
|
4822
|
|
|
|
|
|
|
=begin btest(exceptions) |
4823
|
|
|
|
|
|
|
|
4824
|
|
|
|
|
|
|
my $confClass = __PACKAGE__->pkg_for_type('conf'); |
4825
|
|
|
|
|
|
|
$test->ok($confClass, "Got conf"); |
4826
|
|
|
|
|
|
|
|
4827
|
|
|
|
|
|
|
my $cfg = $confClass->conf; |
4828
|
|
|
|
|
|
|
$test->ok($cfg, "Got configuration"); |
4829
|
|
|
|
|
|
|
|
4830
|
|
|
|
|
|
|
my $exceptions = $cfg->{"Basset::Object"}->{'exceptions'}; |
4831
|
|
|
|
|
|
|
|
4832
|
|
|
|
|
|
|
$test->is($cfg->{"Basset::Object"}->{'exceptions'} = 0, 0, "disables exceptions"); |
4833
|
|
|
|
|
|
|
$test->is($cfg->{"Basset::Object"}->{'exceptions'} = 0, 0, "enables exceptions"); |
4834
|
|
|
|
|
|
|
$test->is($cfg->{"Basset::Object"}->{'exceptions'} = $exceptions, $exceptions, "reset exceptions"); |
4835
|
|
|
|
|
|
|
|
4836
|
|
|
|
|
|
|
=end btest(exceptions) |
4837
|
|
|
|
|
|
|
|
4838
|
|
|
|
|
|
|
=cut |
4839
|
|
|
|
|
|
|
|
4840
|
|
|
|
|
|
|
__PACKAGE__->add_default_class_attr('exceptions'); |
4841
|
|
|
|
|
|
|
|
4842
|
|
|
|
|
|
|
=pod |
4843
|
|
|
|
|
|
|
|
4844
|
|
|
|
|
|
|
=item last_exception |
4845
|
|
|
|
|
|
|
|
4846
|
|
|
|
|
|
|
stores the message associated with the last exception |
4847
|
|
|
|
|
|
|
|
4848
|
|
|
|
|
|
|
=cut |
4849
|
|
|
|
|
|
|
|
4850
|
|
|
|
|
|
|
=pod |
4851
|
|
|
|
|
|
|
|
4852
|
|
|
|
|
|
|
=begin btest(last_exception) |
4853
|
|
|
|
|
|
|
|
4854
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
4855
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
4856
|
|
|
|
|
|
|
|
4857
|
|
|
|
|
|
|
my $confClass = __PACKAGE__->pkg_for_type('conf'); |
4858
|
|
|
|
|
|
|
$test->ok($confClass, "Got conf"); |
4859
|
|
|
|
|
|
|
|
4860
|
|
|
|
|
|
|
my $cfg = $confClass->conf; |
4861
|
|
|
|
|
|
|
$test->ok($cfg, "Got configuration"); |
4862
|
|
|
|
|
|
|
|
4863
|
|
|
|
|
|
|
$test->ok($cfg->{"Basset::Object"}->{'exceptions'} = 1, "enables exceptions"); |
4864
|
|
|
|
|
|
|
|
4865
|
|
|
|
|
|
|
$test->ok(scalar __PACKAGE__->wipe_errors, "Wiped out errors"); |
4866
|
|
|
|
|
|
|
$test->ok(! __PACKAGE__->last_exception, "Last exception is empty"); |
4867
|
|
|
|
|
|
|
eval { |
4868
|
|
|
|
|
|
|
__PACKAGE__->error('test exception', 'test code'); |
4869
|
|
|
|
|
|
|
}; |
4870
|
|
|
|
|
|
|
$test->like($@, "/test code/", "Thrown exception matches"); |
4871
|
|
|
|
|
|
|
$test->like(__PACKAGE__->last_exception, qr/test exception/, "Last exception matches"); |
4872
|
|
|
|
|
|
|
$test->like($o->last_exception, qr/test exception/, "Object last exception matches"); |
4873
|
|
|
|
|
|
|
$test->is($cfg->{"Basset::Object"}->{'exceptions'} = 0, 0,"disables exceptions"); |
4874
|
|
|
|
|
|
|
|
4875
|
|
|
|
|
|
|
=end btest(last_exception) |
4876
|
|
|
|
|
|
|
|
4877
|
|
|
|
|
|
|
=cut |
4878
|
|
|
|
|
|
|
|
4879
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('last_exception'); |
4880
|
|
|
|
|
|
|
|
4881
|
|
|
|
|
|
|
=pod |
4882
|
|
|
|
|
|
|
|
4883
|
|
|
|
|
|
|
=back |
4884
|
|
|
|
|
|
|
|
4885
|
|
|
|
|
|
|
=cut |
4886
|
|
|
|
|
|
|
|
4887
|
|
|
|
|
|
|
1; |
4888
|
|
|
|
|
|
|
__END__ |