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