File Coverage

blib/lib/Class/ClassDecorator.pm
Criterion Covered Total %
statement 60 65 92.3
branch 11 14 78.5
condition 1 3 33.3
subroutine 11 11 100.0
pod 0 2 0.0
total 83 95 87.3


line stmt bran cond sub pod time code
1             package Class::ClassDecorator;
2              
3 3     3   2156 use strict;
  3         150  
  3         118  
4              
5 3     3   17 use vars qw($VERSION);
  3         5  
  3         189  
6              
7             $VERSION = 0.02;
8              
9 3     3   4418 use NEXT;
  3         15975  
  3         406  
10              
11             # Given a set of classes like Foo::Base, Foo::Bar, and Foo::Baz, we
12             # end up with a hierarchy like this:
13             #
14             # Foo::Baz Foo::Bar Foo::Base
15             # \ | /
16             # \ | /
17             # MadeBy::Class::ClassDecorator::Class000000000
18             #
19             # As long as all the top classes (excluding Foo::Base) use NEXT::
20             # instead of SUPER::, it works.
21             #
22              
23             my %Cache;
24             sub decorate
25             {
26 8 50   8 0 10003 unless ( @_ > 1 )
27             {
28 0         0 require Carp;
29 0         0 Carp::croak( "Cannot call decorate() function with only a single class name.\n" );
30             }
31              
32             # class names should never have spaces in them
33 8         26 my $key = join ' ', @_;
34              
35 8 100       33 return $Cache{decorate}{$key} if $Cache{decorate}{$key};
36              
37 6         16 my $name = _make_name();
38              
39             {
40 3     3   29 no strict 'refs';
  3         5  
  3         731  
  6         11  
41 6         12 @{"$name\::ISA"} = ( reverse @_ );
  6         191  
42              
43 6         14 *{"$name\::class_decorator_class"} = sub () { 1 };
  6         37  
44             }
45              
46 6         29 return $Cache{decorate}{$key} = $name;
47             }
48              
49             sub hierarchy
50             {
51 8 50   8 0 13561 unless (@_)
52             {
53 0         0 require Carp;
54 0         0 Carp::croak( "Cannot call hierarchy() function with only a single class name.\n" );
55             }
56              
57 8         28 my $key = join ' ', @_;
58              
59 8 100       36 return $Cache{hierarchy}{$key} if $Cache{hierarchy}{$key};
60              
61 6         10 my @parents;
62             my @children;
63 0         0 my $last;
64 6         18 foreach my $class (@_)
65             {
66 18         37 my $name = _make_name();
67              
68 18 100       55 my @isa = ( $class, ( $last ? $last : () ) );
69              
70             {
71 3     3   17 no strict 'refs';
  3         6  
  3         706  
  18         20  
72 18         21 @{"$name\::ISA"} = @isa;
  18         440  
73             }
74              
75 18         52 $last = $name;
76             }
77              
78 6         30 return $Cache{hierarchy}{$key} = $last;
79             }
80              
81             my $Base = 'MadeBy::Class::ClassDecorator::Class';
82             my $Num = 0;
83              
84 24     24   103 sub _make_name { sprintf( '%s%09d', $Base, $Num++ ) }
85              
86             package super;
87              
88             sub AUTOLOAD
89             {
90 6     6   89 my $caller_class = caller();
91              
92 6   33     32 my $descendant_class = ref $_[0] || $_[0];
93              
94 6         7 my $class = $descendant_class;
95              
96 6         7 my $class_to_call;
97              
98             # I'm too lazy to write this in a saner way. Basically we are
99             # going up the inheritance tree looking at the "right" side
100 6         8 while (1)
101             {
102             {
103 3     3   16 no strict 'refs';
  3         7  
  3         334  
  9         10  
104 9         11 $class_to_call = ${"$class\::ISA"}[1];
  9         36  
105             }
106              
107 9 50       35 die "Cannot use super for classes not created by Class::ClassDecorator\n"
108             unless $class_to_call =~ /^MadeBy::Class::ClassDecorator/;
109              
110 9 100       97 if ( $class_to_call->isa($caller_class) )
111             {
112 3     3   16 no strict 'refs';
  3         5  
  3         498  
113 3         4 $class = ${"$class\::ISA"}[1];
  3         11  
114              
115 3         6 next;
116             }
117              
118 6         10 last;
119             }
120              
121 6         25 my $meth = join '::', $class_to_call, (split /::/, $super::AUTOLOAD)[1];
122              
123 6         630 return shift->$meth(@_);
124             }
125              
126              
127             1;
128              
129             __END__