adding method exclusion
[gitmo/Moose.git] / lib / Moose / Role.pm
CommitLineData
e185c027 1
2package Moose::Role;
3
4use strict;
5use warnings;
6
e65dccbc 7use Scalar::Util 'blessed';
e185c027 8use Carp 'confess';
9use Sub::Name 'subname';
10
c4538447 11use Data::OptList;
2d562421 12use Sub::Exporter;
13
d44714be 14our $VERSION = '0.07';
15our $AUTHORITY = 'cpan:STEVAN';
e185c027 16
e65dccbc 17use Moose ();
18
e185c027 19use Moose::Meta::Role;
7eaef7ad 20use Moose::Util::TypeConstraints;
e185c027 21
2d562421 22{
23 my ( $CALLER, %METAS );
24
25 sub _find_meta {
7eaef7ad 26 my $role = $CALLER;
2d562421 27
7eaef7ad 28 return $METAS{$role} if exists $METAS{$role};
fb1e11d5 29
7eaef7ad 30 # make a subtype for each Moose class
31 subtype $role
32 => as 'Role'
33 => where { $_->does($role) }
fb1e11d5 34 => optimize_as { blessed($_[0]) && $_[0]->can('does') && $_[0]->does($role) }
35 unless find_type_constraint($role);
2d562421 36
fb1e11d5 37 my $meta;
38 if ($role->can('meta')) {
39 $meta = $role->meta();
40 (blessed($meta) && $meta->isa('Moose::Meta::Role'))
68efb014 41 || confess "You already have a &meta function, but it does not return a Moose::Meta::Role";
fb1e11d5 42 }
43 else {
44 $meta = Moose::Meta::Role->initialize($role);
45 $meta->alias_method('meta' => sub { $meta });
46 }
2d562421 47
7eaef7ad 48 return $METAS{$role} = $meta;
2d562421 49 }
fb1e11d5 50
51
52 my %exports = (
2d562421 53 extends => sub {
54 my $meta = _find_meta();
fb1e11d5 55 return subname 'Moose::Role::extends' => sub {
2d562421 56 confess "Moose::Role does not currently support 'extends'"
fb1e11d5 57 };
58 },
59 with => sub {
60 my $meta = _find_meta();
61 return subname 'Moose::Role::with' => sub (@) {
c4538447 62 my (@args) = @_;
63 confess "Must specify at least one role" unless @args;
64
65 my $roles = Data::OptList::mkopt(\@args);
66
67 #use Data::Dumper;
68 #warn Dumper $roles;
69
70 Class::MOP::load_class($_->[0]) for @$roles;
71
72 ($_->[0]->can('meta') && $_->[0]->meta->isa('Moose::Meta::Role'))
73 || confess "You can only consume roles, " . $_->[0] . " is not a Moose role"
74 foreach @$roles;
75
76 if (scalar @$roles == 1) {
77 my ($role, $params) = @{$roles->[0]};
78 $role->meta->apply($meta, (defined $params ? %$params : ()));
d05cd563 79 }
80 else {
81 Moose::Meta::Role->combine(
c4538447 82 map { $_->[0]->meta } @$roles
d05cd563 83 )->apply($meta);
84 }
2d562421 85 };
fb1e11d5 86 },
2d562421 87 requires => sub {
88 my $meta = _find_meta();
fb1e11d5 89 return subname 'Moose::Role::requires' => sub (@) {
68117c45 90 confess "Must specify at least one method" unless @_;
2d562421 91 $meta->add_required_methods(@_);
fb1e11d5 92 };
93 },
d79e62fd 94 excludes => sub {
95 my $meta = _find_meta();
fb1e11d5 96 return subname 'Moose::Role::excludes' => sub (@) {
68117c45 97 confess "Must specify at least one role" unless @_;
d79e62fd 98 $meta->add_excluded_roles(@_);
fb1e11d5 99 };
100 },
2d562421 101 has => sub {
102 my $meta = _find_meta();
fb1e11d5 103 return subname 'Moose::Role::has' => sub ($;%) {
104 my ($name, %options) = @_;
105 $meta->add_attribute($name, %options)
106 };
107 },
2d562421 108 before => sub {
109 my $meta = _find_meta();
fb1e11d5 110 return subname 'Moose::Role::before' => sub (@&) {
0558683c 111 my $code = pop @_;
112 $meta->add_before_method_modifier($_, $code) for @_;
fb1e11d5 113 };
114 },
2d562421 115 after => sub {
116 my $meta = _find_meta();
fb1e11d5 117 return subname 'Moose::Role::after' => sub (@&) {
118 my $code = pop @_;
119 $meta->add_after_method_modifier($_, $code) for @_;
120 };
121 },
2d562421 122 around => sub {
123 my $meta = _find_meta();
fb1e11d5 124 return subname 'Moose::Role::around' => sub (@&) {
125 my $code = pop @_;
126 $meta->add_around_method_modifier($_, $code) for @_;
127 };
128 },
129 super => sub {
52c7c330 130 {
131 no strict 'refs';
132 $Moose::SUPER_SLOT{$CALLER} = \*{"${CALLER}::super"};
133 }
2d562421 134 my $meta = _find_meta();
0558683c 135 return subname 'Moose::Role::super' => sub {};
2d562421 136 },
137 override => sub {
138 my $meta = _find_meta();
2c0cbef7 139 return subname 'Moose::Role::override' => sub ($&) {
0558683c 140 my ($name, $code) = @_;
141 $meta->add_override_method_modifier($name, $code);
fb1e11d5 142 };
143 },
2d562421 144 inner => sub {
145 my $meta = _find_meta();
146 return subname 'Moose::Role::inner' => sub {
0558683c 147 confess "Moose::Role cannot support 'inner'";
fb1e11d5 148 };
149 },
2d562421 150 augment => sub {
151 my $meta = _find_meta();
152 return subname 'Moose::Role::augment' => sub {
06b30515 153 confess "Moose::Role cannot support 'augment'";
fb1e11d5 154 };
155 },
2d562421 156 confess => sub {
157 return \&Carp::confess;
158 },
159 blessed => sub {
160 return \&Scalar::Util::blessed;
fb1e11d5 161 }
162 );
2d562421 163
fb1e11d5 164 my $exporter = Sub::Exporter::build_exporter({
2d562421 165 exports => \%exports,
166 groups => {
167 default => [':all']
168 }
169 });
fb1e11d5 170
2d562421 171 sub import {
fc9a40d7 172 $CALLER =
173 ref $_[1] && defined $_[1]->{into} ? $_[1]->{into}
174 : ref $_[1]
175 && defined $_[1]->{into_level} ? caller( $_[1]->{into_level} )
176 : caller();
177
fb1e11d5 178
c235cd98 179 strict->import;
fb1e11d5 180 warnings->import;
2d562421 181
182 # we should never export to main
183 return if $CALLER eq 'main';
184
185 goto $exporter;
186 };
187
e185c027 188}
189
1901;
191
192__END__
193
194=pod
195
196=head1 NAME
197
198Moose::Role - The Moose Role
199
76d37e5a 200=head1 SYNOPSIS
201
202 package Eq;
85424612 203 use Moose::Role; # automatically turns on strict and warnings
fb1e11d5 204
e46edf94 205 requires 'equal';
fb1e11d5 206
207 sub no_equal {
76d37e5a 208 my ($self, $other) = @_;
209 !$self->equal($other);
210 }
fb1e11d5 211
76d37e5a 212 # ... then in your classes
fb1e11d5 213
76d37e5a 214 package Currency;
85424612 215 use Moose; # automatically turns on strict and warnings
fb1e11d5 216
76d37e5a 217 with 'Eq';
fb1e11d5 218
76d37e5a 219 sub equal {
220 my ($self, $other) = @_;
bdabd620 221 $self->as_float == $other->as_float;
76d37e5a 222 }
223
e185c027 224=head1 DESCRIPTION
225
85424612 226Role support in Moose is pretty solid at this point. However, the best
227documentation is still the the test suite. It is fairly safe to assume Perl 6
228style behavior and then either refer to the test suite, or ask questions on
229#moose if something doesn't quite do what you expect.
d44714be 230
85424612 231We are planning writing some more documentation in the near future, but nothing
232is ready yet, sorry.
76d37e5a 233
2c0cbef7 234=head1 EXPORTED FUNCTIONS
235
85424612 236Moose::Role currently supports all of the functions that L<Moose> exports, but
237differs slightly in how some items are handled (see L<CAVEATS> below for
238details).
76d37e5a 239
85424612 240Moose::Role also offers two role-specific keyword exports:
e185c027 241
242=over 4
243
2c0cbef7 244=item B<requires (@method_names)>
76d37e5a 245
fb1e11d5 246Roles can require that certain methods are implemented by any class which
85424612 247C<does> the role.
9e93dd19 248
2c0cbef7 249=item B<excludes (@role_names)>
250
9e93dd19 251Roles can C<exclude> other roles, in effect saying "I can never be combined
fb1e11d5 252with these C<@role_names>". This is a feature which should not be used
85424612 253lightly.
9e93dd19 254
2c0cbef7 255=back
256
257=head1 CAVEATS
258
85424612 259Role support has only a few caveats:
2c0cbef7 260
261=over 4
76d37e5a 262
76d37e5a 263=item *
264
fb1e11d5 265Roles cannot use the C<extends> keyword; it will throw an exception for now.
266The same is true of the C<augment> and C<inner> keywords (not sure those
267really make sense for roles). All other Moose keywords will be I<deferred>
85424612 268so that they can be applied to the consuming class.
76d37e5a 269
fb1e11d5 270=item *
2c0cbef7 271
85424612 272Role composition does its best to B<not> be order-sensitive when it comes to
273conflict resolution and requirements detection. However, it is order-sensitive
274when it comes to method modifiers. All before/around/after modifiers are
275included whenever a role is composed into a class, and then applied in the order
276in which the roles are used. This also means that there is no conflict for
277before/around/after modifiers.
2c0cbef7 278
85424612 279In most cases, this will be a non-issue; however, it is something to keep in
280mind when using method modifiers in a role. You should never assume any
2c0cbef7 281ordering.
282
283=item *
284
fb1e11d5 285The C<requires> keyword currently only works with actual methods. A method
286modifier (before/around/after and override) will not count as a fufillment
2c0cbef7 287of the requirement, and neither will an autogenerated accessor for an attribute.
288
85424612 289It is likely that attribute accessors will eventually be allowed to fufill those
290requirements, or we will introduce a C<requires_attr> keyword of some kind
291instead. This decision has not yet been finalized.
2c0cbef7 292
e185c027 293=back
294
295=head1 BUGS
296
fb1e11d5 297All complex software has bugs lurking in it, and this module is no
e185c027 298exception. If you find a bug please either email me, or add the bug
299to cpan-RT.
300
301=head1 AUTHOR
302
303Stevan Little E<lt>stevan@iinteractive.comE<gt>
304
db1ab48d 305Christian Hansen E<lt>chansen@cpan.orgE<gt>
98aae381 306
e185c027 307=head1 COPYRIGHT AND LICENSE
308
778db3ac 309Copyright 2006-2008 by Infinity Interactive, Inc.
e185c027 310
311L<http://www.iinteractive.com>
312
313This library is free software; you can redistribute it and/or modify
fb1e11d5 314it under the same terms as Perl itself.
e185c027 315
68117c45 316=cut