Even non moose classes get metaclassed for delegation
[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
4c4fbe56 12our $VERSION = '0.05';
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 @_);
28}
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";
41 foreach my $role (@{$self->roles}) {
bdabd620 42 return 1 if $role->does_role($role_name);
ef333f17 43 }
44 return 0;
45}
46
8c9d74e7 47sub new_object {
48 my ($class, %params) = @_;
49 my $self = $class->SUPER::new_object(%params);
50 foreach my $attr ($class->compute_all_applicable_attributes()) {
5faf11bb 51 next unless $params{$attr->init_arg} && $attr->can('has_trigger') && $attr->has_trigger;
52 $attr->trigger->($self, $params{$attr->init_arg}, $attr);
8c9d74e7 53 }
54 return $self;
55}
56
a15dff8d 57sub construct_instance {
58 my ($class, %params) = @_;
ddd0ec20 59 my $meta_instance = $class->get_meta_instance;
60 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
a15dff8d 61 foreach my $attr ($class->compute_all_applicable_attributes()) {
ddd0ec20 62 $attr->initialize_instance_slot($meta_instance, $instance, \%params)
a15dff8d 63 }
64 return $instance;
65}
66
a7d0cd00 67sub has_method {
68 my ($self, $method_name) = @_;
69 (defined $method_name && $method_name)
70 || confess "You must define a method name";
71
72 my $sub_name = ($self->name . '::' . $method_name);
73
74 no strict 'refs';
75 return 0 if !defined(&{$sub_name});
76 my $method = \&{$sub_name};
77
78 return 1 if blessed($method) && $method->isa('Moose::Meta::Role::Method');
79 return $self->SUPER::has_method($method_name);
80}
81
54b1cdf0 82sub add_attribute {
83 my ($self, $name, %params) = @_;
84
85 my @delegations;
86 if ( my $delegation = delete $params{handles} ) {
87 my @method_names_or_hashes = $self->compute_delegation( $name, $delegation, \%params );
88 @delegations = $self->get_delegatable_methods( @method_names_or_hashes );
89 }
90
91 my $ret = $self->SUPER::add_attribute( $name, %params );
92
93 if ( @delegations ) {
94 my $attr = $self->get_attribute( $name );
95 $self->generate_delgate_method( $attr, $_ ) for @delegations;
96 }
97
98 return $ret;
99}
100
101sub generate_delgate_method {
102 my ( $self, $attr, $method ) = @_;
103
104 # FIXME like generated accessors these methods must be regenerated
105 # FIXME the reader may not work for subclasses with weird instances
106
107 my $reader = $attr->generate_reader_method( $attr->name ); # FIXME no need for attr name
108
109 my $method_name = $method->{name};
110 my $new_name = $method->{new_name} || $method_name;
111
112 $self->add_method( $new_name, sub {
113 if ( Scalar::Util::blessed( my $delegate = shift->$reader ) ) {
114 return $delegate->$method_name( @_ );
115 }
116 return;
117 });
118}
119
120sub compute_delegation {
121 my ( $self, $attr_name, $delegation, $params ) = @_;
122
123
124 # either it's a concrete list of method names
125 return $delegation unless ref $delegation; # single method name
126 return @$delegation if reftype($delegation) eq "ARRAY";
127
128 # or it's a generative api
129 my $delegator_meta = $self->_guess_attr_class_or_role( $attr_name, $params );
130 $self->generate_delegation_list( $delegation, $delegator_meta );
131}
132
133sub get_delegatable_methods {
134 my ( $self, @names_or_hashes ) = @_;
135 my @hashes = map { ref($_) ? $_ : { name => $_ } } @names_or_hashes;
136 return grep { !$self->name->can( $_->{name} ) } @hashes;
137}
138
139sub generate_delegation_list {
140 my ( $self, $delegation, $delegator_meta ) = @_;
141
142 if ( reftype($delegation) eq "CODE" ) {
143 return $delegation->( $self, $delegator_meta );
144 } elsif ( blessed($delegation) eq "Regexp" ) {
aff2941e 145 confess "For regular expression support the delegator class/role must use a Class::MOP::Class metaclass"
146 unless $delegator_meta->isa( "Class::MOP::Class" );
54b1cdf0 147 return grep { $_->{name} =~ /$delegation/ } $delegator_meta->compute_all_applicable_methods();
148 } else {
149 confess "The 'handles' specification '$delegation' is not supported";
150 }
151}
152
153sub _guess_attr_class_or_role {
154 my ( $self, $attr, $params ) = @_;
155
156 my ( $isa, $does ) = @{ $params }{qw/isa does/};
157
158 confess "Generative delegations must explicitly specify a class or a role for the attribute's type"
159 unless $isa || $does;
160
54b1cdf0 161 for (grep { blessed($_) } $isa, $does) {
4e848edb 162 confess "You must use classes/roles, not type constraints to use delegation ($_)"
54b1cdf0 163 unless $_->isa( "Moose::Meta::Class" );
164 }
165
166 confess "Cannot have an isa option and a does option if the isa does not do the does"
aff2941e 167 if $isa and $does and $isa->can("does") and !$isa->does( $does );
168
169 # if it's a class/role name make it into a meta object
170 for ($isa, $does) {
171 $_ = $_->meta if defined and !ref and $_->can("meta");
172 }
54b1cdf0 173
4e848edb 174 $isa = Class::MOP::Class->initialize($isa) if $isa and !ref($isa);
175
54b1cdf0 176 return $isa || $does;
177}
a7d0cd00 178
78cd1d3b 179sub add_override_method_modifier {
180 my ($self, $name, $method, $_super_package) = @_;
181 # need this for roles ...
182 $_super_package ||= $self->name;
183 my $super = $self->find_next_method_by_name($name);
184 (defined $super)
185 || confess "You cannot override '$name' because it has no super method";
05d9eaf6 186 $self->add_method($name => bless sub {
78cd1d3b 187 my @args = @_;
188 no strict 'refs';
189 no warnings 'redefine';
190 local *{$_super_package . '::super'} = sub { $super->(@args) };
191 return $method->(@args);
05d9eaf6 192 } => 'Moose::Meta::Method::Overriden');
78cd1d3b 193}
194
195sub add_augment_method_modifier {
05d9eaf6 196 my ($self, $name, $method) = @_;
78cd1d3b 197 my $super = $self->find_next_method_by_name($name);
198 (defined $super)
05d9eaf6 199 || confess "You cannot augment '$name' because it has no super method";
200 my $_super_package = $super->package_name;
201 # BUT!,... if this is an overriden method ....
202 if ($super->isa('Moose::Meta::Method::Overriden')) {
203 # we need to be sure that we actually
204 # find the next method, which is not
205 # an 'override' method, the reason is
206 # that an 'override' method will not
207 # be the one calling inner()
208 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
209 $_super_package = $real_super->package_name;
210 }
78cd1d3b 211 $self->add_method($name => sub {
212 my @args = @_;
213 no strict 'refs';
214 no warnings 'redefine';
05d9eaf6 215 local *{$_super_package . '::inner'} = sub { $method->(@args) };
78cd1d3b 216 return $super->(@args);
217 });
218}
219
05d9eaf6 220sub _find_next_method_by_name_which_is_not_overridden {
221 my ($self, $name) = @_;
222 my @methods = $self->find_all_methods_by_name($name);
223 foreach my $method (@methods) {
224 return $method->{code}
225 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
226 }
227 return undef;
228}
229
230package Moose::Meta::Method::Overriden;
231
232use strict;
233use warnings;
234
235our $VERSION = '0.01';
236
237use base 'Class::MOP::Method';
238
c0e30cf5 2391;
240
241__END__
242
243=pod
244
245=head1 NAME
246
e522431d 247Moose::Meta::Class - The Moose metaclass
c0e30cf5 248
c0e30cf5 249=head1 DESCRIPTION
250
e522431d 251This is a subclass of L<Class::MOP::Class> with Moose specific
252extensions.
253
6ba6d68c 254For the most part, the only time you will ever encounter an
255instance of this class is if you are doing some serious deep
256introspection. To really understand this class, you need to refer
257to the L<Class::MOP::Class> documentation.
258
c0e30cf5 259=head1 METHODS
260
261=over 4
262
590868a3 263=item B<initialize>
264
8c9d74e7 265=item B<new_object>
266
02a0fb52 267We override this method to support the C<trigger> attribute option.
268
a15dff8d 269=item B<construct_instance>
270
6ba6d68c 271This provides some Moose specific extensions to this method, you
272almost never call this method directly unless you really know what
273you are doing.
274
275This method makes sure to handle the moose weak-ref, type-constraint
276and type coercion features.
ef1d5f4b 277
e9ec68d6 278=item B<has_method ($name)>
279
280This accomidates Moose::Meta::Role::Method instances, which are
281aliased, instead of added, but still need to be counted as valid
282methods.
283
78cd1d3b 284=item B<add_override_method_modifier ($name, $method)>
285
02a0fb52 286This will create an C<override> method modifier for you, and install
287it in the package.
288
78cd1d3b 289=item B<add_augment_method_modifier ($name, $method)>
290
02a0fb52 291This will create an C<augment> method modifier for you, and install
292it in the package.
293
ef333f17 294=item B<roles>
295
02a0fb52 296This will return an array of C<Moose::Meta::Role> instances which are
297attached to this class.
298
ef333f17 299=item B<add_role ($role)>
300
02a0fb52 301This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
302to the list of associated roles.
303
ef333f17 304=item B<does_role ($role_name)>
305
02a0fb52 306This will test if this class C<does> a given C<$role_name>. It will
307not only check it's local roles, but ask them as well in order to
308cascade down the role hierarchy.
309
4e848edb 310=item B<add_attribute $attr_name, %params>
311
312This method does the same thing as L<Class::MOP::Class/add_attribute>, but adds
313suport for delegation.
314
315=back
316
317=head1 INTERNAL METHODS
318
319=over 4
320
321=item compute_delegation
322
323=item generate_delegation_list
324
325=item generate_delgate_method
326
327=item get_delegatable_methods
328
c0e30cf5 329=back
330
331=head1 BUGS
332
333All complex software has bugs lurking in it, and this module is no
334exception. If you find a bug please either email me, or add the bug
335to cpan-RT.
336
c0e30cf5 337=head1 AUTHOR
338
339Stevan Little E<lt>stevan@iinteractive.comE<gt>
340
341=head1 COPYRIGHT AND LICENSE
342
343Copyright 2006 by Infinity Interactive, Inc.
344
345L<http://www.iinteractive.com>
346
347This library is free software; you can redistribute it and/or modify
348it under the same terms as Perl itself.
349
8a7a9c53 350=cut