Make ->isa and ->can work when called on the class itself
[gitmo/MooseX-Types.git] / lib / MooseX / Types / TypeDecorator.pm
CommitLineData
4c2125a4 1package MooseX::Types::TypeDecorator;
ef8b7b7a 2
3#ABSTRACT: Wraps Moose::Meta::TypeConstraint objects with added features
4c2125a4 4
a706b0f2 5use strict;
6use warnings;
4c2125a4 7
bb5b7b28 8use Carp::Clan qw( ^MooseX::Types );
475bbd1d 9use Moose::Util::TypeConstraints ();
bb5b7b28 10use Moose::Meta::TypeConstraint::Union;
371efa05 11use Scalar::Util qw(blessed);
bb5b7b28 12
4c2125a4 13use overload(
1f071601 14 '0+' => sub {
15 my $self = shift @_;
16 my $tc = $self->{__type_constraint};
17 return 0+$tc;
18 },
4c2125a4 19 '""' => sub {
c1260541 20 my $self = shift @_;
21 if(blessed $self) {
22 return $self->__type_constraint->name;
23 } else {
24 return "$self";
25 }
4c2125a4 26 },
1f071601 27 bool => sub { 1 },
cf1a8bfa 28 '|' => sub {
686e5888 29
30 ## It's kind of ugly that we need to know about Union Types, but this
31 ## is needed for syntax compatibility. Maybe someday we'll all just do
32 ## Or[Str,Str,Int]
442e42ba 33
0d07f026 34 my @args = @_[0,1]; ## arg 3 is special, see the overload docs.
35 my @tc = grep {blessed $_} map {
36 blessed $_ ? $_ :
37 Moose::Util::TypeConstraints::find_or_parse_type_constraint($_)
3ade1c44 38 || __PACKAGE__->_throw_error( "$_ is not a type constraint")
0d07f026 39 } @args;
40
41 ( scalar @tc == scalar @args)
3ade1c44 42 || __PACKAGE__->_throw_error(
43 "one of your type constraints is bad. Passed: ". join(', ', @args) ." Got: ". join(', ', @tc));
0d07f026 44
45 ( scalar @tc >= 2 )
3ade1c44 46 || __PACKAGE__->_throw_error("You must pass in at least 2 type names to make a union");
442e42ba 47
bb5b7b28 48 my $union = Moose::Meta::TypeConstraint::Union->new(type_constraints=>\@tc);
49 return Moose::Util::TypeConstraints::register_type_constraint($union);
cf1a8bfa 50 },
1d9a68a6 51 fallback => 1,
52
4c2125a4 53);
54
4c2125a4 55=head1 DESCRIPTION
56
57This is a decorator object that contains an underlying type constraint. We use
58this to control access to the type constraint and to add some features.
59
a706b0f2 60=head1 METHODS
4c2125a4 61
a706b0f2 62This class defines the following methods.
4c2125a4 63
a706b0f2 64=head2 new
4c2125a4 65
a706b0f2 66Old school instantiation
4c2125a4 67
68=cut
69
a706b0f2 70sub new {
06cab001 71 my $proto = shift;
72 if (ref($proto)) {
73 return $proto->_try_delegate('new', @_);
74 }
75 my $class = $proto;
475bbd1d 76 if(my $arg = shift @_) {
371efa05 77 if(blessed $arg && $arg->isa('Moose::Meta::TypeConstraint')) {
475bbd1d 78 return bless {'__type_constraint'=>$arg}, $class;
e7d06577 79 } elsif(
80 blessed $arg &&
81 $arg->isa('MooseX::Types::UndefinedType')
82 ) {
475bbd1d 83 ## stub in case we'll need to handle these types differently
84 return bless {'__type_constraint'=>$arg}, $class;
371efa05 85 } elsif(blessed $arg) {
3ade1c44 86 __PACKAGE__->_throw_error("Argument must be ->isa('Moose::Meta::TypeConstraint') or ->isa('MooseX::Types::UndefinedType'), not ". blessed $arg);
475bbd1d 87 } else {
3ade1c44 88 __PACKAGE__->_throw_error("Argument cannot be '$arg'");
475bbd1d 89 }
bb5b7b28 90 } else {
3ade1c44 91 __PACKAGE__->_throw_error("This method [new] requires a single argument.");
bb5b7b28 92 }
a706b0f2 93}
4c2125a4 94
5a9b6d38 95=head2 __type_constraint ($type_constraint)
4c2125a4 96
e088dd03 97Set/Get the type_constraint.
4c2125a4 98
99=cut
100
475bbd1d 101sub __type_constraint {
c1260541 102 my $self = shift @_;
371efa05 103 if(blessed $self) {
104 if(defined(my $tc = shift @_)) {
105 $self->{__type_constraint} = $tc;
106 }
107 return $self->{__type_constraint};
108 } else {
3ade1c44 109 __PACKAGE__->_throw_error('cannot call __type_constraint as a class method');
a706b0f2 110 }
a706b0f2 111}
4c2125a4 112
bb5b7b28 113=head2 isa
114
989b0570 115handle $self->isa since AUTOLOAD can't - this tries both the type constraint,
116and for a class type, the class.
bb5b7b28 117
118=cut
119
48ec5fb3 120sub isa {
6e73ec86 121 my $self = shift;
122 return
b325a217 123 blessed $self
124 ? $self->__type_constraint->isa(@_)
125 || $self->_try_delegate( 'isa', @_ )
126 : $self->SUPER::isa(@_);
48ec5fb3 127}
3ade1c44 128
bb5b7b28 129=head2 can
130
131handle $self->can since AUTOLOAD can't.
132
133=cut
134
b325a217 135sub can {
136 my $self = shift;
137
138 return blessed $self
139 ? $self->_try_delegate( 'can', @_ )
140 : $self->SUPER::can(@_);
141}
c1260541 142
3ade1c44 143=head2 _throw_error
144
145properly delegate error messages
146
147=cut
148
149sub _throw_error {
150 shift;
151 require Moose;
152 unshift @_, 'Moose';
153 goto &Moose::throw_error;
154}
c1260541 155
a706b0f2 156=head2 DESTROY
4c2125a4 157
a706b0f2 158We might need it later
4c2125a4 159
a706b0f2 160=cut
4c2125a4 161
a706b0f2 162sub DESTROY {
163 return;
164}
4c2125a4 165
a706b0f2 166=head2 AUTOLOAD
4c2125a4 167
989b0570 168Delegate to the decorator target, unless this is a class type, in which
ee3f4093 169case it will try to delegate to the type object, then if that fails try
7f95d0bf 170the class. The method 'new' is special cased to only be permitted on
171the class; if there is no class, or it does not provide a new method,
172an exception will be thrown.
4c2125a4 173
a706b0f2 174=cut
4c2125a4 175
e088dd03 176sub AUTOLOAD {
475bbd1d 177 my ($self, @args) = @_;
a706b0f2 178 my ($method) = (our $AUTOLOAD =~ /([^:]+)$/);
077ac262 179
180 ## We delegate with this method in an attempt to support a value of
181 ## __type_constraint which is also AUTOLOADing, in particular the class
182 ## MooseX::Types::UndefinedType which AUTOLOADs during autovivication.
06cab001 183
184 $self->_try_delegate($method, @args);
185}
186
187sub _try_delegate {
188 my ($self, $method, @args) = @_;
189 my $tc = $self->__type_constraint;
cabfc8ed 190 my $class;
d7d0bd99 191 if ($tc->can('is_subtype_of')) { # Union can't
192 my $search_tc = $tc;
193 while (1) {
194 if ($search_tc->isa('Moose::Meta::TypeConstraint::Class')) {
195 $class = $search_tc->class;
196 last;
197 }
198 $search_tc = $search_tc->parent;
e513c4a5 199 last unless $search_tc && $search_tc->is_subtype_of('Object');
cabfc8ed 200 }
cabfc8ed 201 }
202
ee3f4093 203 my $inv = do {
7f95d0bf 204 if ($method eq 'new') {
205 die "new called on type decorator for non-class-type ".$tc->name
206 unless $class;
207 die "new called on class type decorator ".$tc->name."\n"
208 ." for class ${class}\n"
209 ." which does not provide a new method - did you forget to load it?"
210 unless $class->can('new');
211 $class
212 } elsif ($class && !$tc->can($method)) {
ee3f4093 213 $class
214 } else {
215 $tc
216 }
217 };
989b0570 218
06cab001 219 $inv->$method(@args);
a706b0f2 220}
4c2125a4 221
4c2125a4 222=head1 LICENSE
223
224This program is free software; you can redistribute it and/or modify
225it under the same terms as perl itself.
226
227=cut
228
2291;