File Coverage

blib/lib/Class/BuildMethods.pm
Criterion Covered Total %
statement 173 214 80.8
branch 58 84 69.0
condition 5 16 31.2
subroutine 26 29 89.6
pod 6 6 100.0
total 268 349 76.7


line stmt bran cond sub pod time code
1             package Class::BuildMethods;
2              
3 4     4   96466 use warnings;
  4         11  
  4         137  
4 4     4   22 use strict;
  4         10  
  4         138  
5              
6 4     4   33 use Scalar::Util qw/blessed/;
  4         11  
  4         2893  
7              
8             #
9             # This is provided next to Scalar::Util so that people can see what's going
10             # on. Basically, ActiveState's build system does not provide a version of
11             # Scalar::Util with refaddr, so modules requiring this function cannot build.
12             # As a result, I'm forced to manually copy it here.
13             #
14              
15             sub _refaddr($) {
16 63 50   63   169 my $pkg = ref( $_[0] ) or return undef;
17 63 50       263 if ( blessed( $_[0] ) ) {
18 63         200 bless $_[0], 'Class::BuildMethods::Fake';
19             }
20             else {
21 0         0 $pkg = undef;
22             }
23 63         272 "$_[0]" =~ /0x(\w+)/;
24 63         79 my $i = do { local $^W; hex $1 };
  63         149  
  63         193  
25 63 50       757 bless $_[0], $pkg if defined $pkg;
26 63         171 $i;
27             }
28              
29             my $VALID_METHOD_NAME = qr/^[_[:alpha:]][[:word:]]*$/;
30              
31             =head1 NAME
32              
33             Class::BuildMethods - Lightweight implementation-agnostic generic methods.
34              
35             =head1 VERSION
36              
37             Version 0.22
38              
39             =cut
40              
41             our $VERSION = '0.22';
42              
43             =head1 SYNOPSIS
44              
45             use Class::BuildMethods
46             'name',
47             rank => { default => 'private' },
48             date => { validate => \&valid_date };
49              
50             =head1 DESCRIPTION
51              
52             This class allows you to quickly add simple getter/setter methods to your
53             classes with optional default values and validation. We assume no
54             implementation for your class, so you may use a standard blessed hashref,
55             blessed arrayref, inside-out objects, etc. This module B alter
56             anything about your class aside from installing requested methods and
57             optionally adding a C method. See L for more
58             information, particularly the C method.
59              
60             =head1 BASIC METHODS
61              
62             package Foo;
63             use Class::BuildMethods qw/name rank/;
64            
65             sub new {
66             ... whatever implementation you need
67             }
68              
69             # later
70              
71             my $foo = Foo->new;
72             $foo->name('bob');
73             print $foo->name; # prints 'bob'
74              
75             Using a simple list with C adds those methods as
76             getters/setters to your class.
77              
78             Note that when using a method as a setter, you may only pass in a single
79             value. Arrays and hashes should be passed by reference.
80              
81             =head1 DEFAULT VALUES
82              
83             package Foo;
84             use Class::BuildMethods
85             'name',
86             rank => { default => 'private' };
87              
88             # later
89              
90             my $foo = Foo->new;
91             print $foo->rank; # prints 'private'
92             $foo->rank('corporal');
93             print $foo->rank; # prints 'corporal'
94              
95             After any method name passed to C, you may pass it a hash
96             reference of constraints. If a key of "default" is found, the value for that
97             key will be assigned as the default value for the method.
98              
99             =head1 VALIDATION
100              
101             package Drinking::Buddy;
102             use Class::BuildMethods;
103             'name',
104             age => {
105             validate => sub {
106             my ($self, $age) = @_;
107             die "Too young" if $age < 21;
108             }
109             },
110             drinking_age => {
111             class_data => 1,
112             default => 21
113             };
114              
115             # later
116              
117             my $bubba = Drinking::Buddy->new;
118             $bubba->age(18); # fatal error
119             $bubba->age(21); # Works
120             print $bubba->age; # prints '21'
121             print $bubba->drinking_age; # prints '21'
122              
123             my $jimbo = Drinking::Buddy->new;
124             print $jimbo->drinking_age; # prints '21'
125             $jimbo->drinking_age(18); # UK drinking age
126             print $jimbo->drinking_age; # prints '18'
127             print $bubba->drinking_age; # prints '18'
128              
129             If a key of "validate" is found, a subroutine is expected as the next
130             argument. When setting a value, the subroutine will be called with the
131             invocant as the first argument and the new value as the second argument. You
132             may supply any code you wish to enforce validation.
133              
134             =cut
135              
136 12     12   888 sub import { goto &build }
137              
138             ##############################################################################
139              
140             =head1 ADDING METHODS AT RUNTIME
141              
142             =head2 build
143              
144             Class::BuildMethods->build(
145             'name',
146             rank => { default => 'private' }
147             );
148              
149             This allows you to add the methods at runtime. Takes the same arguments as
150             the import list to the class.
151              
152             =cut
153              
154             my %value_for;
155             my %default_for;
156             my %methods_for;
157             my %no_destroy_for;
158             my %class_data_for;
159              
160             sub build {
161 13     13 1 25 my $class = shift;
162 13         37 my ($calling_package) = caller();
163 13   50     89 $methods_for{$calling_package} ||= [];
164 13         36 while (@_) {
165 21         32 my $method = shift;
166 21 100       51 if ( '[NO_DESTROY]' eq $method ) {
167 1         2 $no_destroy_for{$calling_package} = 1;
168 1         2 next;
169             }
170 20 100       125 unless ( $method =~ $VALID_METHOD_NAME ) {
171 1         10 require Carp;
172 1         231 Carp::croak("'$method' is not a valid method name");
173             }
174 19         38 $method = "${calling_package}::$method";
175 19         20 push @{ $methods_for{$calling_package} } => $method;
  19         46  
176 19         33 my ( $constraints, $validation_sub, $class_data );
177 19 100       46 if ( 'HASH' eq ref $_[0] ) {
178 9         109 $constraints = shift;
179 9 100       42 $default_for{$method} = delete $constraints->{default}
180             if exists $constraints->{default};
181 9         15 $validation_sub = delete $constraints->{validate};
182 9         15 $class_data = delete $constraints->{class_data};
183              
184 9 100       37 if ( my @keys = keys %$constraints ) {
185 1         11 require Carp;
186 1         384 Carp::croak("Unknown constraint keys (@keys) for $method");
187             }
188             }
189 4     4   27 no strict 'refs';
  4         8  
  4         392  
190              
191             # XXX Note that the code duplication here is very annoying, yet
192             # purposeful. By not trying anything fancy like building the code and
193             # eval'ing it or trying to shove too many conditionals into one sub,
194             # we keep them fairly lightweight.
195 18 100       35 if ($class_data) {
196 4         6 $class_data_for{$calling_package} = 1;
197 4 50       11 if ( defined $validation_sub ) {
198 0 0       0 if ( exists $default_for{$method} ) {
199             *$method = sub {
200 0     0   0 my $proto = shift;
201 0   0     0 my $class = ref $proto || $proto;
202 0 0       0 unless ( exists $class_data_for{$class} ) {
203 4     4   20 no strict 'refs';
  4         9  
  4         838  
204 0         0 my @isa = @{"$class\::ISA"};
  0         0  
205 0         0 return $isa[0]->$method(@_);
206             }
207 0 0       0 unless ( exists $value_for{$method}{$class} ) {
208 0         0 $value_for{$method}{$class}
209             = $default_for{$method};
210             }
211 0 0       0 return $value_for{$method}{$class}
212             unless @_;
213 0         0 my $new_value = shift;
214 0         0 $proto->$validation_sub($new_value);
215 0         0 $value_for{$method}{$class} = $new_value;
216 0         0 return $proto;
217 0         0 };
218             }
219             else {
220             *$method = sub {
221 0     0   0 my $proto = shift;
222 0   0     0 my $class = ref $proto || $proto;
223 0 0       0 unless ( exists $class_data_for{$class} ) {
224 4     4   24 no strict 'refs';
  4         6  
  4         733  
225 0         0 my @isa = @{"$class\::ISA"};
  0         0  
226 0         0 return $isa[0]->$method(@_);
227             }
228 0 0       0 return $value_for{$method}{$class}
229             unless @_;
230 0         0 my $new_value = shift;
231 0         0 $proto->$validation_sub($new_value);
232 0         0 $value_for{$method}{$class} = $new_value;
233 0         0 return $proto;
234 0         0 };
235             }
236             }
237             else {
238 4 100       8 if ( exists $default_for{$method} ) {
239             *$method = sub {
240 26     26   59 my $proto = shift;
241 26   66     56 my $class = ref $proto || $proto;
242 26 100       45 unless ( exists $class_data_for{$class} ) {
243 4     4   28 no strict 'refs';
  4         14  
  4         800  
244 5         4 my @isa = @{"$class\::ISA"};
  5         16  
245 5         21 return $isa[0]->$method(@_);
246             }
247 21 100       43 unless ( exists $value_for{$method}{$class} ) {
248 3         7 $value_for{$method}{$class}
249             = $default_for{$method};
250             }
251 21 100       85 return $value_for{$method}{$class}
252             unless @_;
253 4         5 $value_for{$method}{$class} = shift;
254 4         8 return $proto;
255 2         25 };
256             }
257             else {
258             *$method = sub {
259 2     2   7 my $proto = shift;
260 2   33     7 my $class = ref $proto || $proto;
261 2 50       6 unless ( exists $class_data_for{$class} ) {
262 4     4   22 no strict 'refs';
  4         8  
  4         2629  
263 0         0 my @isa = @{"$class\::ISA"};
  0         0  
264 0         0 return $isa[0]->$method(@_);
265             }
266 2 100       9 return $value_for{$method}{$class} unless @_;
267 1         2 $value_for{$method}{$class} = shift;
268 1         3 return $proto;
269 2         17 };
270             }
271             }
272             }
273             else { # instance data, not class data
274 14 100       21 if ( defined $validation_sub ) {
275 1 50       3 if ( exists $default_for{$method} ) {
276             *$method = sub {
277 0     0   0 my $self = shift;
278 0         0 my $instance = _refaddr $self;
279 0 0       0 unless ( exists $value_for{$method}{$instance} ) {
280 0         0 $value_for{$method}{$instance}
281             = $default_for{$method};
282             }
283 0 0       0 return $value_for{$method}{$instance} unless @_;
284 0         0 my $new_value = shift;
285 0         0 $self->$validation_sub($new_value);
286 0         0 $value_for{$method}{$instance} = $new_value;
287 0         0 return $self;
288 0         0 };
289             }
290             else {
291             *$method = sub {
292 6     6   1036 my $self = shift;
293 6         12 my $instance = _refaddr $self;
294 6 100       38 return $value_for{$method}{$instance} unless @_;
295 3         5 my $new_value = shift;
296 3         9 $self->$validation_sub($new_value);
297 2         11 $value_for{$method}{$instance} = $new_value;
298 2         13 return $self;
299 1         12 };
300             }
301             }
302             else {
303 13 100       26 if ( exists $default_for{$method} ) {
304             *$method = sub {
305 7     7   1962 my $self = shift;
306 7         28 my $instance = _refaddr $self;
307 7 100       34 unless ( exists $value_for{$method}{$instance} ) {
308 5         20 $value_for{$method}{$instance}
309             = $default_for{$method};
310             }
311 7 100       41 return $value_for{$method}{$instance} unless @_;
312 1         4 $value_for{$method}{$instance} = shift;
313 1         5 return $self;
314 3         22 };
315             }
316             else {
317             *$method = sub {
318 18     18   628 my $self = shift;
319 18         40 my $instance = _refaddr $self;
320 18 100       575 return $value_for{$method}{$instance} unless @_;
321 9         40 $value_for{$method}{$instance} = shift;
322 9         27 return $self;
323 10         91 };
324             }
325             }
326             }
327             }
328 11 100       1818 unless ( $no_destroy_for{$calling_package} ) {
329 4     4   26 no strict 'refs';
  4         11  
  4         1128  
330 10         5054 *{"${calling_package}::DESTROY"} = sub {
331 13     13   2256 __PACKAGE__->destroy(shift);
332 10         47 };
333             }
334             }
335              
336             ##############################################################################
337              
338             =head1 CLASS DATA
339              
340             Class data are data which are shared by all members of a class. For example,
341             if you create a C class, it's reasonable to assume that they will
342             all share the same value for PI (~ 3.14159), assuming you're really keen on
343             the anthropic principle and take it too far. You do this by simply
344             specifying a method as class data:
345              
346             package Universe;
347              
348             use Class::BuildMethods
349             pi => {
350             class_data => 1,
351             default => 3.1415927,
352             };
353              
354             The default is not mandatary for class data, but it's more commonly used than
355             for instance data. The validation property is still supported.
356              
357             Note that if you inherit a class method, the inherited class will B
358             share this class data:
359              
360             package Universe;
361              
362             use Class::BuildMethods
363             pi => {
364             class_data => 1,
365             default => 3.1415927,
366             };
367              
368             sub new { bless {}, shift }
369              
370             package Universe::Fantasy;
371             use base 'Universe';
372              
373             In the above example, both C and C will share the
374             value of C and changing the value in either the superclass or subclass
375             will change the value for the other.
376              
377             If you wish to be able to override the class data value, your subclass must
378             also declare the class data using C.
379              
380              
381             package Universe;
382              
383             use Class::BuildMethods
384             pi => {
385             class_data => 1,
386             default => 3.1415927,
387             };
388              
389             sub new { bless {}, shift }
390              
391             package Universe::Roman;
392             use base 'Universe';
393              
394             # Note that the story that ancient Romans used '3' for the value of pi is
395             # probably apocryphal.
396              
397             use Class::BuildMethods
398             pi => {
399             class_data => 1,
400             default => 3,
401             };
402            
403             With the above code, the value of pi is not shared between the classes. If
404             you want the C class to have the initial value for pi but
405             later be able to change it independently, do something like this:
406            
407             package Universe::Roman;
408             use base 'Universe';
409              
410             # Note that the story that ancient Romans used '3' for the value of pi is
411             # probably apocryphal.
412              
413             use Class::BuildMethods
414             pi => {
415             class_data => 1,
416             };
417            
418             sub new {
419             my $class = shift;
420             $class->pi($class->SUPER::pi);
421             return bless {}, $class;
422             }
423            
424             =cut
425              
426             ##############################################################################
427              
428             =head1 CLEANING UP
429              
430             =head2 destroy
431              
432             Class::BuildMethods->destroy($instance);
433              
434             This method destroys instance data for the instance supplied.
435              
436             Ordinarily you should never have to call this as a C method is
437             installed in your namespace which does this for you. However, if you need a
438             custom destroy method, provide the special C<[NO_DESTROY]> token to
439             C when you're creating it.
440              
441             use Class::BuildMethods qw(
442             name
443             rank
444             serial
445             [NO_DESTROY]
446             );
447              
448             sub DESTROY {
449             my $self shift;
450             # whatever cleanup code you need
451             Class::BuildMethods->destroy($self);
452             }
453              
454             =cut
455              
456             sub destroy {
457 14     14 1 24 my ( $class, $object ) = @_;
458 14         92 my @methods = $class->_find_methods($object);
459 14         28 my $instance = _refaddr $object;
460              
461 14 50       68 if (@methods) {
462 14         23 foreach my $method (@methods) {
463 30         122 delete $value_for{$method}{$instance};
464             }
465             }
466 14         362 return 1;
467             }
468              
469             sub _find_methods {
470 15     15   20 my ( $class, $object ) = @_;
471 15         29 my $instance = _refaddr $object;
472 15 50       54 my $this_package = ref $object if blessed $object;
473 15   50     42 $this_package ||= '';
474            
475 15         31 my @packages = $this_package;
476             {
477 4     4   59 no strict 'refs';
  4         12  
  4         2312  
  15         17  
478 15         18 push @packages => @{"${this_package}::ISA"};
  15         63  
479             }
480 15         16 my @methods;
481 15         26 foreach my $package (@packages) {
482 19 100       76 if ( !exists $methods_for{$package} ) {
483 2         13 while ( my ( $method, $instance_hash ) = each %value_for ) {
484 13 100       50 if ( exists $instance_hash->{$instance} ) {
485 1         5 push @methods => $method;
486             }
487             }
488             }
489             else {
490 17         15 push @methods => @{ $methods_for{$package} };
  17         84  
491             }
492             }
493 15         60 return @methods;
494             }
495              
496             # this is a testing hook to ensure that destroyed data is really gone
497             # do not rely on this method
498             sub _peek {
499 4     4   687 my ( $class, $package, $method, $_refaddr ) = @_;
500 4         9 my $fq_method = "${package}::$method";
501 4 100       16 return unless exists $value_for{$fq_method}{$_refaddr};
502 3         15 return $value_for{$fq_method}{$_refaddr};
503             }
504              
505             =head2 reset
506              
507             Class::BuildMethods->reset; # assumes current package
508             Class::BuildMethods->reset($package);
509              
510             This methods deletes all of the values for the methods added by
511             C. Any methods with default values will now have their
512             default values restored. It does not remove the methods. Returns the number
513             of methods reset.
514              
515             =cut
516              
517             sub reset {
518 4     4 1 1670 my ( $class, $package ) = @_;
519 4 100       14 unless ( defined $package ) {
520 1         4 ($package) = caller();
521             }
522 4 100       17 return unless $methods_for{$package};
523 3         4 my @methods = @{ $methods_for{$package} };
  3         11  
524 3         14 delete @value_for{@methods};
525 3         14 return scalar @methods;
526             }
527              
528             ##############################################################################
529              
530             =head2 reclaim
531              
532             Class::BuildMethods->reclaim; # assumes current package
533             Class::BuildMethods->reclaim($package);
534            
535             Like C but more final. Removes any values set for methods, any default
536             values and pretty much any trace of a given module from this package. It
537             B remove the methods. Any attempt to use the the autogenerated
538             methods after this method is called is not guaranteed.
539              
540             =cut
541              
542             sub reclaim {
543 2     2 1 1686 my ( $class, $package ) = @_;
544 2 100       12 unless ( defined $package ) {
545 1         4 ($package) = caller();
546             }
547 2 50       8 return unless $methods_for{$package};
548 2         3 my @methods = @{ $methods_for{$package} };
  2         6  
549 2         6 delete $methods_for{$package};
550 2         3 delete $class_data_for{$package};
551 2         3 delete $no_destroy_for{$package};
552 2         7 delete @default_for{@methods};
553 2         5 delete @value_for{@methods};
554 2         8 return scalar @methods;
555             }
556              
557             ##############################################################################
558              
559             =head2 packages
560              
561             my @packages = Class::BuildMethods->packages;
562              
563             Returns a sorted list of packages for which methods have been built. If
564             C has been called for a package, this method will not return that
565             package. This is generally useful if you need to do a global code cleanup
566             from a remote package:
567              
568             foreach my $package (Class::BuildMethods->packages) {
569             Class::BuildMethods->reclaim($package);
570             }
571             # then whatever teardown you need
572              
573             In reality, you probably will never need this method.
574              
575             =cut
576              
577             sub packages {
578 2     2 1 1347 return sort keys %methods_for;
579             }
580              
581             ##############################################################################
582              
583             =head1 DEBUGGING
584              
585             =head2 dump
586              
587             my $hash_ref = Class::BuildMethods->dump($object);
588              
589             The C method returns a hashref. The keys are the method names and the
590             values are whatever they are currently set to. This method is provided to
591             ease debugging as merely dumping an inside-out object generally does not
592             return its structure.
593              
594             =cut
595              
596             sub dump {
597 1     1 1 3 my ( $class, $object ) = @_;
598 1         5 my @methods = $class->_find_methods($object);
599 1         4 my $instance = _refaddr $object;
600              
601 1         2 my %dump_for;
602 1 50       4 if (@methods) {
603 1         2 foreach my $method (@methods) {
604 2         9 my ($attribute) = $method =~ /^.*::([^:]+)$/;
605 2         10 $dump_for{$attribute} = $value_for{$method}{$instance};
606             }
607             }
608 1         5 return \%dump_for;
609             }
610              
611             =head1 CAVEATS
612              
613             Some people will not be happy that if they need to store an array or a hash
614             they must pass them by reference as each generated method expects a single
615             value to be passed in when used as a "setter". This is because this module is
616             designed to be I. It's very lightweight and very fast.
617              
618             Note that you cannot automatically serialize the data herein. The reason for
619             this is fairly simple: you can add extra attributes with this module, but
620             since it makes no implementation assumptions, it doesn't know how your code
621             stores its data. If you need to serialize your objects, use the C<&dump>
622             method to fetch the attribute values from C and handle
623             the serialization manually.
624              
625             When in C is invoked, class data is not removed because other
626             instances may have that data.
627              
628             =head1 AUTHOR
629              
630             Curtis "Ovid" Poe, C<< >>
631              
632             =head1 ACKNOWLEDGEMENTS
633              
634             Thanks to Kineticode, Inc. for supporting development of this package.
635              
636             =head1 BUGS
637              
638             Please report any bugs or feature requests to
639             C, or through the web interface at
640             L.
641             I will be notified, and then you'll automatically be notified of progress on
642             your bug as I make changes.
643              
644             =head1 COPYRIGHT & LICENSE
645              
646             Copyright 2005 Curtis "Ovid" Poe, all rights reserved.
647              
648             This program is free software; you can redistribute it and/or modify it
649             under the same terms as Perl itself.
650              
651             =cut
652              
653             1; # End of Class::BuildMethods