Commit | Line | Data |
d60c78b9 |
1 | #!/usr/bin/env perl |
2 | package Mouse::TypeRegistry; |
3 | use strict; |
4 | use warnings; |
9baf5d6b |
5 | |
61a02a3a |
6 | use Carp (); |
29312fc3 |
7 | use Mouse::Util qw/blessed looks_like_number openhandle/; |
d60c78b9 |
8 | |
4188b837 |
9 | my $SUBTYPE = +{}; |
10 | my $COERCE = +{}; |
11 | |
12 | sub import { |
13 | my $class = shift; |
14 | my %args = @_; |
15 | my $caller = caller(0); |
16 | |
4188b837 |
17 | if (defined $args{'-export'} && ref($args{'-export'}) eq 'ARRAY') { |
18 | no strict 'refs'; |
19 | *{"$caller\::import"} = sub { _import(@_) }; |
20 | } |
21 | |
22 | no strict 'refs'; |
61a02a3a |
23 | *{"$caller\::as"} = \&_as; |
24 | *{"$caller\::where"} = \&_where; |
25 | *{"$caller\::message"} = \&_message; |
26 | *{"$caller\::from"} = \&_from; |
27 | *{"$caller\::via"} = \&_via; |
4188b837 |
28 | *{"$caller\::subtype"} = \&_subtype; |
29 | *{"$caller\::coerce"} = \&_coerce; |
ecc6e3b1 |
30 | *{"$caller\::class_type"} = \&_class_type; |
47f36c05 |
31 | *{"$caller\::role_type"} = \&_role_type; |
4188b837 |
32 | } |
33 | |
61a02a3a |
34 | |
35 | sub _as ($) { |
36 | as => $_[0] |
37 | } |
38 | sub _where (&) { |
39 | where => $_[0] |
40 | } |
41 | sub _message ($) { |
42 | message => $_[0] |
43 | } |
44 | |
45 | sub _from { @_ } |
46 | sub _via (&) { |
47 | $_[0] |
48 | } |
49 | |
4188b837 |
50 | sub _import { |
51 | my($class, @types) = @_; |
52 | return unless exists $SUBTYPE->{$class} && exists $COERCE->{$class}; |
53 | my $pkg = caller(1); |
54 | return unless @types; |
55 | copy_types($class, $pkg, @types); |
56 | } |
57 | |
58 | sub _subtype { |
59 | my $pkg = caller(0); |
61a02a3a |
60 | my($name, %conf) = @_; |
61 | if (my $type = $SUBTYPE->{$name}) { |
62 | Carp::croak "The type constraint '$name' has already been created, cannot be created again in $pkg"; |
63 | }; |
64 | my $as = $conf{as}; |
65 | my $stuff = $conf{where} || optimized_constraints()->{$as}; |
66 | |
67 | $SUBTYPE->{$name} = $stuff; |
4188b837 |
68 | } |
69 | |
70 | sub _coerce { |
61a02a3a |
71 | my($name, %conf) = @_; |
72 | |
73 | Carp::croak "Cannot find type '$name', perhaps you forgot to load it." |
74 | unless optimized_constraints()->{$name}; |
75 | |
76 | my $subtypes = optimized_constraints(); |
77 | $COERCE->{$name} ||= {}; |
78 | while (my($type, $code) = each %conf) { |
79 | Carp::croak "A coercion action already exists for '$type'" |
80 | if $COERCE->{$name}->{$type}; |
81 | |
82 | Carp::croak "Could not find the type constraint ($type) to coerce from" |
83 | unless $subtypes->{$type}; |
84 | |
85 | $COERCE->{$name}->{$type} = $code; |
86 | } |
4188b837 |
87 | } |
88 | |
ecc6e3b1 |
89 | sub _class_type { |
90 | my $pkg = caller(0); |
ecc6e3b1 |
91 | my($name, $conf) = @_; |
92 | my $class = $conf->{class}; |
61a02a3a |
93 | _subtype( |
94 | $name => where => sub { |
95 | defined $_ && ref($_) eq $class; |
96 | } |
97 | ); |
ecc6e3b1 |
98 | } |
99 | |
47f36c05 |
100 | sub _role_type { |
47f36c05 |
101 | my($name, $conf) = @_; |
102 | my $role = $conf->{role}; |
61a02a3a |
103 | _subtype( |
104 | $name => where => sub { |
105 | return unless defined $_ && ref($_) && $_->isa('Mouse::Object'); |
106 | $_->meta->does_role($role); |
107 | } |
108 | ); |
47f36c05 |
109 | } |
110 | |
4188b837 |
111 | sub typecast_constraints { |
112 | my($class, $pkg, $type, $value) = @_; |
61a02a3a |
113 | return $value unless $COERCE->{$type}; |
4188b837 |
114 | |
115 | my $optimized_constraints = optimized_constraints(); |
61a02a3a |
116 | for my $coerce_type (keys %{ $COERCE->{$type} }) { |
4188b837 |
117 | local $_ = $value; |
118 | if ($optimized_constraints->{$coerce_type}->()) { |
119 | local $_ = $value; |
61a02a3a |
120 | return $COERCE->{$type}->{$coerce_type}->(); |
4188b837 |
121 | } |
122 | } |
123 | |
4188b837 |
124 | return $value; |
125 | } |
126 | |
127 | { |
128 | no warnings 'uninitialized'; |
129 | my $optimized_constraints = { |
d60c78b9 |
130 | Any => sub { 1 }, |
131 | Item => sub { 1 }, |
132 | Bool => sub { |
133 | !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' |
134 | }, |
f5fbe3cc |
135 | Undef => sub { !defined($_) }, |
136 | Defined => sub { defined($_) }, |
0f636a97 |
137 | Value => sub { defined($_) && !ref($_) }, |
138 | Num => sub { !ref($_) && looks_like_number($_) }, |
139 | Int => sub { defined($_) && !ref($_) && /^-?[0-9]+$/ }, |
140 | Str => sub { defined($_) && !ref($_) }, |
79af4b55 |
141 | ClassName => sub { Mouse::is_class_loaded($_) }, |
0f636a97 |
142 | Ref => sub { ref($_) }, |
143 | |
144 | ScalarRef => sub { ref($_) eq 'SCALAR' }, |
145 | ArrayRef => sub { ref($_) eq 'ARRAY' }, |
146 | HashRef => sub { ref($_) eq 'HASH' }, |
147 | CodeRef => sub { ref($_) eq 'CODE' }, |
148 | RegexpRef => sub { ref($_) eq 'Regexp' }, |
149 | GlobRef => sub { ref($_) eq 'GLOB' }, |
150 | |
151 | FileHandle => sub { |
152 | ref($_) eq 'GLOB' |
153 | && openhandle($_) |
154 | or |
155 | blessed($_) |
156 | && $_->isa("IO::Handle") |
157 | }, |
158 | |
159 | Object => sub { blessed($_) && blessed($_) ne 'Regexp' }, |
d60c78b9 |
160 | }; |
4188b837 |
161 | sub optimized_constraints { |
61a02a3a |
162 | return { %{ $SUBTYPE }, %{ $optimized_constraints } }; |
4188b837 |
163 | } |
d60c78b9 |
164 | } |
165 | |
166 | 1; |
167 | |
6feb83f1 |
168 | __END__ |
169 | |
170 | =head1 NAME |
171 | |
172 | Mouse::TypeRegistry - simple type constraints |
173 | |
174 | =head1 METHODS |
175 | |
176 | =head2 optimized_constraints -> HashRef[CODE] |
177 | |
178 | Returns the simple type constraints that Mouse understands. |
179 | |
180 | =cut |
181 | |
182 | |