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
c8cf9aaa 12our $VERSION = '0.09';
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,
27 ':attribute_metaclass' => 'Moose::Meta::Attribute',
8ee73eeb 28 ':method_metaclass' => 'Moose::Meta::Method',
ddd0ec20 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;
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');
215 # if it's meta is a vanilla Moose,
216 # then we can safely ignore it.
217 next if blessed($super->meta) eq 'Moose::Meta::Class';
218 # but if we have anything else,
219 # we need to check it out ...
220 unless (# see if of our metaclass is incompatible
221 ($self->isa(blessed($super->meta)) &&
222 # and see if our instance metaclass is incompatible
223 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
224 # ... and if we are just a vanilla Moose
225 $self->isa('Moose::Meta::Class')) {
226 # re-initialize the meta ...
227 my $super_meta = $super->meta;
228 # NOTE:
229 # We might want to consider actually
230 # transfering any attributes from the
231 # original meta into this one, but in
232 # general you should not have any there
233 # at this point anyway, so it's very
234 # much an obscure edge case anyway
235 $self = $super_meta->reinitialize($self->name => (
236 ':attribute_metaclass' => $super_meta->attribute_metaclass,
237 ':method_metaclass' => $super_meta->method_metaclass,
238 ':instance_metaclass' => $super_meta->instance_metaclass,
239 ));
240 }
241 }
242 return $self;
243}
244
245sub _apply_all_roles {
246 my ($self, @roles) = @_;
247 ($_->can('meta') && $_->meta->isa('Moose::Meta::Role'))
248 || confess "You can only consume roles, $_ is not a Moose role"
249 foreach @roles;
250 if (scalar @roles == 1) {
251 $roles[0]->meta->apply($self);
252 }
253 else {
68efb014 254 # FIXME
255 # we should make a Moose::Meta::Role::Composite
256 # which is a smaller version of Moose::Meta::Role
257 # which does not use any package stuff
1341f10c 258 Moose::Meta::Role->combine(
259 map { $_->meta } @roles
260 )->apply($self);
261 }
262}
263
264sub _process_attribute {
265 my ($self, $name, %options) = @_;
266 if ($name =~ /^\+(.*)/) {
267 my $new_attr = $self->_process_inherited_attribute($1, %options);
268 $self->add_attribute($new_attr);
269 }
270 else {
271 if ($options{metaclass}) {
272 Moose::_load_all_classes($options{metaclass});
273 $self->add_attribute($options{metaclass}->new($name, %options));
274 }
275 else {
276 $self->add_attribute($name, %options);
277 }
278 }
279}
280
281sub _process_inherited_attribute {
282 my ($self, $attr_name, %options) = @_;
283 my $inherited_attr = $self->find_attribute_by_name($attr_name);
284 (defined $inherited_attr)
285 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
286 my $new_attr;
287 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
288 $new_attr = $inherited_attr->clone_and_inherit_options(%options);
289 }
290 else {
291 # NOTE:
292 # kind of a kludge to handle Class::MOP::Attributes
293 $new_attr = Moose::Meta::Attribute::clone_and_inherit_options(
294 $inherited_attr, %options
295 );
296 }
297 return $new_attr;
298}
299
c0e30cf5 3001;
301
302__END__
303
304=pod
305
306=head1 NAME
307
e522431d 308Moose::Meta::Class - The Moose metaclass
c0e30cf5 309
c0e30cf5 310=head1 DESCRIPTION
311
e522431d 312This is a subclass of L<Class::MOP::Class> with Moose specific
313extensions.
314
6ba6d68c 315For the most part, the only time you will ever encounter an
316instance of this class is if you are doing some serious deep
317introspection. To really understand this class, you need to refer
318to the L<Class::MOP::Class> documentation.
319
c0e30cf5 320=head1 METHODS
321
322=over 4
323
590868a3 324=item B<initialize>
325
8c9d74e7 326=item B<new_object>
327
02a0fb52 328We override this method to support the C<trigger> attribute option.
329
a15dff8d 330=item B<construct_instance>
331
6ba6d68c 332This provides some Moose specific extensions to this method, you
333almost never call this method directly unless you really know what
334you are doing.
335
336This method makes sure to handle the moose weak-ref, type-constraint
337and type coercion features.
ef1d5f4b 338
093b12c2 339=item B<get_method_map>
e9ec68d6 340
68efb014 341This accommodates Moose::Meta::Role::Method instances, which are
e9ec68d6 342aliased, instead of added, but still need to be counted as valid
343methods.
344
78cd1d3b 345=item B<add_override_method_modifier ($name, $method)>
346
02a0fb52 347This will create an C<override> method modifier for you, and install
348it in the package.
349
78cd1d3b 350=item B<add_augment_method_modifier ($name, $method)>
351
02a0fb52 352This will create an C<augment> method modifier for you, and install
353it in the package.
354
2b14ac61 355=item B<calculate_all_roles>
356
ef333f17 357=item B<roles>
358
02a0fb52 359This will return an array of C<Moose::Meta::Role> instances which are
360attached to this class.
361
ef333f17 362=item B<add_role ($role)>
363
02a0fb52 364This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
365to the list of associated roles.
366
ef333f17 367=item B<does_role ($role_name)>
368
02a0fb52 369This will test if this class C<does> a given C<$role_name>. It will
370not only check it's local roles, but ask them as well in order to
371cascade down the role hierarchy.
372
d79e62fd 373=item B<excludes_role ($role_name)>
374
375This will test if this class C<excludes> a given C<$role_name>. It will
376not only check it's local roles, but ask them as well in order to
377cascade down the role hierarchy.
378
9e93dd19 379=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 380
9e93dd19 381This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
382support for taking the C<$params> as a HASH ref.
ac1ef2f9 383
c0e30cf5 384=back
385
386=head1 BUGS
387
388All complex software has bugs lurking in it, and this module is no
389exception. If you find a bug please either email me, or add the bug
390to cpan-RT.
391
c0e30cf5 392=head1 AUTHOR
393
394Stevan Little E<lt>stevan@iinteractive.comE<gt>
395
396=head1 COPYRIGHT AND LICENSE
397
398Copyright 2006 by Infinity Interactive, Inc.
399
400L<http://www.iinteractive.com>
401
402This library is free software; you can redistribute it and/or modify
403it under the same terms as Perl itself.
404
8a7a9c53 405=cut
1a563243 406