File Coverage

blib/lib/Class/Delegate.pm
Criterion Covered Total %
statement 57 62 91.9
branch 24 32 75.0
condition 1 3 33.3
subroutine 10 11 90.9
pod 3 3 100.0
total 95 111 85.5


line stmt bran cond sub pod time code
1             package Class::Delegate;
2              
3             =head1 NAME
4              
5             Class::Delegate - easy-to-use implementation of object delegation.
6              
7             =head1 SYNOPSIS
8              
9             require Class::Delegate;
10             @ISA = 'Class::Delegate';
11              
12             $self->add_delegate('some_name', $a);
13             $self->add_delegate($b);
14             $self->do_something_that_b_knows_how_to_do();
15             $self->do_something_that_a_knows_how_to_do();
16              
17             =head1 DESCRIPTION
18              
19             This class provides transparent support for object delegation. For more
20             information on delegation, see B by Erich Gamma, et al.
21              
22             =cut
23              
24 1     1   599 use strict;
  1         2  
  1         37  
25 1     1   6 use vars qw($VERSION $AUTOLOAD);
  1         1  
  1         864  
26              
27              
28             $VERSION = '0.06';
29              
30              
31             my $Debug = 0;
32 0     0   0 sub _debug { $Debug = shift }
33 3 50   3   10 sub _log { print STDERR @_ if $Debug }
34              
35              
36              
37             =head1 METHODS
38              
39             =over 4
40              
41             =item add_delegate([ $name, ] $delegate)
42              
43             Assigns a delegate to your object. Any delegate can be named or unnamed
44             (see the delegate() method for information on the usefulness of naming a
45             delegate).
46              
47             =cut
48              
49             sub add_delegate
50             {
51 3     3 1 96 my ($self, @delegates) = @_;
52              
53 3         11 _prepare($self);
54              
55             # Each entry is either a pair, or just an .
56             # If it is a lone object, then we name it after its stringified value.
57             # NOTE: If you don't specify a name for a delegate, then there is no
58             # documented API for accessing said delegate!
59 3         9 while (@delegates) {
60 3 100       21 my $name = ref($delegates[0]) ? "$delegates[0]" : shift @delegates;
61 3         5 my $object = shift @delegates;
62              
63 3 50 33     28 die "Argument `$object' to add_delegate() is not an object\n"
64             unless (ref($object) and $object =~ /=/);
65              
66 3         9 $$self{__delegates}{$name} = $object;
67              
68             # If the delegate wants to know who its owner is, then tell it.
69 3 100       57 $object->set_owner($self) if $object->can('set_owner');
70             }
71              
72 3         16 return $self;
73             }
74              
75              
76             =item resolve($methodname, $delegatename)
77              
78             Declare that calls to $methodname should be dispatched to the delegate
79             named $delegatename. This is primarily for resolving ambiguities when
80             an object may have multiple delegates, more than one of which implements
81             the same method.
82              
83             =cut
84              
85             sub resolve
86             {
87 1     1 1 15 my ($self, $methodname, $delegatename) = @_;
88 1         13 my $delegate = $self->delegate($delegatename);
89              
90 1 50       6 die "No delegate named `$delegatename' found\n" unless defined $delegate;
91              
92 1         3 $$self{__delegation_cache}{$methodname} = $delegate;
93              
94 1         4 return $self;
95             }
96              
97              
98             =item delegate($name)
99              
100             This method returns the delegate named $name, or the empty list if there is
101             no such delegate.
102              
103             =cut
104              
105             sub delegate
106             {
107 3     3 1 86 my ($self, $name) = @_;
108              
109 3 50       11 if (defined $$self{__delegates}{$name}) {
110 3         15 return $$self{__delegates}{$name};
111             } else {
112 0         0 return;
113             }
114             }
115              
116              
117             # This method is currently for internal use only:
118 1     1   20 sub _delegates { return %{ $_[0]->{__delegates} } }
  1         16  
119              
120              
121             # Assure that this object has the necessary structure to handle delegation.
122             sub _prepare
123             {
124 3     3   5 my ($self) = @_;
125              
126 3 50       10 die "$self is not an object" unless ref($self);
127 3 50       20 die "$self is not a hash reference" unless $self =~ /=HASH\(/;
128              
129 3 100       14 $$self{__delegates} = {} unless defined $$self{__delegates};
130 3 100       11 $$self{__delegation_cache} = {} unless defined $$self{__delegation_cache};
131              
132 3         7 return $self;
133             }
134              
135              
136             # This subroutine does most of the work. It catches an attempted subroutine
137             # call, and looks at all the delegates for the object to make sure that there
138             # is exactly one delegate that implements the given method.
139             sub AUTOLOAD
140             {
141 3     3   116 my ($self, @args) = @_;
142 3         8 my $class = ref $self;
143 3         22 my ($method) = ($AUTOLOAD =~ /([^:]+)$/);
144 3         10 my ($pack,$file,$line) = caller;
145            
146 3         15 _log("AUTOLOAD is `$AUTOLOAD', class is `$class', method is `$method'\n");
147              
148             # If there's a cache miss:
149 3 100       12 if (!defined $$self{__delegation_cache}{$method}) {
150 2         3 my @targets;
151              
152 2         10 foreach my $delegate (values %{ $$self{__delegates} }) {
  2         9  
153 1     1   6 no strict 'refs';
  1         4  
  1         244  
154 6         15 my $public = ref($delegate) . '::PUBLIC';
155              
156             # Look in @Somepackage::PUBLIC, if it exists . . .
157 6 100       8 if (@{ $public }) {
  6         27  
158 2         4 foreach my $public_method (@{ $public }) {
  2         7  
159 2 50       12 if ($public_method eq $method) {
160 0         0 push @targets, ref($delegate);
161 0         0 last;
162             }
163             }
164             # . . . else trundle through all of Somepackage's methods.
165             } else {
166 4 100       26 push @targets, $delegate if $delegate->can($method);
167             }
168             }
169              
170 2 50       10 if (@targets == 0) {
    100          
171 0         0 die "Unresolvable call to `$method' ",
172             "from class `$class' ",
173             "in `$file' at `$line'\n";
174             } elsif (@targets == 1) {
175 1         4 $$self{__delegation_cache}{$method} = $targets[0];
176             } else {
177 1         4 my @which = map { ref($_) . "\n" } @targets;
  2         9  
178              
179 1         14 die "Ambiguous call to $class->$method()",
180             "implemented in `$file' at `$line' as:\n",
181             @which;
182             }
183             }
184              
185             # If we've gotten here, then the cache is primed:
186 2         10 return $$self{__delegation_cache}{$method}->$method(@args);
187             }
188              
189              
190             1;
191              
192              
193             __END__