Don't assume that $search_tc->parent returns an object!
[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
123 $self->__type_constraint->isa(@_)
124 || $self->_try_delegate('isa', @_);
48ec5fb3 125}
3ade1c44 126
bb5b7b28 127=head2 can
128
129handle $self->can since AUTOLOAD can't.
130
131=cut
132
06cab001 133sub can { shift->_try_delegate('can', @_) }
c1260541 134
3ade1c44 135=head2 _throw_error
136
137properly delegate error messages
138
139=cut
140
141sub _throw_error {
142 shift;
143 require Moose;
144 unshift @_, 'Moose';
145 goto &Moose::throw_error;
146}
c1260541 147
a706b0f2 148=head2 DESTROY
4c2125a4 149
a706b0f2 150We might need it later
4c2125a4 151
a706b0f2 152=cut
4c2125a4 153
a706b0f2 154sub DESTROY {
155 return;
156}
4c2125a4 157
a706b0f2 158=head2 AUTOLOAD
4c2125a4 159
989b0570 160Delegate to the decorator target, unless this is a class type, in which
ee3f4093 161case it will try to delegate to the type object, then if that fails try
7f95d0bf 162the class. The method 'new' is special cased to only be permitted on
163the class; if there is no class, or it does not provide a new method,
164an exception will be thrown.
4c2125a4 165
a706b0f2 166=cut
4c2125a4 167
e088dd03 168sub AUTOLOAD {
475bbd1d 169 my ($self, @args) = @_;
a706b0f2 170 my ($method) = (our $AUTOLOAD =~ /([^:]+)$/);
077ac262 171
172 ## We delegate with this method in an attempt to support a value of
173 ## __type_constraint which is also AUTOLOADing, in particular the class
174 ## MooseX::Types::UndefinedType which AUTOLOADs during autovivication.
06cab001 175
176 $self->_try_delegate($method, @args);
177}
178
179sub _try_delegate {
180 my ($self, $method, @args) = @_;
181 my $tc = $self->__type_constraint;
cabfc8ed 182 my $class;
d7d0bd99 183 if ($tc->can('is_subtype_of')) { # Union can't
184 my $search_tc = $tc;
185 while (1) {
186 if ($search_tc->isa('Moose::Meta::TypeConstraint::Class')) {
187 $class = $search_tc->class;
188 last;
189 }
190 $search_tc = $search_tc->parent;
e513c4a5 191 last unless $search_tc && $search_tc->is_subtype_of('Object');
cabfc8ed 192 }
cabfc8ed 193 }
194
ee3f4093 195 my $inv = do {
7f95d0bf 196 if ($method eq 'new') {
197 die "new called on type decorator for non-class-type ".$tc->name
198 unless $class;
199 die "new called on class type decorator ".$tc->name."\n"
200 ." for class ${class}\n"
201 ." which does not provide a new method - did you forget to load it?"
202 unless $class->can('new');
203 $class
204 } elsif ($class && !$tc->can($method)) {
ee3f4093 205 $class
206 } else {
207 $tc
208 }
209 };
989b0570 210
06cab001 211 $inv->$method(@args);
a706b0f2 212}
4c2125a4 213
4c2125a4 214=head1 LICENSE
215
216This program is free software; you can redistribute it and/or modify
217it under the same terms as perl itself.
218
219=cut
220
2211;