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