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