custom attribute metaclasses
[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';
a7d0cd00 10use Scalar::Util 'weaken', 'blessed';
a15dff8d 11
78cd1d3b 12our $VERSION = '0.04';
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
21sub add_role {
22 my ($self, $role) = @_;
23 (blessed($role) && $role->isa('Moose::Meta::Role'))
24 || confess "Roles must be instances of Moose::Meta::Role";
25 push @{$self->roles} => $role;
26}
27
28sub does_role {
29 my ($self, $role_name) = @_;
30 (defined $role_name)
31 || confess "You must supply a role name to look for";
32 foreach my $role (@{$self->roles}) {
bdabd620 33 return 1 if $role->does_role($role_name);
ef333f17 34 }
35 return 0;
36}
37
8c9d74e7 38sub new_object {
39 my ($class, %params) = @_;
40 my $self = $class->SUPER::new_object(%params);
41 foreach my $attr ($class->compute_all_applicable_attributes()) {
42 next unless $params{$attr->name} && $attr->has_trigger;
43 $attr->trigger->($self, $params{$attr->name});
44 }
45 return $self;
46}
47
a15dff8d 48sub construct_instance {
49 my ($class, %params) = @_;
e522431d 50 my $instance = $params{'__INSTANCE__'} || {};
a15dff8d 51 foreach my $attr ($class->compute_all_applicable_attributes()) {
52 my $init_arg = $attr->init_arg();
53 # try to fetch the init arg from the %params ...
54 my $val;
ca01a97b 55 if (exists $params{$init_arg}) {
56 $val = $params{$init_arg};
57 }
58 else {
59 # skip it if it's lazy
60 next if $attr->is_lazy;
6730c8d9 61 # and die if it's required and doesn't have a default value
ca01a97b 62 confess "Attribute (" . $attr->name . ") is required"
6730c8d9 63 if $attr->is_required && !$attr->has_default;
ca01a97b 64 }
a15dff8d 65 # if nothing was in the %params, we can use the
66 # attribute's default value (if it has one)
8339fae2 67 if (!defined $val && $attr->has_default) {
68 $val = $attr->default($instance);
69 }
00867c44 70 if (defined $val) {
00867c44 71 if ($attr->has_type_constraint) {
34a66aa3 72 if ($attr->should_coerce && $attr->type_constraint->has_coercion) {
a27aa600 73 $val = $attr->type_constraint->coercion->coerce($val);
7415b2cb 74 }
a27aa600 75 (defined($attr->type_constraint->check($val)))
66811d63 76 || confess "Attribute (" . $attr->name . ") does not pass the type contraint with '$val'";
00867c44 77 }
a15dff8d 78 }
79 $instance->{$attr->name} = $val;
6ba6d68c 80 if (defined $val && $attr->is_weak_ref) {
81 weaken($instance->{$attr->name});
82 }
a15dff8d 83 }
84 return $instance;
85}
86
a7d0cd00 87sub has_method {
88 my ($self, $method_name) = @_;
89 (defined $method_name && $method_name)
90 || confess "You must define a method name";
91
92 my $sub_name = ($self->name . '::' . $method_name);
93
94 no strict 'refs';
95 return 0 if !defined(&{$sub_name});
96 my $method = \&{$sub_name};
97
98 return 1 if blessed($method) && $method->isa('Moose::Meta::Role::Method');
99 return $self->SUPER::has_method($method_name);
100}
101
102
78cd1d3b 103sub add_override_method_modifier {
104 my ($self, $name, $method, $_super_package) = @_;
105 # need this for roles ...
106 $_super_package ||= $self->name;
107 my $super = $self->find_next_method_by_name($name);
108 (defined $super)
109 || confess "You cannot override '$name' because it has no super method";
05d9eaf6 110 $self->add_method($name => bless sub {
78cd1d3b 111 my @args = @_;
112 no strict 'refs';
113 no warnings 'redefine';
114 local *{$_super_package . '::super'} = sub { $super->(@args) };
115 return $method->(@args);
05d9eaf6 116 } => 'Moose::Meta::Method::Overriden');
78cd1d3b 117}
118
119sub add_augment_method_modifier {
05d9eaf6 120 my ($self, $name, $method) = @_;
78cd1d3b 121 my $super = $self->find_next_method_by_name($name);
122 (defined $super)
05d9eaf6 123 || confess "You cannot augment '$name' because it has no super method";
124 my $_super_package = $super->package_name;
125 # BUT!,... if this is an overriden method ....
126 if ($super->isa('Moose::Meta::Method::Overriden')) {
127 # we need to be sure that we actually
128 # find the next method, which is not
129 # an 'override' method, the reason is
130 # that an 'override' method will not
131 # be the one calling inner()
132 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
133 $_super_package = $real_super->package_name;
134 }
78cd1d3b 135 $self->add_method($name => sub {
136 my @args = @_;
137 no strict 'refs';
138 no warnings 'redefine';
05d9eaf6 139 local *{$_super_package . '::inner'} = sub { $method->(@args) };
78cd1d3b 140 return $super->(@args);
141 });
142}
143
05d9eaf6 144sub _find_next_method_by_name_which_is_not_overridden {
145 my ($self, $name) = @_;
146 my @methods = $self->find_all_methods_by_name($name);
147 foreach my $method (@methods) {
148 return $method->{code}
149 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
150 }
151 return undef;
152}
153
154package Moose::Meta::Method::Overriden;
155
156use strict;
157use warnings;
158
159our $VERSION = '0.01';
160
161use base 'Class::MOP::Method';
162
c0e30cf5 1631;
164
165__END__
166
167=pod
168
169=head1 NAME
170
e522431d 171Moose::Meta::Class - The Moose metaclass
c0e30cf5 172
c0e30cf5 173=head1 DESCRIPTION
174
e522431d 175This is a subclass of L<Class::MOP::Class> with Moose specific
176extensions.
177
6ba6d68c 178For the most part, the only time you will ever encounter an
179instance of this class is if you are doing some serious deep
180introspection. To really understand this class, you need to refer
181to the L<Class::MOP::Class> documentation.
182
c0e30cf5 183=head1 METHODS
184
185=over 4
186
8c9d74e7 187=item B<new_object>
188
02a0fb52 189We override this method to support the C<trigger> attribute option.
190
a15dff8d 191=item B<construct_instance>
192
6ba6d68c 193This provides some Moose specific extensions to this method, you
194almost never call this method directly unless you really know what
195you are doing.
196
197This method makes sure to handle the moose weak-ref, type-constraint
198and type coercion features.
ef1d5f4b 199
e9ec68d6 200=item B<has_method ($name)>
201
202This accomidates Moose::Meta::Role::Method instances, which are
203aliased, instead of added, but still need to be counted as valid
204methods.
205
78cd1d3b 206=item B<add_override_method_modifier ($name, $method)>
207
02a0fb52 208This will create an C<override> method modifier for you, and install
209it in the package.
210
78cd1d3b 211=item B<add_augment_method_modifier ($name, $method)>
212
02a0fb52 213This will create an C<augment> method modifier for you, and install
214it in the package.
215
ef333f17 216=item B<roles>
217
02a0fb52 218This will return an array of C<Moose::Meta::Role> instances which are
219attached to this class.
220
ef333f17 221=item B<add_role ($role)>
222
02a0fb52 223This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
224to the list of associated roles.
225
ef333f17 226=item B<does_role ($role_name)>
227
02a0fb52 228This will test if this class C<does> a given C<$role_name>. It will
229not only check it's local roles, but ask them as well in order to
230cascade down the role hierarchy.
231
c0e30cf5 232=back
233
234=head1 BUGS
235
236All complex software has bugs lurking in it, and this module is no
237exception. If you find a bug please either email me, or add the bug
238to cpan-RT.
239
c0e30cf5 240=head1 AUTHOR
241
242Stevan Little E<lt>stevan@iinteractive.comE<gt>
243
244=head1 COPYRIGHT AND LICENSE
245
246Copyright 2006 by Infinity Interactive, Inc.
247
248L<http://www.iinteractive.com>
249
250This library is free software; you can redistribute it and/or modify
251it under the same terms as Perl itself.
252
253=cut