Commit | Line | Data |
52d358e2 |
1 | package MooseX::Types; |
8af0a70d |
2 | |
3 | =head1 NAME |
4 | |
52d358e2 |
5 | MooseX::Types - Organise your Moose types in libraries |
8af0a70d |
6 | |
7 | =cut |
8 | |
3df5416a |
9 | #use warnings; |
10 | #use strict; |
8af0a70d |
11 | |
12 | use Sub::Uplevel; |
13 | use Moose::Util::TypeConstraints; |
9616cebc |
14 | use MooseX::Types::Base (); |
15 | use MooseX::Types::Util qw( filter_tags ); |
52d358e2 |
16 | use MooseX::Types::UndefinedType; |
9616cebc |
17 | use Sub::Install qw( install_sub ); |
249888e7 |
18 | use Carp qw( croak ); |
3df5416a |
19 | use Moose; |
9616cebc |
20 | |
21 | use namespace::clean -except => [qw( meta )]; |
8af0a70d |
22 | |
b54b55ff |
23 | our $VERSION = 0.02; |
8af0a70d |
24 | |
25 | my $UndefMsg = q{Action for type '%s' not yet defined in library '%s'}; |
26 | |
27 | =head1 SYNOPSIS |
28 | |
9616cebc |
29 | =head2 Library Definition |
30 | |
8af0a70d |
31 | package MyLibrary; |
32 | use strict; |
33 | |
34 | # predeclare our own types |
52d358e2 |
35 | use MooseX::Types |
8af0a70d |
36 | -declare => [qw( PositiveInt NegativeInt )]; |
37 | |
38 | # import builtin types |
52d358e2 |
39 | use MooseX::Types::Moose 'Int'; |
8af0a70d |
40 | |
41 | # type definition |
42 | subtype PositiveInt, |
43 | as Int, |
44 | where { $_ > 0 }, |
45 | message { "Int is not larger than 0" }; |
46 | |
47 | subtype NegativeInt, |
48 | as Int, |
49 | where { $_ < 0 }, |
50 | message { "Int is not smaller than 0" }; |
51 | |
52 | # type coercion |
53 | coerce PositiveInt, |
54 | from Int, |
55 | via { 1 }; |
56 | |
57 | 1; |
58 | |
9616cebc |
59 | =head2 Usage |
60 | |
8af0a70d |
61 | package Foo; |
62 | use Moose; |
63 | use MyLibrary qw( PositiveInt NegativeInt ); |
64 | |
65 | # use the exported constants as type names |
66 | has 'bar', |
67 | isa => PositiveInt, |
68 | is => 'rw'; |
69 | has 'baz', |
70 | isa => NegativeInt, |
71 | is => 'rw'; |
72 | |
73 | sub quux { |
74 | my ($self, $value); |
75 | |
76 | # test the value |
77 | print "positive\n" if is_PositiveInt($value); |
78 | print "negative\n" if is_NegativeInt($value); |
79 | |
80 | # coerce the value, NegativeInt doesn't have a coercion |
81 | # helper, since it didn't define any coercions. |
82 | $value = to_PositiveInt($value) or die "Cannot coerce"; |
83 | } |
84 | |
85 | 1; |
86 | |
87 | =head1 DESCRIPTION |
88 | |
89 | The types provided with L<Moose> are by design global. This package helps |
90 | you to organise and selectively import your own and the built-in types in |
91 | libraries. As a nice side effect, it catches typos at compile-time too. |
92 | |
93 | However, the main reason for this module is to provide an easy way to not |
94 | have conflicts with your type names, since the internal fully qualified |
95 | names of the types will be prefixed with the library's name. |
96 | |
97 | This module will also provide you with some helper functions to make it |
98 | easier to use Moose types in your code. |
99 | |
100 | =head1 TYPE HANDLER FUNCTIONS |
101 | |
102 | =head2 $type |
103 | |
104 | A constant with the name of your type. It contains the type's fully |
105 | qualified name. Takes no value, as all constants. |
106 | |
107 | =head2 is_$type |
108 | |
109 | This handler takes a value and tests if it is a valid value for this |
110 | C<$type>. It will return true or false. |
111 | |
112 | =head2 to_$type |
113 | |
114 | A handler that will take a value and coerce it into the C<$type>. It will |
115 | return a false value if the type could not be coerced. |
116 | |
117 | B<Important Note>: This handler will only be exported for types that can |
118 | do type coercion. This has the advantage that a coercion to a type that |
119 | cannot hasn't defined any coercions will lead to a compile-time error. |
120 | |
121 | =head1 LIBRARY DEFINITION |
122 | |
52d358e2 |
123 | A MooseX::Types is just a normal Perl module. Unlike Moose |
8af0a70d |
124 | itself, it does not install C<use strict> and C<use warnings> in your |
125 | class by default, so this is up to you. |
126 | |
127 | The only thing a library is required to do is |
128 | |
52d358e2 |
129 | use MooseX::Types -declare => \@types; |
8af0a70d |
130 | |
131 | with C<@types> being a list of types you wish to define in this library. |
132 | This line will install a proper base class in your package as well as the |
133 | full set of L<handlers|/"TYPE HANDLER FUNCTIONS"> for your declared |
134 | types. It will then hand control over to L<Moose::Util::TypeConstraints>' |
135 | C<import> method to export the functions you will need to declare your |
136 | types. |
137 | |
138 | If you want to use Moose' built-in types (e.g. for subtyping) you will |
139 | want to |
140 | |
52d358e2 |
141 | use MooseX::Types::Moose @types; |
8af0a70d |
142 | |
52d358e2 |
143 | to import the helpers from the shipped L<MooseX::Types::Moose> |
8af0a70d |
144 | library which can export all types that come with Moose. |
145 | |
146 | You will have to define coercions for your types or your library won't |
147 | export a L</to_$type> coercion helper for it. |
148 | |
249888e7 |
149 | Note that you currently cannot define types containint C<::>, since |
150 | exporting would be a problem. |
151 | |
8af0a70d |
152 | =head1 LIBRARY USAGE |
153 | |
154 | You can import the L<"type helpers"|/"TYPE HANDLER FUNCTIONS"> of a |
155 | library by C<use>ing it with a list of types to import as arguments. If |
156 | you want all of them, use the C<:all> tag. For example: |
157 | |
158 | use MyLibrary ':all'; |
159 | use MyOtherLibrary qw( TypeA TypeB ); |
160 | |
52d358e2 |
161 | MooseX::Types comes with a library of Moose' built-in types called |
162 | L<MooseX::Types::Moose>. |
8af0a70d |
163 | |
c20dc98b |
164 | =head1 WRAPPING A LIBRARY |
165 | |
166 | You can define your own wrapper subclasses to manipulate the behaviour |
167 | of a set of library exports. Here is an example: |
168 | |
169 | package MyWrapper; |
170 | use strict; |
171 | use Class::C3; |
52d358e2 |
172 | use base 'MooseX::Types::Wrapper'; |
c20dc98b |
173 | |
174 | sub coercion_export_generator { |
175 | my $class = shift; |
176 | my $code = $class->next::method(@_); |
177 | return sub { |
178 | my $value = $code->(@_); |
179 | warn "Coercion returned undef!" |
180 | unless defined $value; |
181 | return $value; |
182 | }; |
183 | } |
184 | |
185 | 1; |
186 | |
187 | This class wraps the coercion generator (e.g., C<to_Int()>) and warns |
188 | if a coercion returned an undefined value. You can wrap any library |
189 | with this: |
190 | |
191 | package Foo; |
192 | use strict; |
193 | use MyWrapper MyLibrary => [qw( Foo Bar )], |
194 | Moose => [qw( Str Int )]; |
195 | |
196 | ... |
197 | 1; |
198 | |
199 | The C<Moose> library name is a special shortcut for |
52d358e2 |
200 | L<MooseX::Types::Moose>. |
c20dc98b |
201 | |
202 | =head2 Generator methods you can overload |
203 | |
204 | =over 4 |
205 | |
206 | =item type_export_generator( $short, $full ) |
207 | |
208 | Creates a closure returning the type's L<Moose::Meta::TypeConstraint> |
209 | object. |
210 | |
211 | =item check_export_generator( $short, $full, $undef_message ) |
212 | |
213 | This creates the closure used to test if a value is valid for this type. |
214 | |
215 | =item coercion_export_generator( $short, $full, $undef_message ) |
216 | |
217 | This is the closure that's doing coercions. |
218 | |
219 | =back |
220 | |
221 | =head2 Provided Parameters |
222 | |
223 | =over 4 |
224 | |
225 | =item $short |
226 | |
227 | The short, exported name of the type. |
228 | |
229 | =item $full |
230 | |
231 | The fully qualified name of this type as L<Moose> knows it. |
232 | |
233 | =item $undef_message |
234 | |
235 | A message that will be thrown when type functionality is used but the |
236 | type does not yet exist. |
237 | |
238 | =back |
239 | |
8af0a70d |
240 | =head1 METHODS |
241 | |
242 | =head2 import |
243 | |
52d358e2 |
244 | Installs the L<MooseX::Types::Base> class into the caller and |
e211870f |
245 | exports types according to the specification described in |
246 | L</"LIBRARY DEFINITION">. This will continue to |
247 | L<Moose::Util::TypeConstraints>' C<import> method to export helper |
248 | functions you will need to declare your types. |
249 | |
8af0a70d |
250 | =cut |
251 | |
252 | sub import { |
253 | my ($class, %args) = @_; |
254 | my $callee = caller; |
255 | |
256 | # inject base class into new library |
257 | { no strict 'refs'; |
52d358e2 |
258 | unshift @{ $callee . '::ISA' }, 'MooseX::Types::Base'; |
8af0a70d |
259 | } |
260 | |
261 | # generate predeclared type helpers |
e211870f |
262 | if (my @orig_declare = @{ $args{ -declare } || [] }) { |
263 | my ($tags, $declare) = filter_tags @orig_declare; |
264 | |
265 | for my $type (@$declare) { |
249888e7 |
266 | |
267 | croak "Cannot create a type containing '::' ($type) at the moment" |
268 | if $type =~ /::/; |
269 | |
8af0a70d |
270 | $callee->add_type($type); |
271 | $callee->export_type_into( |
272 | $callee, $type, |
273 | sprintf($UndefMsg, $type, $callee), |
274 | -full => 1, |
275 | ); |
276 | } |
277 | } |
278 | |
279 | # run type constraints import |
c20dc98b |
280 | return Moose::Util::TypeConstraints->import({ into => $callee }); |
8af0a70d |
281 | } |
282 | |
283 | =head2 type_export_generator |
284 | |
e211870f |
285 | Generate a type export, e.g. C<Int()>. This will return either a |
286 | L<Moose::Meta::TypeConstraint> object, or alternatively a |
52d358e2 |
287 | L<MooseX::Types::UndefinedType> object if the type was not |
e211870f |
288 | yet defined. |
289 | |
8af0a70d |
290 | =cut |
291 | |
292 | sub type_export_generator { |
293 | my ($class, $type, $full) = @_; |
e211870f |
294 | return sub { |
295 | return find_type_constraint($full) |
52d358e2 |
296 | || MooseX::Types::UndefinedType->new($full); |
e211870f |
297 | }; |
8af0a70d |
298 | } |
299 | |
300 | =head2 coercion_export_generator |
301 | |
e211870f |
302 | This generates a coercion handler function, e.g. C<to_Int($value)>. |
303 | |
8af0a70d |
304 | =cut |
305 | |
306 | sub coercion_export_generator { |
307 | my ($class, $type, $full, $undef_msg) = @_; |
308 | return sub { |
309 | my ($value) = @_; |
310 | |
311 | # we need a type object |
312 | my $tobj = find_type_constraint($full) or croak $undef_msg; |
313 | my $return = $tobj->coerce($value); |
314 | |
315 | # non-successful coercion returns false |
316 | return unless $tobj->check($return); |
317 | |
318 | return $return; |
319 | } |
320 | } |
321 | |
322 | =head2 check_export_generator |
323 | |
e211870f |
324 | Generates a constraint check closure, e.g. C<is_Int($value)>. |
325 | |
8af0a70d |
326 | =cut |
327 | |
328 | sub check_export_generator { |
329 | my ($class, $type, $full, $undef_msg) = @_; |
330 | return sub { |
331 | my ($value) = @_; |
332 | |
333 | # we need a type object |
334 | my $tobj = find_type_constraint($full) or croak $undef_msg; |
335 | |
336 | return $tobj->check($value); |
337 | } |
338 | } |
339 | |
e211870f |
340 | =head1 CAVEATS |
341 | |
342 | A library makes the types quasi-unique by prefixing their names with (by |
343 | default) the library package name. If you're only using the type handler |
52d358e2 |
344 | functions provided by MooseX::Types, you shouldn't ever have to use |
e211870f |
345 | a type's actual full name. |
346 | |
8af0a70d |
347 | =head1 SEE ALSO |
348 | |
52d358e2 |
349 | L<Moose>, L<Moose::Util::TypeConstraints>, L<MooseX::Types::Moose> |
8af0a70d |
350 | |
351 | =head1 AUTHOR AND COPYRIGHT |
352 | |
353 | Robert 'phaylon' Sedlacek C<E<lt>rs@474.atE<gt>>, with many thanks to |
354 | the C<#moose> cabal on C<irc.perl.org>. |
355 | |
356 | =head1 LICENSE |
357 | |
358 | This program is free software; you can redistribute it and/or modify |
359 | it under the same terms as perl itself. |
360 | |
361 | =cut |
362 | |
363 | 1; |