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