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