2 package Moose::Meta::Class;
10 use Scalar::Util 'weaken', 'blessed', 'reftype';
12 our $VERSION = '0.05';
14 use base 'Class::MOP::Class';
16 __PACKAGE__->meta->add_attribute('roles' => (
24 $class->SUPER::initialize($pkg,
25 ':attribute_metaclass' => 'Moose::Meta::Attribute',
26 ':instance_metaclass' => 'Moose::Meta::Instance',
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;
38 my ($self, $role_name) = @_;
40 || confess "You must supply a role name to look for";
41 foreach my $role (@{$self->roles}) {
42 return 1 if $role->does_role($role_name);
48 my ($class, %params) = @_;
49 my $self = $class->SUPER::new_object(%params);
50 foreach my $attr ($class->compute_all_applicable_attributes()) {
51 next unless $params{$attr->init_arg} && $attr->can('has_trigger') && $attr->has_trigger;
52 $attr->trigger->($self, $params{$attr->init_arg}, $attr);
57 sub construct_instance {
58 my ($class, %params) = @_;
59 my $meta_instance = $class->get_meta_instance;
60 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
61 foreach my $attr ($class->compute_all_applicable_attributes()) {
62 $attr->initialize_instance_slot($meta_instance, $instance, \%params)
68 my ($self, $method_name) = @_;
69 (defined $method_name && $method_name)
70 || confess "You must define a method name";
72 my $sub_name = ($self->name . '::' . $method_name);
75 return 0 if !defined(&{$sub_name});
76 my $method = \&{$sub_name};
78 return 1 if blessed($method) && $method->isa('Moose::Meta::Role::Method');
79 return $self->SUPER::has_method($method_name);
83 my ($self, $name, %params) = @_;
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 );
91 my $ret = $self->SUPER::add_attribute( $name, %params );
94 my $attr = $self->get_attribute( $name );
95 $self->generate_delgate_method( $attr, $_ ) for @delegations;
101 sub generate_delgate_method {
102 my ( $self, $attr, $method ) = @_;
104 # FIXME like generated accessors these methods must be regenerated
105 # FIXME the reader may not work for subclasses with weird instances
107 my $reader = $attr->generate_reader_method( $attr->name ); # FIXME no need for attr name
109 my $method_name = $method->{name};
110 my $new_name = $method->{new_name} || $method_name;
112 $self->add_method( $new_name, sub {
113 if ( Scalar::Util::blessed( my $delegate = shift->$reader ) ) {
114 return $delegate->$method_name( @_ );
120 sub compute_delegation {
121 my ( $self, $attr_name, $delegation, $params ) = @_;
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";
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 );
133 sub 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;
139 sub generate_delegation_list {
140 my ( $self, $delegation, $delegator_meta ) = @_;
142 if ( reftype($delegation) eq "CODE" ) {
143 return $delegation->( $self, $delegator_meta );
144 } elsif ( blessed($delegation) eq "Regexp" ) {
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" );
147 return grep { $_->{name} =~ /$delegation/ } $delegator_meta->compute_all_applicable_methods();
149 confess "The 'handles' specification '$delegation' is not supported";
153 sub _guess_attr_class_or_role {
154 my ( $self, $attr, $params ) = @_;
156 my ( $isa, $does ) = @{ $params }{qw/isa does/};
158 confess "Generative delegations must explicitly specify a class or a role for the attribute's type"
159 unless $isa || $does;
161 for (grep { blessed($_) } $isa, $does) {
162 confess "You must use classes/roles, not type constraints to use delegation ($_)"
163 unless $_->isa( "Moose::Meta::Class" );
166 confess "Cannot have an isa option and a does option if the isa does not do the does"
167 if $isa and $does and $isa->can("does") and !$isa->does( $does );
169 # if it's a class/role name make it into a meta object
171 $_ = $_->meta if defined and !ref and $_->can("meta");
174 $isa = Class::MOP::Class->initialize($isa) if $isa and !ref($isa);
176 return $isa || $does;
179 sub 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);
185 || confess "You cannot override '$name' because it has no super method";
186 $self->add_method($name => bless sub {
189 no warnings 'redefine';
190 local *{$_super_package . '::super'} = sub { $super->(@args) };
191 return $method->(@args);
192 } => 'Moose::Meta::Method::Overriden');
195 sub add_augment_method_modifier {
196 my ($self, $name, $method) = @_;
197 my $super = $self->find_next_method_by_name($name);
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;
211 $self->add_method($name => sub {
214 no warnings 'redefine';
215 local *{$_super_package . '::inner'} = sub { $method->(@args) };
216 return $super->(@args);
220 sub _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');
230 package Moose::Meta::Method::Overriden;
235 our $VERSION = '0.01';
237 use base 'Class::MOP::Method';
247 Moose::Meta::Class - The Moose metaclass
251 This is a subclass of L<Class::MOP::Class> with Moose specific
254 For the most part, the only time you will ever encounter an
255 instance of this class is if you are doing some serious deep
256 introspection. To really understand this class, you need to refer
257 to the L<Class::MOP::Class> documentation.
267 We override this method to support the C<trigger> attribute option.
269 =item B<construct_instance>
271 This provides some Moose specific extensions to this method, you
272 almost never call this method directly unless you really know what
275 This method makes sure to handle the moose weak-ref, type-constraint
276 and type coercion features.
278 =item B<has_method ($name)>
280 This accomidates Moose::Meta::Role::Method instances, which are
281 aliased, instead of added, but still need to be counted as valid
284 =item B<add_override_method_modifier ($name, $method)>
286 This will create an C<override> method modifier for you, and install
289 =item B<add_augment_method_modifier ($name, $method)>
291 This will create an C<augment> method modifier for you, and install
296 This will return an array of C<Moose::Meta::Role> instances which are
297 attached to this class.
299 =item B<add_role ($role)>
301 This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
302 to the list of associated roles.
304 =item B<does_role ($role_name)>
306 This will test if this class C<does> a given C<$role_name>. It will
307 not only check it's local roles, but ask them as well in order to
308 cascade down the role hierarchy.
310 =item B<add_attribute $attr_name, %params>
312 This method does the same thing as L<Class::MOP::Class/add_attribute>, but adds
313 suport for delegation.
317 =head1 INTERNAL METHODS
321 =item compute_delegation
323 =item generate_delegation_list
325 =item generate_delgate_method
327 =item get_delegatable_methods
333 All complex software has bugs lurking in it, and this module is no
334 exception. If you find a bug please either email me, or add the bug
339 Stevan Little E<lt>stevan@iinteractive.comE<gt>
341 =head1 COPYRIGHT AND LICENSE
343 Copyright 2006 by Infinity Interactive, Inc.
345 L<http://www.iinteractive.com>
347 This library is free software; you can redistribute it and/or modify
348 it under the same terms as Perl itself.