4c2bc8eb1a2d02a03d8efe5e93e0f9ff93c9b926
[gitmo/MooseX-Types.git] / lib / MooseX / Types / TypeDecorator.pm
1 package MooseX::Types::TypeDecorator;
2
3 #ABSTRACT: Wraps Moose::Meta::TypeConstraint objects with added features
4
5 use strict;
6 use warnings;
7
8 use Carp::Clan qw( ^MooseX::Types );
9 use Moose::Util::TypeConstraints ();
10 use Moose::Meta::TypeConstraint::Union;
11 use Scalar::Util qw(blessed);
12
13 use overload(
14     '0+' => sub {
15             my $self = shift @_;
16             my $tc = $self->{__type_constraint};
17             return 0+$tc;
18      },
19     '""' => sub {
20                 my $self = shift @_;
21                 if(blessed $self) {
22                         return $self->__type_constraint->name;                  
23                 } else {
24                         return "$self";
25                 }
26     },
27     bool => sub { 1 },
28     '|' => sub {
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]
33
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($_)
38               || __PACKAGE__->_throw_error( "$_ is not a type constraint")
39         } @args;
40
41         ( scalar @tc == scalar @args)
42             || __PACKAGE__->_throw_error(
43                           "one of your type constraints is bad.  Passed: ". join(', ', @args) ." Got: ". join(', ', @tc));
44
45         ( scalar @tc >= 2 )
46             || __PACKAGE__->_throw_error("You must pass in at least 2 type names to make a union");
47
48         my $union = Moose::Meta::TypeConstraint::Union->new(type_constraints=>\@tc);
49         return Moose::Util::TypeConstraints::register_type_constraint($union);
50     },
51     fallback => 1,
52     
53 );
54
55 =head1 DESCRIPTION
56
57 This is a decorator object that contains an underlying type constraint.  We use
58 this to control access to the type constraint and to add some features.
59
60 =head1 METHODS
61
62 This class defines the following methods.
63
64 =head2 new
65
66 Old school instantiation
67
68 =cut
69
70 sub new {
71     my $class = shift @_;
72     if(my $arg = shift @_) {
73         if(blessed $arg && $arg->isa('Moose::Meta::TypeConstraint')) {
74             return bless {'__type_constraint'=>$arg}, $class;
75         } elsif(
76             blessed $arg &&
77             $arg->isa('MooseX::Types::UndefinedType') 
78           ) {
79             ## stub in case we'll need to handle these types differently
80             return bless {'__type_constraint'=>$arg}, $class;
81         } elsif(blessed $arg) {
82             __PACKAGE__->_throw_error("Argument must be ->isa('Moose::Meta::TypeConstraint') or ->isa('MooseX::Types::UndefinedType'), not ". blessed $arg);
83         } else {
84             __PACKAGE__->_throw_error("Argument cannot be '$arg'");
85         }
86     } else {
87         __PACKAGE__->_throw_error("This method [new] requires a single argument.");        
88     }
89 }
90
91 =head2 __type_constraint ($type_constraint)
92
93 Set/Get the type_constraint.
94
95 =cut
96
97 sub __type_constraint {
98     my $self = shift @_;    
99     if(blessed $self) {
100         if(defined(my $tc = shift @_)) {
101             $self->{__type_constraint} = $tc;
102         }
103         return $self->{__type_constraint};        
104     } else {
105         __PACKAGE__->_throw_error('cannot call __type_constraint as a class method');
106     }
107 }
108
109 =head2 isa
110
111 handle $self->isa since AUTOLOAD can't.
112
113 =cut
114
115 sub isa {
116     my ($self, $target) = @_;  
117     if(defined $target) {
118         if(blessed $self) {
119                 return $self->__type_constraint->isa($target);
120         } else {
121                 return;
122         }
123     } else {
124         return;
125     }
126 }
127
128
129 =head2 can
130
131 handle $self->can since AUTOLOAD can't.
132
133 =cut
134
135 sub can {
136     my ($self, $target) = @_;
137     if(defined $target) {
138         if(blessed $self) {
139                 return $self->__type_constraint->can($target);
140         } else {
141                 return;
142         }
143     } else {
144         return;
145     }
146 }
147
148 =head2 meta
149
150 have meta examine the underlying type constraints
151
152 =cut
153
154 sub meta {
155         my $self = shift @_;
156         if(blessed $self) {
157                 return $self->__type_constraint->meta;
158         } 
159 }
160
161 =head2 _throw_error
162
163 properly delegate error messages
164
165 =cut
166
167 sub _throw_error {
168     shift;
169     require Moose;
170     unshift @_, 'Moose';
171     goto &Moose::throw_error;
172 }
173
174 =head2 DESTROY
175
176 We might need it later
177
178 =cut
179
180 sub DESTROY {
181     return;
182 }
183
184 =head2 AUTOLOAD
185
186 Delegate to the decorator targe
187
188 =cut
189
190 sub AUTOLOAD {
191     
192     my ($self, @args) = @_;
193     my ($method) = (our $AUTOLOAD =~ /([^:]+)$/);
194     
195     ## We delegate with this method in an attempt to support a value of
196     ## __type_constraint which is also AUTOLOADing, in particular the class
197     ## MooseX::Types::UndefinedType which AUTOLOADs during autovivication.
198     
199     my $return;
200     eval {
201         $return = $self->__type_constraint->$method(@args);
202     }; if($@) {
203         __PACKAGE__->_throw_error($@);
204     } else {
205         return $return;
206     }
207 }
208
209 =head1 LICENSE
210
211 This program is free software; you can redistribute it and/or modify
212 it under the same terms as perl itself.
213
214 =cut
215
216 1;