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