try again you muppet
[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
162the class. The method 'new' is special cased to go to the class first
163if present.
4c2125a4 164
a706b0f2 165=cut
4c2125a4 166
e088dd03 167sub AUTOLOAD {
475bbd1d 168 my ($self, @args) = @_;
a706b0f2 169 my ($method) = (our $AUTOLOAD =~ /([^:]+)$/);
077ac262 170
171 ## We delegate with this method in an attempt to support a value of
172 ## __type_constraint which is also AUTOLOADing, in particular the class
173 ## MooseX::Types::UndefinedType which AUTOLOADs during autovivication.
06cab001 174
175 $self->_try_delegate($method, @args);
176}
177
178sub _try_delegate {
179 my ($self, $method, @args) = @_;
180 my $tc = $self->__type_constraint;
cabfc8ed 181 my $class;
d7d0bd99 182 if ($tc->can('is_subtype_of')) { # Union can't
183 my $search_tc = $tc;
184 while (1) {
185 if ($search_tc->isa('Moose::Meta::TypeConstraint::Class')) {
186 $class = $search_tc->class;
187 last;
188 }
189 $search_tc = $search_tc->parent;
190 last unless $search_tc->is_subtype_of('Object');
cabfc8ed 191 }
cabfc8ed 192 }
193
ee3f4093 194 my $inv = do {
195 if ($tc->can($method) and $method ne 'new') {
196 $tc
197 } elsif ($class && $class->can($method)) {
198 $class
199 } else {
200 $tc
201 }
202 };
989b0570 203
06cab001 204 $inv->$method(@args);
a706b0f2 205}
4c2125a4 206
4c2125a4 207=head1 LICENSE
208
209This program is free software; you can redistribute it and/or modify
210it under the same terms as perl itself.
211
212=cut
213
2141;