File Coverage

lib/Class/Builder.pm
Criterion Covered Total %
statement 148 323 45.8
branch 31 102 30.3
condition 9 54 16.6
subroutine 23 64 35.9
pod 11 16 68.7
total 222 559 39.7


line stmt bran cond sub pod time code
1             # ======================================================================== #
2             # Class::Builder -- auto-generator of class accessors/special methods #
3             # ======================================================================== #
4             # Author: Wei, Huang, Weitop Corp., 2003-8-30
5             # $Revision: 1.10 $ - $Date: 2003/10/05 07:28:03 $
6             # (c) Copyright: 2003 - 2006.
7            
8             # *WARNNING* ALPHA RELEASE
9             # This software is not ready for production use.
10            
11             # *WARNING* TOTALLY NO WARRANTY
12             # This module is free software, you can use/modify or distribute
13             # it as the term of perlself. The software comes without any
14             # warranty of any type, use it at you own risk.
15            
16             package Class::Builder;
17             our $VERSION = do { my @r = (q$Revision: 1.10 $ =~ /\d+/g); sprintf "%d."."%03d" x $#r, @r };;
18            
19 1     1   89833 use 5.006001;
  1         4  
  1         40  
20 1     1   5 use strict;
  1         2  
  1         46  
21 1     1   6 use warnings;
  1         7  
  1         43  
22 1     1   2216 use integer;
  1         10  
  1         5  
23 1     1   897 use autouse 'Carp' => qw(carp croak);
  1         952  
  1         6  
24 1     1   137 use Exporter;
  1         2  
  1         41  
25 1     1   5352 use Storable qw(dclone);
  1         7715  
  1         127  
26 1     1   14 use base qw{Exporter};
  1         4  
  1         3132  
27             our @EXPORT = qw{struct};
28            
29             # ========================= | PACKAGE VARIABLES |
30             $Class::Builder::current_class = '';
31             %Class::Builder::defaults = ();
32             %Class::Builder::initializers = ();
33             %Class::Builder::classdata = ();
34             # ===============================================
35            
36             # ---------------- | Public Methods |
37             sub known_types{
38 3     3 1 9 return qw(string number boolean counter classdata hashref arrayref);
39             }
40            
41             sub struct{
42             # to inherite from Class::Builder
43             # overridden this functions with
44             # same contents in your sub class
45 0     0 1 0 __PACKAGE__->_struct(@_);
46             }
47            
48             sub import {
49             # two type of arguments possible here:
50             # use Class::Builder { args };
51             # use Class::Builder ( args );
52 3     3   571 my $self = shift;
53 3         18 my $class = (caller(0))[0];
54 3 50 33     24 if(((scalar @_) == 1) and ((ref $_[0]) eq 'HASH')){
    0          
55 3         12 $self->setup($_[0], $class);
56             }elsif((scalar @_) > 1){
57 0         0 my %hash = (@_);
58 0         0 $self->setup(\%hash, $class);
59             }else{ ; }
60 3         10028 $self->export_to_level( 1, $self, @EXPORT );
61             }
62            
63             # ---------------- | Private Methods |
64             sub _struct{
65             # three types of arguments are considerable:
66             # 1. struct { args } # implicit class name, not recommended
67             # 2. struct class => { args }, class2 => { args };
68             # 3. struct [class => {args}, class2 => {args}];
69             # same as above, with little perfomance improvement
70 0     0   0 my $builder = shift;
71 0         0 my @args = @_;
72 0 0 0     0 if((@args == 1) and (ref $args[0]) eq 'HASH'){
73 0   0     0 my $class ||= (caller(1))[0];
74 0         0 $builder->setup($args[0], $class);
75             }else{
76 0         0 my $aref;
77 0 0 0     0 if((@args == 1) and (ref $args[0]) eq 'ARRAY'){
78 0         0 $aref = shift @args;
79 0         0 }else{ $aref = \@args; }
80 0 0       0 croak "wrong number of arguments for struct.\n"
81             if(@$aref % 2);
82            
83 0         0 my $class;
84 0         0 while ($class = shift @$aref){
85 0         0 $builder->setup((shift @$aref), $class);
86             }
87 0         0 return scalar @$aref;
88             }
89 0         0 return 1;
90             }
91            
92             sub setup{
93             # for sub-classing: you may
94             # add you own extension here
95 3     3 1 7 shift->_setup(@_);
96             }
97            
98             sub _setup{
99             # typical args hash:
100             # {
101             # '-methods' => {
102             # =>
103             # },
104             # => {
105             # => ,
106             # final => 1,
107             # forward =>
108             # }
109             # }
110             # : number, string (or any scalar including references)
111             # boolean, counter, , classdata, hashref, arrayref
112             # : initializer, constructor, group
113             # abstract, dumper, clone,
114 3     3   5 my ($builder, $arg, $class) = @_;
115 3   33     7 $class ||= (caller(1))[0];
116            
117             # initialize class variables:
118 3         4 $Class::Builder::current_class = $class;
119 3   50     14 $Class::Builder::defaults{$class} ||={};
120 3   50     12 $Class::Builder::initializers{$class} ||= [];
121            
122             # a loosing way for classdata inheritage:
123 3         5 my $inherite = {};
124 3 100       32 $inherite = $class->__classdata() if($class->can('__classdata'));
125 3         6 %{$Class::Builder::classdata{$class}} = %$inherite;
  3         7  
126            
127             # get definations about special methods first
128 3         5 my $special_methods = {};
129 3 100       8 if(exists $arg->{-methods}){
130 1         3 $special_methods = $arg->{-methods};
131 1         2 delete $arg->{-methods};
132            
133 1 50       4 if(exists $special_methods->{initializer}){
134 1         2 my $list = $special_methods->{initializer};
135 1 50       4 $Class::Builder::initializers{$class}
136             = (ref $list) ? $list : [$list];
137            
138 1         2 delete $special_methods->{initializer};
139             }
140             }
141 3 100 66     33 $special_methods->{constructor} = 'new'
142             unless($special_methods->{constructor} or $class->can('new'));
143            
144             # range field informations,
145 3         7 my $field_methods = {};
146 3         4 my $any_classdata = 0;
147             {
148 3         4 my %known = map {$_, 1} $builder->known_types();
  3         7  
  21         44  
149            
150 3         16 while(my ($field, $def) = each %$arg){
151 6         13 my $fieldarg = {name=> $field, final => 0};
152 6         10 foreach (qw(final forward)){
153 12 50       30 next unless exists $def->{$_};
154 0         0 $fieldarg->{$_} = $def->{$_};
155 0         0 delete $def->{$_};
156             }
157 6 50       14 croak "santax error for field $field, check it again."
158             unless((scalar keys %$def) == 1);
159 6         15 ($fieldarg->{type}, $fieldarg->{default}) = each %$def;
160            
161 6 50 33     40 croak "can not set counter fields to be final, for ", $fieldarg->{name},
162             ".\n" if($fieldarg->{final} and ($fieldarg->{type} eq 'counter'));
163            
164 6         8 my $type = $fieldarg->{type};
165 6 50       12 unless($known{$type}){
166 0         0 $fieldarg->{type} = 'object';
167             # 1. Type => ['constructor', 'arg1', 'arg2']
168             # 2. Type => 'constructor'
169             # 3. Type => created_object
170 0         0 $fieldarg->{class} = $type;
171 0         0 my $default = $fieldarg->{default};
172 0 0 0     0 if($default and !(ref $default)){
    0          
173 0         0 $fieldarg->{default} = $type->$default();
174             }elsif((ref $default) eq 'ARRAY'){
175 0         0 my $constructor = shift @$default;
176 0         0 $fieldarg->{default} = $type->$constructor(@$default);
177             }else{;}
178             }
179            
180             # class data default goes here
181 6 100       14 if($fieldarg->{type} eq 'classdata'){
182 3         4 ++ $any_classdata;
183 3         5 $Class::Builder::classdata{$class}->{$field} = $fieldarg->{default};
184             }else{
185 3         6 $Class::Builder::defaults{$class}->{$field} = $fieldarg->{default}
186             }
187            
188 6         25 $field_methods->{$field} = $fieldarg;
189             }
190             }
191            
192             # set up fields methods, localize `no strict'
193             {
194 3         4 my %methods = ();
  3         4  
195 3         7 foreach my $name (keys %$field_methods){
196 6         11 my $type = $field_methods->{$name}->{type};
197 6         21 %methods = (%methods, $builder->$type( $field_methods->{$name} ));
198             }
199            
200 1     1   13 no strict 'refs';
  1         3  
  1         246  
201 2     1   7 *{"$class"."::"."__classdata"} = sub {$Class::Builder::classdata{$class}}
  1         4  
202 3 100       18 if($any_classdata);
203 3         9 while (my ($name, $code) = each %methods){
204 8         24 *{"$class"."::"."$name"} = $code;
  8         43  
205             }
206             }
207            
208             # setup methods for special methods (after defaults set):
209             {
210 3         3 my %methods = map {$builder->$_( $special_methods->{$_} )}
  3         7  
  2         6  
211             (keys %$special_methods);
212             # $_ is a element of a subset of
213             # qw[constructor, abstract, dumper, clone]
214 1     1   8 no strict 'refs';
  1         3  
  1         3069  
215 3         9 while (my ($name, $code) = each %methods){
216 2         3 *{"$class"."::"."$name"} = $code;
  2         9  
217             }
218            
219             # if any of a field declared, add a function to clear it.
220             # i hope this will make some perfomance improvement, if any,
221             # than the x/clear_x approach of MethodMaker.
222 3 50       7 if(scalar keys %$field_methods){
223 3         12 *{"${class}"."::"."clear"} = sub {
224 0     0   0 my $self = shift; foreach (@_){$self->{$_} = undef}; undef;
  0         0  
  0         0  
  0         0  
225 3         9 };
226             # in addtion, you can get a list of
227 3         16 *{"${class}"."::"."get"} = sub {
228 0     0   0 my $self = shift; my $valuelist = [];
  0         0  
229 0         0 foreach (@_){push @$valuelist, $self->$_; }
  0         0  
230 0         0 return @$valuelist;
231 3         7 };
232             }
233             }
234            
235 3         13 return 1;
236             }
237            
238             # -------------------------------------- | Field Methods Builder |
239             sub string{
240             # args name, final, type, default
241 2     2 1 2 shift;
242 2         3 my $args = shift;
243 2         3 my $name = $args->{name};
244 2 50   0   6 return ($name, sub { shift->{$name}; }) if($args->{final});
  0         0  
245            
246             return ($name, sub : lvalue {
247 7     7   24 my $self = shift;
248 7 100       26 $self->{$name} = $_[0] if(@_ == 1);
249 7         41 $self->{$name};
250 2         15 });
251             }
252            
253             sub number{
254             # implemented nothing special :-)
255 0     0 1 0 shift->string(@_);
256             }
257            
258             sub arrayref{
259 0     0 1 0 shift;
260 0         0 my ($arg) = @_;
261 0         0 my $class = $Class::Builder::current_class;
262 0         0 my $defaults = $Class::Builder::defaults{$class};
263 0         0 my $name = $arg->{name};
264             return (
265             $name,
266             sub {
267 0     0   0 my $self = shift;
268 0 0 0     0 $self->{$name} = $_[0] if(@_ == 1 and (ref $_[0] eq 'ARRAY'));
269 0 0       0 wantarray ? @{$self->{$name}} : $self->{$name};
  0         0  
270             },
271             "${name}_push",
272             sub {
273 0     0   0 my $self = shift;
274 0 0       0 $self->{$name} = [] unless((ref $self->{$name}) eq 'ARRAY');
275 0         0 push @{$self->{$name}}, @_;
  0         0  
276             },
277             "${name}_pop",
278             sub {
279 0     0   0 my ($self, $new) = @_;
280 0         0 pop @{$self->{$name}};
  0         0  
281             },
282             "${name}_shift",
283             sub {
284 0     0   0 my $self = shift;
285 0         0 shift @{$self->{$name}};
  0         0  
286             },
287             "${name}_unshift",
288             sub {
289 0     0   0 my $self = shift;
290 0 0       0 $self->{$name} = [] unless((ref $self->{$name}) eq 'ARRAY');
291 0         0 unshift @{$self->{$name}}, @_;
  0         0  
292             },
293             "${name}_count",
294             sub {
295 0     0   0 my $self = shift;
296 0 0       0 return exists $self->{$name} ? scalar @{$self->{$name}} : 0;
  0         0  
297             },
298             "${name}_splice",
299             sub {
300 0     0   0 my ($self, $offset, $len, @list) = @_;
301 0         0 splice(@{$self->{$name}}, $offset, $len, @list);
  0         0  
302             }
303 0         0 );
304             }
305            
306             sub hashref{
307 0     0 1 0 shift;
308 0         0 my ($arg) = @_;
309 0         0 my $class = $Class::Builder::current_class;
310 0         0 my $defaults = $Class::Builder::defaults{$class};
311 0         0 my $name = $arg->{name};
312             return (
313             $name,
314             sub {
315 0     0   0 my $self = shift;
316 0 0       0 $self->{$name} = {} unless((ref $self->{$name}) eq 'HASH');
317 0 0 0     0 if(@_ == 1 and (ref $_[0] eq 'HASH')){
318 0         0 $self->{$name} = $_[0];
319 0         0 return $self->{$name};
320             }
321 0 0       0 return @{$self->{$name}}{@_} if(scalar @_);
  0         0  
322 0         0 $self->{$name};
323             },
324             "${name}_keys",
325             sub {
326 0     0   0 my $self = shift;
327 0 0       0 $self->{$name} = {} unless((ref $self->{$name}) eq 'HASH');
328 0         0 keys %{$self->{$name}};
  0         0  
329             },
330             "${name}_values",
331             sub {
332 0     0   0 my $self = shift;
333 0 0       0 $self->{$name} = {} unless((ref $self->{$name}) eq 'HASH');
334 0         0 values %{$self->{$name}};
  0         0  
335             },
336             "${name}_exists",
337             sub {
338 0     0   0 my $self = shift;
339 0         0 my $key = shift;
340             return
341 0   0     0 exists $self->{$name} && exists $self->{$name}->{$key};
342             },
343             "${name}_delete",
344             sub {
345 0     0   0 my ($self, @keys) = @_;
346 0         0 delete @{$self->{$name}}{@keys};
  0         0  
347             },
348 0         0 );
349             }
350            
351             sub object{
352 0     0 1 0 shift;
353 0         0 my $arg = shift;
354 0         0 my $name = $arg->{name};
355 0         0 my $forward = [];
356 0 0       0 $forward = (ref $arg->{forward}) ? $arg->{forward}
    0          
357             : [$arg->{forward}] if($arg->{forward});
358 0         0 my %results = ();
359             $results{$name} = sub : lvalue {
360 0     0   0 my $self = shift;
361 0 0 0     0 $self->{$name} = $_[0] if(@_ == 1 and ref $_[0]);
362 0         0 $self->{$name};
363 0         0 };
364            
365 0         0 foreach my $meth (@$forward){
366             $results{$meth} =
367             sub {
368 0     0   0 my ($self, @args) = @_;
369 0         0 $self->$name()->$meth(@args);
370 0         0 };
371             }
372            
373 0         0 return %results;
374             }
375            
376             sub boolean{
377             # args name, final, type, default
378 1     1 1 1 shift;
379 1         3 my $args = shift;
380 1         1 my $class = $Class::Builder::current_class;
381 1         2 my $defaults = $Class::Builder::defaults{$class};
382 1         1 my $name = $args->{name};
383            
384 1 50   0   5 return ($name, sub { shift->{$name}; }) if($args->{final});
  0         0  
385            
386             return (
387             $name,
388             sub : lvalue {
389 1     1   2 my $self = shift;
390 1 0       5 if(@_ == 1){ $self->{$name} = ($_[0]) ? 1 : 0; }
  0 50       0  
391 1         6 $self->{$name};
392             },
393             "${name}_rev",
394             sub {
395 0     0   0 my $self = shift;
396 0 0       0 $self->{$name} = ($self->{$name}) ? 0 : 1;
397             },
398             "${name}_reset",
399             sub {
400 0     0   0 my $self = shift;
401 0   0     0 my $val = $defaults->{$name} || 0;
402 0         0 $self->{$name} = $val;
403             },
404 1         23 );
405             }
406            
407             sub counter{
408 0     0 1 0 shift;
409 0         0 my ($arg) = @_;
410 0         0 my $class = $Class::Builder::current_class;
411 0         0 my $defaults = $Class::Builder::defaults{$class};
412 0         0 my $name = $arg->{name};
413             return (
414             $name,
415             sub {
416 0     0   0 my $self = shift;
417 0 0 0     0 $self->{$name} = $_[0] if(@_ == 1 and ref $_[0]);
418 0         0 $self->{$name};
419             },
420             "${name}_add",
421             sub {
422 0     0   0 my ($self, $new) = @_;
423 0   0     0 $new ||= 1;
424 0         0 $self->{$name} += $new;
425             },
426             "${name}_remove",
427             sub {
428 0     0   0 my ($self, $new) = @_;
429 0   0     0 $new ||= 1;
430 0         0 $self->{$name} -= $new;
431             },
432             "${name}_reset",
433             sub {
434 0     0   0 my $self = shift;
435 0   0     0 my $val = $defaults->{$name} || 0;
436 0         0 $self->{$name} = $val;
437             },
438             "${name}_set",
439             sub {
440 0     0   0 my $self = shift;
441 0         0 $self->{$name} = int(shift);
442             }
443 0         0 );
444             }
445            
446             sub classdata{
447 3     3 0 2 shift;
448 3         4 my $args = shift;
449 3         3 my $class = $Class::Builder::current_class;
450 3         6 my $classdata = $Class::Builder::classdata{$class};
451 3         5 my $name = $args->{name};
452 3 50   0   9 return ($name, sub { $classdata->{$name}; }) if($args->{final});
  0         0  
453             return ($name, sub : lvalue {
454 5     5   11 shift;
455 5         7 my $arg = shift;
456 5 100       15 defined $arg and $classdata->{$name} = $arg;
457 5         129 $classdata->{$name};
458 3         16 });
459             }
460            
461             # -------------------------------------- | Special Methods Builder |
462             sub constructor{
463 2     2 1 3 shift;
464 2         4 my $arg = shift;
465 2 50       6 my @list = ref($arg) eq 'ARRAY' ? @$arg : ($arg);
466 2         3 my $class = $Class::Builder::current_class;
467 2         3 my $initializers = $Class::Builder::initializers{$class};
468 2         3 my $defaults = $Class::Builder::defaults{$class};
469            
470             map {
471 2         3 $_,
472             sub {
473 4 50   4   199610 my $class = (ref $_[0]) ? ref shift : shift;
474 4         9 my $self = {};
475 4         283 $self = dclone($defaults); my @args = @_;
  4         10  
476 4         12 bless $self, $class;
477            
478 4         6 my $hashref = {};
479 4 100 66     30 if($args[0] and (ref $args[0] eq 'HASH')){
480 2         5 $hashref = shift @args;
481             }else{
482 2 50       11 %$hashref = @args unless(scalar @$initializers);
483             }
484 4         12 map {$_, $self->$_($hashref->{$_})} (keys %$hashref);
  2         10  
485 4         8 map {$_, $self->$_(@args)} @$initializers;
  2         11  
486 4         16 return $self;
487             }
488 2         14 } @list;
489             }
490            
491             sub abstract {
492             # implement abstract methods:
493 0     0 0   shift;
494 0           my $arg = shift;
495 0           my $class = $Class::Builder::current_class;
496 0 0         my @list = ref($arg) eq 'ARRAY' ? @$arg : ($arg);
497 0           map {
498 0           my $name = $_;
499             ($name,
500             sub {
501 0     0     my ($self) = @_;
502 0           my $calling_class = ref $self;
503 0           die "[ABSTRACT METHOD] you can not call ${calling_class}::$name ",
504             "(defined in class '$class') without overridden.";
505             }
506             )
507 0           } @list;
508             }
509            
510             sub group{
511 0     0 0   shift;
512 0           my $arg = shift;
513 0 0         croak "the argument for a group must be a reference."
514             unless(ref $arg);
515 0           my $hashref = {};
516 0           my %results = ();
517 0 0         if(ref $arg eq 'ARRAY'){
518 0           %$hashref = @$arg;
519             }else{
520 0           $hashref = $arg;
521             }
522 0           while (my ($group, $garg) = each %$hashref){
523 0 0   0     $results{$group} = sub {wantarray ? @$garg : $garg};
  0            
524             }
525 0           return %results;
526             }
527            
528             sub dumper{
529 0     0 0   shift;
530 0           my $arg = shift;
531 0 0         my @list = ref($arg) eq 'ARRAY' ? @$arg : ($arg);
532             map {
533 0           $_,
534 0     0     sub { require Data::Dumper; return Data::Dumper::Dumper(shift); }
  0            
535 0           } @list;
536             }
537            
538             sub clone{
539 0     0 0   shift;
540 0           my $arg = shift;
541 0 0         my @list = ref($arg) eq 'ARRAY' ? @$arg : ($arg);
542             map {
543 0           $_,
544 0     0     sub { require Storable; return Storable::dclone(shift); }
  0            
545 0           } @list;
546             }
547            
548             1;
549            
550             __END__