File Coverage

blib/lib/Class/Role.pm
Criterion Covered Total %
statement 71 108 65.7
branch 20 42 47.6
condition 5 18 27.7
subroutine 8 9 88.8
pod n/a
total 104 177 58.7


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