File Coverage

blib/lib/Class/Role.pm
Criterion Covered Total %
statement 63 100 63.0
branch 20 42 47.6
condition 5 18 27.7
subroutine 5 6 83.3
pod n/a
total 93 166 56.0


line stmt bran cond sub pod time code
1              
2             package Class::Role;
3              
4 1     1   22071 use 5.006;
  1         5  
  1         44  
5 1     1   5 use Carp;
  1         2  
  1         1470  
6              
7             our $VERSION = 0.03;
8              
9             my %valid_option_keys = (
10             -excludes => 1,
11             -conflict => 1,
12             );
13             my %valid_param_keys = (
14             -methods => 1,
15             );
16              
17             sub import {
18 5     5   1315 shift; # Package name
19            
20 5 100 66     27 if ($_[0] && $_[0] !~ /^-/) { # If you don't want a seperate
21             # file for each role
22 3         5 goto &{"$_[0]::import"};
  3         14  
23             }
24            
25 2         4 my %param = @_;
26              
27 2         9 for (keys %param) {
28 0 0       0 croak "Unknown option ($_) for Class::Role" unless $valid_param_keys{$_};
29             }
30            
31 2         5 my $package = caller;
32            
33 2         4 ${"$package\::__IS_ROLE__"} = 1;
  2         9  
34            
35 2         177 *{"$package\::import"} = sub {
36 3     3   3 shift; # Package name
37 3         8 my %options = @_;
38              
39 3         8 for (keys %options) {
40 1 50       11 croak "Unknown option ($_) for Class::Role" unless $valid_option_keys{$_};
41             }
42            
43 3         4 my %exclude;
44 3 100 33     20 if (ref($options{-excludes}) eq 'ARRAY') {
    50          
    50          
45 1         1 %exclude = map { $_ => 1 } @{$options{-excludes}};
  1         4  
  1         3  
46             }
47             elsif ($options{-excludes} && !ref($options{-excludes})) {
48 0         0 %exclude = ($options{-excludes} => 1);
49             }
50             elsif ($options{-excludes}) {
51 0         0 croak "Unknown type for -excludes to Class::Role";
52             }
53            
54 3         4 my (@methods, @conflicts);
55 3         5 my $target = caller;
56 3         4 my $roles = \%{"$target\::__ROLES__"};
  3         15  
57            
58 3 50 33     19 if (ref($param{-methods}) eq 'ARRAY') {
    50          
    50          
59 0         0 @methods = @{$param{-methods}};
  0         0  
60             }
61             elsif ($param{-methods} && !ref $param{-methods}) {
62 0         0 @methods = ($param{-methods});
63             }
64             elsif ($param{-methods}) {
65 0         0 croak "Unknown type for -methods to Class::Role";
66             }
67             else {
68 23         84 @methods = grep {
69 26 100       54 $_ ne 'import' && *{"$package\::$_"}{CODE}
  3         20  
70 3         4 } keys %{"$package\::"};
71             }
72            
73 3         21 for my $method (@methods) {
74 16 100       39 next if $exclude{$method};
75            
76 15 50       18 if (grep { $_ ne $package } @{$roles->{$method}}) { # Conflict
  0         0  
  15         42  
77 0         0 push @{$roles->{$method}}, $package;
  0         0  
78 0         0 push @conflicts, $method;
79 0         0 next;
80             }
81              
82 15 50       19 if (*{"$target\::$method"}{CODE}) { # Override
  15         73  
83 0         0 next;
84             }
85            
86 15 100       15 if (${"$target\::__IS_ROLE__"}) {
  15         41  
87 5         8 *{"$target\::$method"} = \&{"$package\::$method"};
  5         18  
  5         11  
88             }
89             else {
90 10     0   800 eval <
  0         0  
  1         295  
  1         301  
  0         0  
  1         304  
  1         298  
  1         828  
  1         302  
  1         334  
91             package $target;
92             *$method = sub { &$package\::$method };
93             EOC
94             }
95 15         18 push @{$roles->{$method}}, $package;
  15         46  
96             }
97              
98 3 50       2120 if (@conflicts) {
99 0 0 0     0 if (!$options{-conflict} || lc $options{-conflict} eq 'die') {
    0 0        
    0          
    0          
100 0         0 my $msg;
101 0         0 for my $conflict (@conflicts) {
102 0         0 $msg .= "Role conflict in package $target:\n";
103 0         0 $msg .= " $_\::$conflict\n" for @{$roles->{$conflict}};
  0         0  
104             }
105 0         0 die $msg;
106             }
107             elsif (lc $options{-conflict} eq 'exclude') {
108 0         0 for (@conflicts) {
109 0         0 delete ${"$target\::"}{$_};
  0         0  
110 0         0 delete $roles->{$_};
111             }
112             }
113             elsif (lc $options{-conflict} eq 'keep') {
114             # Leave it alone
115 0         0 for (@conflicts) {
116 0         0 $roles->{$_} = [ $roles->{$_}[0] ];
117             }
118             }
119             elsif (lc $options{-conflict} eq 'replace' ||
120             lc $options{-conflict} eq 'mixin') {
121             # Overwrite
122 0         0 for (@conflicts) {
123 0 0       0 if (${"$target\::__IS_ROLE__"}) {
  0         0  
124 0         0 *{"$target\::$method"} = \&{"$package\::$method"};
  0         0  
  0         0  
125             }
126             else {
127 0         0 eval <
128             package $target;
129             *$method = sub { &$package\::$method };
130             EOC
131             }
132 0         0 $roles->{$_} = [ $package ];
133             }
134             }
135             else {
136 0         0 croak "Unknown option to -conflict ('$options{-conflict}') to Class::Role";
137             }
138             }
139 2         25 };
140             }
141              
142             package PARENT;
143              
144             sub AUTOLOAD {
145 2     2   25 my $name = our $AUTOLOAD;
146 2         13 $name =~ s/^.*:://; # Rip off everything except for the method name
147            
148 2         3 my $self = shift;
149 2 50 33     19 if (ref $self && !$builtin_types{ref $self}) { # Method call, probably
150 2         31 my $method = scalar(caller 1) . "::SUPER::$name";
151 2         33 $self->$method(@_); # XXX This stack frame shouldn't stick around...
152             }
153             }
154              
155             package Class::Role;
156              
157             1;
158              
159             =head1 NAME
160              
161             Class::Role - Support for the role object model
162              
163             =head1 SYNOPSIS
164              
165             package LongLiver;
166             use Class::Role; # This is a role
167            
168             sub profess {
169             my ($self) = @_;
170             print $self->name . " live a long time\n";
171             }
172              
173             package Donkey;
174             use Class::Role LongLiver; # Incorporates this role
175              
176             sub name {
177             return "Donkeys";
178             }
179              
180             sub new {
181             bless {} => shift;
182             }
183              
184             package main;
185             my $benjamin = Donkey->new;
186              
187             $benjamin->profess; # prints "Donkeys live a long time"
188              
189             =head1 DESCRIPTION
190              
191             C is an implementation of 'traits', as explained in this
192             paper:
193              
194             http://www.cse.ogi.edu/~black/publications/TR_CSE_02-012.pdf
195              
196             It's an object model similar to mixins, except saner. The module gets
197             its name from Larry's current name for a similar concept in Perl 6's
198             object model. In Perl 6, traits are a different thing entirely, and I
199             don't want to confuse anybody. C<:-)>
200              
201             Inheritance is [was designed to be] used as a way to extend an object in
202             its behavior, but it is often abused as a method of simple code reuse
203             (in the form of stateless, abstract classes). Roles fit this latter,
204             er, role better. A Role is a small, combinable piece of reusable code.
205              
206             Roles are stateless collections of methods that can be combined into a
207             class (or another role). These methods may call methods of the
208             combining object, not defined by the role itself. They are incorporated
209             in as if they were written directly into the combining class.
210              
211             To define a role, create a package with the methods you want the role to
212             provide, and C, as in the L.
213              
214             When creating a role, you may specify which methods you wish to export
215             to the combining class with the C<-methods> option. If the option is
216             not given, all methods (except for C) are exported.
217              
218             To combine a role, either C with the name of the role
219             as an argument, or just eg. C, if you have defined it in
220             C. Methods defined in the combining class override methods
221             in a combined role, however methods in the role override methods in any
222             base classes.
223              
224             When combining a role, there are several options you can give:
225              
226             =over
227              
228             =item C<-excludes>
229              
230             Give a method or arrayref of methods to exclude from combining. This is
231             the recommended way to deal with conflicts (see below).
232              
233             For instance,
234              
235             use Class::Role Farm, excludes => ['snowball'];
236              
237             =item C<-conflict>
238              
239             What to do if there's role conflict. One of the values:
240              
241             =over
242              
243             =item C<'die'>
244              
245             Exit with an error message. This is the default.
246              
247             =item C<'exclude'>
248              
249             Omit the offending method entirely. Usually this means you'll implement
250             it yourself.
251              
252             =item C<'keep'>
253              
254             "Keep" any existing role method defined; that is, use the first one.
255             Methods in the combining class still override.
256              
257             =item C<'replace'>
258              
259             Overwrite any existing role method defined; that is, use the last one.
260             Methods in the combining class still override.
261              
262             =item C<'mixin'>
263              
264             Synonym for C<'replace'>.
265              
266             =back
267              
268             It is recommended that you keep this the default.
269              
270             =back
271              
272             There is one small detail regarding methods behaving exactly as if they
273             were written directly into the combining class: C doesn't work
274             right. C would instead look in any base classes of the I,
275             not of the the combining class.
276              
277             To circumvent this, C provides the pseudopackage C,
278             which works exactly like C, except that it works correctly for
279             (and I for) roles. So, when you're writing a role, use C
280             instead of C.
281              
282             =head1 SEE ALSO
283              
284             C, C
285              
286             =head1 AUTHOR
287              
288             Luke Palmer, Eluke@luqui.orgE
289              
290             =head1 COPYRIGHT AND LICENSE
291              
292             Copyright 2003 by Luke Palmer
293              
294             This library is free software; you can redistribute it and/or modify
295             it under the same terms as Perl itself.
296              
297             =cut