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