Fix is_subtype_of to handle not-yet-defined role
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint / Role.pm
CommitLineData
620db045 1package Moose::Meta::TypeConstraint::Role;
2
3use strict;
4use warnings;
5use metaclass;
6
089455fc 7use B;
620db045 8use Scalar::Util 'blessed';
9use Moose::Util::TypeConstraints ();
10
620db045 11use base 'Moose::Meta::TypeConstraint';
12
13__PACKAGE__->meta->add_attribute('role' => (
14 reader => 'role',
dc2b7cc8 15 Class::MOP::_definition_context(),
620db045 16));
17
089455fc 18my $inliner = sub {
19 my $self = shift;
20 my $val = shift;
21
3975b592 22 return 'Moose::Util::does_role('
23 . $val . ', '
24 . B::perlstring($self->role)
25 . ')';
089455fc 26};
27
620db045 28sub new {
29 my ( $class, %args ) = @_;
30
f37d0936 31 $args{parent} = Moose::Util::TypeConstraints::find_type_constraint('Object');
7ae2d933 32
33 my $role_name = $args{role};
34 $args{constraint} = sub { Moose::Util::does_role( $_[0], $role_name ) };
35
089455fc 36 $args{inlined} = $inliner;
37
92a88343 38 my $self = $class->SUPER::new( \%args );
620db045 39
40 $self->_create_hand_optimized_type_constraint;
41 $self->compile_type_constraint();
42
43 return $self;
44}
45
46sub _create_hand_optimized_type_constraint {
47 my $self = shift;
48 my $role = $self->role;
49 $self->hand_optimized_type_constraint(
50 sub { Moose::Util::does_role($_[0], $role) }
51 );
52}
53
54sub parents {
55 my $self = shift;
56 return (
57 $self->parent,
58 map {
59 # FIXME find_type_constraint might find a TC named after the role but that isn't really it
60 # I did this anyway since it's a convention that preceded TypeConstraint::Role, and it should DWIM
61 # if anybody thinks this problematic please discuss on IRC.
62 # a possible fix is to add by attr indexing to the type registry to find types of a certain property
63 # regardless of their name
d03bd989 64 Moose::Util::TypeConstraints::find_type_constraint($_)
65 ||
620db045 66 __PACKAGE__->new( role => $_, name => "__ANON__" )
1551760b 67 } @{ Class::MOP::class_of($self->role)->get_roles },
620db045 68 );
69}
70
71sub equals {
72 my ( $self, $type_or_name ) = @_;
73
74 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
75
4c015454 76 return unless defined $other;
620db045 77 return unless $other->isa(__PACKAGE__);
78
79 return $self->role eq $other->role;
80}
81
82sub is_a_type_of {
83 my ($self, $type_or_name) = @_;
84
8d33489c 85 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
620db045 86
87 ($self->equals($type) || $self->is_subtype_of($type_or_name));
88}
89
90sub is_subtype_of {
91 my ($self, $type_or_name_or_role ) = @_;
92
93 if ( not ref $type_or_name_or_role ) {
94 # it might be a role
0031c50c 95 my $class = Class::MOP::class_of($self->role);
96 return 1 if defined($class) && $class->does_role( $type_or_name_or_role );
620db045 97 }
98
99 my $type = Moose::Util::TypeConstraints::find_type_constraint($type_or_name_or_role);
100
4c015454 101 return unless defined $type;
d03bd989 102
620db045 103 if ( $type->isa(__PACKAGE__) ) {
104 # if $type_or_name_or_role isn't a role, it might be the TC name of another ::Role type
105 # or it could also just be a type object in this branch
0031c50c 106 my $class = Class::MOP::class_of($self->role);
107 return defined($class) && $class->does_role( $type->role );
620db045 108 } else {
109 # the only other thing we are a subtype of is Object
110 $self->SUPER::is_subtype_of($type);
111 }
112}
113
2fb4885e 114sub create_child_type {
115 my ($self, @args) = @_;
39170e48 116 return Moose::Meta::TypeConstraint->new(@args, parent => $self);
2fb4885e 117}
118
620db045 1191;
120
ad46f524 121# ABSTRACT: Role/TypeConstraint parallel hierarchy
122
620db045 123__END__
124
125=pod
126
3cb26ff4 127=head1 DESCRIPTION
128
129This class represents type constraints for a role.
130
131=head1 INHERITANCE
132
133C<Moose::Meta::TypeConstraint::Role> is a subclass of
134L<Moose::Meta::TypeConstraint>.
135
620db045 136=head1 METHODS
137
138=over 4
139
3cb26ff4 140=item B<< Moose::Meta::TypeConstraint::Role->new(%options) >>
141
142This creates a new role type constraint based on the given
143C<%options>.
144
145It takes the same options as its parent, with two exceptions. First,
146it requires an additional option, C<role>, which is name of the
147constraint's role. Second, it automatically sets the parent to the
148C<Object> type.
149
150The constructor also overrides the hand optimized type constraint with
151one it creates internally.
620db045 152
3cb26ff4 153=item B<< $constraint->role >>
620db045 154
3cb26ff4 155Returns the role name associated with the constraint.
620db045 156
3cb26ff4 157=item B<< $constraint->parents >>
620db045 158
3cb26ff4 159Returns all the type's parent types, corresponding to the roles that
160its role does.
620db045 161
2870fb09 162=item B<< $constraint->is_subtype_of($type_name_or_object) >>
620db045 163
3cb26ff4 164If the given type is also a role type, then this checks that the
165type's role does the other type's role.
620db045 166
3cb26ff4 167Otherwise it falls back to the implementation in
168L<Moose::Meta::TypeConstraint>.
2fb4885e 169
3cb26ff4 170=item B<< $constraint->create_child_type(%options) >>
620db045 171
3cb26ff4 172This returns a new L<Moose::Meta::TypeConstraint> object with the type
173as its parent.
620db045 174
3cb26ff4 175Note that it does I<not> return a C<Moose::Meta::TypeConstraint::Role>
176object!
620db045 177
178=back
179
180=head1 BUGS
181
d4048ef3 182See L<Moose/BUGS> for details on reporting bugs.
620db045 183
620db045 184=cut