foo
[gitmo/Moose.git] / lib / Moose / Meta / Class.pm
CommitLineData
c0e30cf5 1
2package Moose::Meta::Class;
3
4use strict;
5use warnings;
6
648e79ae 7use Class::MOP;
8
6ba6d68c 9use Carp 'confess';
54b1cdf0 10use Scalar::Util 'weaken', 'blessed', 'reftype';
a15dff8d 11
37ee30c9 12our $VERSION = '0.08';
bc1e29b5 13
8ee73eeb 14use Moose::Meta::Method::Overriden;
15
c0e30cf5 16use base 'Class::MOP::Class';
17
598340d5 18__PACKAGE__->meta->add_attribute('roles' => (
ef333f17 19 reader => 'roles',
20 default => sub { [] }
21));
22
590868a3 23sub initialize {
24 my $class = shift;
25 my $pkg = shift;
26 $class->SUPER::initialize($pkg,
5cf3dbcf 27 'attribute_metaclass' => 'Moose::Meta::Attribute',
28 'method_metaclass' => 'Moose::Meta::Method',
29 'instance_metaclass' => 'Moose::Meta::Instance',
590868a3 30 @_);
1341f10c 31}
590868a3 32
ef333f17 33sub add_role {
34 my ($self, $role) = @_;
35 (blessed($role) && $role->isa('Moose::Meta::Role'))
36 || confess "Roles must be instances of Moose::Meta::Role";
37 push @{$self->roles} => $role;
38}
39
b8aeb4dc 40sub calculate_all_roles {
41 my $self = shift;
42 my %seen;
43 grep { !$seen{$_->name}++ } map { $_->calculate_all_roles } @{ $self->roles };
44}
45
ef333f17 46sub does_role {
47 my ($self, $role_name) = @_;
48 (defined $role_name)
49 || confess "You must supply a role name to look for";
9c429218 50 foreach my $class ($self->class_precedence_list) {
74f6d830 51 next unless $class->can('meta');
9c429218 52 foreach my $role (@{$class->meta->roles}) {
53 return 1 if $role->does_role($role_name);
54 }
ef333f17 55 }
56 return 0;
57}
58
d79e62fd 59sub excludes_role {
60 my ($self, $role_name) = @_;
61 (defined $role_name)
62 || confess "You must supply a role name to look for";
74f6d830 63 foreach my $class ($self->class_precedence_list) {
64 next unless $class->can('meta');
9c429218 65 foreach my $role (@{$class->meta->roles}) {
66 return 1 if $role->excludes_role($role_name);
67 }
d79e62fd 68 }
69 return 0;
70}
71
8c9d74e7 72sub new_object {
73 my ($class, %params) = @_;
74 my $self = $class->SUPER::new_object(%params);
75 foreach my $attr ($class->compute_all_applicable_attributes()) {
715ea0b7 76 # FIXME:
77 # this does not accept undefined
78 # values, nor does it accept false
79 # values to be passed into the init-arg
5faf11bb 80 next unless $params{$attr->init_arg} && $attr->can('has_trigger') && $attr->has_trigger;
81 $attr->trigger->($self, $params{$attr->init_arg}, $attr);
8c9d74e7 82 }
83 return $self;
84}
85
a15dff8d 86sub construct_instance {
87 my ($class, %params) = @_;
ddd0ec20 88 my $meta_instance = $class->get_meta_instance;
575db57d 89 # FIXME:
90 # the code below is almost certainly incorrect
91 # but this is foreign inheritence, so we might
92 # have to kludge it in the end.
ddd0ec20 93 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
74f6d830 94 foreach my $attr ($class->compute_all_applicable_attributes()) {
ddd0ec20 95 $attr->initialize_instance_slot($meta_instance, $instance, \%params)
a15dff8d 96 }
97 return $instance;
98}
99
a7d0cd00 100
093b12c2 101# FIXME:
102# This is ugly
103sub get_method_map {
104 my $self = shift;
5cf3dbcf 105 my $map = $self->{'%!methods'};
a7d0cd00 106
093b12c2 107 my $class_name = $self->name;
108 my $method_metaclass = $self->method_metaclass;
109
110 foreach my $symbol ($self->list_all_package_symbols('CODE')) {
111
112 my $code = $self->get_package_symbol('&' . $symbol);
113
114 next if exists $map->{$symbol} &&
115 defined $map->{$symbol} &&
116 $map->{$symbol}->body == $code;
117
118 my $gv = B::svref_2object($code)->GV;
119
120 my $pkg = $gv->STASH->NAME;
37ee30c9 121 if ($pkg->can('meta') && $pkg->meta && $pkg->meta->isa('Moose::Meta::Role')) {
093b12c2 122 #my $role = $pkg->meta->name;
123 #next unless $self->does_role($role);
124 }
125 else {
126 next if ($gv->STASH->NAME || '') ne $class_name &&
127 ($gv->NAME || '') ne '__ANON__';
128 }
129
130 $map->{$symbol} = $method_metaclass->wrap($code);
131 }
132
133 return $map;
a7d0cd00 134}
135
093b12c2 136### ---------------------------------------------
137
a2eec5e7 138sub add_attribute {
139 my $self = shift;
140 my $name = shift;
141 if (scalar @_ == 1 && ref($_[0]) eq 'HASH') {
142 # NOTE:
143 # if it is a HASH ref, we de-ref it.
144 # this will usually mean that it is
145 # coming from a role
146 $self->SUPER::add_attribute($name => %{$_[0]});
147 }
148 else {
149 # otherwise we just pass the args
150 $self->SUPER::add_attribute($name => @_);
151 }
152}
153
78cd1d3b 154sub add_override_method_modifier {
155 my ($self, $name, $method, $_super_package) = @_;
d05cd563 156 (!$self->has_method($name))
157 || confess "Cannot add an override method if a local method is already present";
78cd1d3b 158 # need this for roles ...
159 $_super_package ||= $self->name;
160 my $super = $self->find_next_method_by_name($name);
161 (defined $super)
162 || confess "You cannot override '$name' because it has no super method";
093b12c2 163 $self->add_method($name => Moose::Meta::Method::Overriden->wrap(sub {
78cd1d3b 164 my @args = @_;
165 no strict 'refs';
166 no warnings 'redefine';
167 local *{$_super_package . '::super'} = sub { $super->(@args) };
168 return $method->(@args);
093b12c2 169 }));
78cd1d3b 170}
171
172sub add_augment_method_modifier {
05d9eaf6 173 my ($self, $name, $method) = @_;
d05cd563 174 (!$self->has_method($name))
175 || confess "Cannot add an augment method if a local method is already present";
78cd1d3b 176 my $super = $self->find_next_method_by_name($name);
177 (defined $super)
05d9eaf6 178 || confess "You cannot augment '$name' because it has no super method";
179 my $_super_package = $super->package_name;
180 # BUT!,... if this is an overriden method ....
181 if ($super->isa('Moose::Meta::Method::Overriden')) {
182 # we need to be sure that we actually
183 # find the next method, which is not
184 # an 'override' method, the reason is
185 # that an 'override' method will not
186 # be the one calling inner()
187 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
188 $_super_package = $real_super->package_name;
189 }
78cd1d3b 190 $self->add_method($name => sub {
191 my @args = @_;
192 no strict 'refs';
193 no warnings 'redefine';
05d9eaf6 194 local *{$_super_package . '::inner'} = sub { $method->(@args) };
78cd1d3b 195 return $super->(@args);
196 });
197}
198
1341f10c 199## Private Utility methods ...
200
05d9eaf6 201sub _find_next_method_by_name_which_is_not_overridden {
202 my ($self, $name) = @_;
68efb014 203 foreach my $method ($self->find_all_methods_by_name($name)) {
05d9eaf6 204 return $method->{code}
205 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
206 }
207 return undef;
208}
209
1341f10c 210sub _fix_metaclass_incompatability {
211 my ($self, @superclasses) = @_;
212 foreach my $super (@superclasses) {
213 # don't bother if it does not have a meta.
214 next unless $super->can('meta');
8ecb1fa0 215 # get the name, make sure we take
216 # immutable classes into account
217 my $super_meta_name = ($super->meta->is_immutable
218 ? $super->meta->get_mutable_metaclass_name
219 : blessed($super->meta));
1341f10c 220 # if it's meta is a vanilla Moose,
8ecb1fa0 221 # then we can safely ignore it.
222 next if $super_meta_name eq 'Moose::Meta::Class';
1341f10c 223 # but if we have anything else,
224 # we need to check it out ...
225 unless (# see if of our metaclass is incompatible
8ecb1fa0 226 ($self->isa($super_meta_name) &&
1341f10c 227 # and see if our instance metaclass is incompatible
228 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
229 # ... and if we are just a vanilla Moose
230 $self->isa('Moose::Meta::Class')) {
231 # re-initialize the meta ...
232 my $super_meta = $super->meta;
233 # NOTE:
234 # We might want to consider actually
235 # transfering any attributes from the
236 # original meta into this one, but in
237 # general you should not have any there
238 # at this point anyway, so it's very
239 # much an obscure edge case anyway
240 $self = $super_meta->reinitialize($self->name => (
5cf3dbcf 241 'attribute_metaclass' => $super_meta->attribute_metaclass,
242 'method_metaclass' => $super_meta->method_metaclass,
243 'instance_metaclass' => $super_meta->instance_metaclass,
1341f10c 244 ));
245 }
246 }
247 return $self;
248}
249
250sub _apply_all_roles {
251 my ($self, @roles) = @_;
252 ($_->can('meta') && $_->meta->isa('Moose::Meta::Role'))
253 || confess "You can only consume roles, $_ is not a Moose role"
254 foreach @roles;
255 if (scalar @roles == 1) {
256 $roles[0]->meta->apply($self);
257 }
258 else {
68efb014 259 # FIXME
260 # we should make a Moose::Meta::Role::Composite
261 # which is a smaller version of Moose::Meta::Role
262 # which does not use any package stuff
1341f10c 263 Moose::Meta::Role->combine(
264 map { $_->meta } @roles
265 )->apply($self);
266 }
267}
268
269sub _process_attribute {
270 my ($self, $name, %options) = @_;
271 if ($name =~ /^\+(.*)/) {
272 my $new_attr = $self->_process_inherited_attribute($1, %options);
273 $self->add_attribute($new_attr);
274 }
275 else {
276 if ($options{metaclass}) {
277 Moose::_load_all_classes($options{metaclass});
278 $self->add_attribute($options{metaclass}->new($name, %options));
279 }
280 else {
281 $self->add_attribute($name, %options);
282 }
283 }
284}
285
286sub _process_inherited_attribute {
287 my ($self, $attr_name, %options) = @_;
288 my $inherited_attr = $self->find_attribute_by_name($attr_name);
289 (defined $inherited_attr)
290 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
291 my $new_attr;
292 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
293 $new_attr = $inherited_attr->clone_and_inherit_options(%options);
294 }
295 else {
296 # NOTE:
297 # kind of a kludge to handle Class::MOP::Attributes
298 $new_attr = Moose::Meta::Attribute::clone_and_inherit_options(
299 $inherited_attr, %options
300 );
301 }
302 return $new_attr;
303}
304
5cf3dbcf 305## -------------------------------------------------
306
307use Moose::Meta::Method::Constructor;
1f779926 308use Moose::Meta::Method::Destructor;
5cf3dbcf 309
310{
311 # NOTE:
312 # the immutable version of a
313 # particular metaclass is
314 # really class-level data so
315 # we don't want to regenerate
316 # it any more than we need to
317 my $IMMUTABLE_METACLASS;
318 sub make_immutable {
319 my $self = shift;
320
321 $IMMUTABLE_METACLASS ||= Class::MOP::Immutable->new($self, {
322 read_only => [qw/superclasses/],
323 cannot_call => [qw/
324 add_method
325 alias_method
326 remove_method
327 add_attribute
328 remove_attribute
329 add_package_symbol
330 remove_package_symbol
331 add_role
332 /],
333 memoize => {
334 class_precedence_list => 'ARRAY',
335 compute_all_applicable_attributes => 'ARRAY',
336 get_meta_instance => 'SCALAR',
337 get_method_map => 'SCALAR',
338 # maybe ....
339 calculate_all_roles => 'ARRAY',
340 }
341 });
342
343 $IMMUTABLE_METACLASS->make_metaclass_immutable(
344 $self,
345 constructor_class => 'Moose::Meta::Method::Constructor',
1f779926 346 destructor_class => 'Moose::Meta::Method::Destructor',
347 inline_destructor => 1,
348 # NOTE:
349 # no need to do this,
350 # Moose always does it
5cf3dbcf 351 inline_accessors => 0,
352 @_,
353 )
354 }
355}
356
c0e30cf5 3571;
358
359__END__
360
361=pod
362
363=head1 NAME
364
e522431d 365Moose::Meta::Class - The Moose metaclass
c0e30cf5 366
c0e30cf5 367=head1 DESCRIPTION
368
e522431d 369This is a subclass of L<Class::MOP::Class> with Moose specific
370extensions.
371
6ba6d68c 372For the most part, the only time you will ever encounter an
373instance of this class is if you are doing some serious deep
374introspection. To really understand this class, you need to refer
375to the L<Class::MOP::Class> documentation.
376
c0e30cf5 377=head1 METHODS
378
379=over 4
380
590868a3 381=item B<initialize>
382
5cf3dbcf 383=item B<make_immutable>
384
8c9d74e7 385=item B<new_object>
386
02a0fb52 387We override this method to support the C<trigger> attribute option.
388
a15dff8d 389=item B<construct_instance>
390
6ba6d68c 391This provides some Moose specific extensions to this method, you
392almost never call this method directly unless you really know what
393you are doing.
394
395This method makes sure to handle the moose weak-ref, type-constraint
396and type coercion features.
ef1d5f4b 397
093b12c2 398=item B<get_method_map>
e9ec68d6 399
68efb014 400This accommodates Moose::Meta::Role::Method instances, which are
e9ec68d6 401aliased, instead of added, but still need to be counted as valid
402methods.
403
78cd1d3b 404=item B<add_override_method_modifier ($name, $method)>
405
02a0fb52 406This will create an C<override> method modifier for you, and install
407it in the package.
408
78cd1d3b 409=item B<add_augment_method_modifier ($name, $method)>
410
02a0fb52 411This will create an C<augment> method modifier for you, and install
412it in the package.
413
2b14ac61 414=item B<calculate_all_roles>
415
ef333f17 416=item B<roles>
417
02a0fb52 418This will return an array of C<Moose::Meta::Role> instances which are
419attached to this class.
420
ef333f17 421=item B<add_role ($role)>
422
02a0fb52 423This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
424to the list of associated roles.
425
ef333f17 426=item B<does_role ($role_name)>
427
02a0fb52 428This will test if this class C<does> a given C<$role_name>. It will
429not only check it's local roles, but ask them as well in order to
430cascade down the role hierarchy.
431
d79e62fd 432=item B<excludes_role ($role_name)>
433
434This will test if this class C<excludes> a given C<$role_name>. It will
435not only check it's local roles, but ask them as well in order to
436cascade down the role hierarchy.
437
9e93dd19 438=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 439
9e93dd19 440This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
441support for taking the C<$params> as a HASH ref.
ac1ef2f9 442
c0e30cf5 443=back
444
445=head1 BUGS
446
447All complex software has bugs lurking in it, and this module is no
448exception. If you find a bug please either email me, or add the bug
449to cpan-RT.
450
c0e30cf5 451=head1 AUTHOR
452
453Stevan Little E<lt>stevan@iinteractive.comE<gt>
454
455=head1 COPYRIGHT AND LICENSE
456
457Copyright 2006 by Infinity Interactive, Inc.
458
459L<http://www.iinteractive.com>
460
461This library is free software; you can redistribute it and/or modify
462it under the same terms as Perl itself.
463
8a7a9c53 464=cut
1a563243 465