ugly proof of concept for parametrized types only
[gitmo/MooseX-Types.git] / lib / MooseX / Types.pm
1 package MooseX::Types;
2 use Moose;
3
4 =head1 NAME
5
6 MooseX::Types - Organise your Moose types in libraries
7
8 =cut
9
10 #use warnings;
11 #use strict;
12
13 use Moose::Util::TypeConstraints;
14 use MooseX::Types::TypeDecorator;
15 use MooseX::Types::Base             ();
16 use MooseX::Types::Util             qw( filter_tags );
17 use MooseX::Types::UndefinedType;
18 use Carp::Clan                      qw( ^MooseX::Types );
19
20 use namespace::clean -except => [qw( meta )];
21
22 our $VERSION = 0.05;
23
24 my $UndefMsg = q{Action for type '%s' not yet defined in library '%s'};
25
26 =head1 SYNOPSIS
27
28 =head2 Library Definition
29
30   package MyLibrary;
31
32   # predeclare our own types
33   use MooseX::Types 
34       -declare => [qw( PositiveInt NegativeInt )];
35
36   # import builtin types
37   use MooseX::Types::Moose 'Int';
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
57 =head2 Usage
58
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
121 A MooseX::Types is just a normal Perl module. Unlike Moose 
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
127   use MooseX::Types -declare => \@types;
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
139   use MooseX::Types::Moose @types;
140
141 to import the helpers from the shipped L<MooseX::Types::Moose>
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
147 Note that you currently cannot define types containing C<::>, since 
148 exporting would be a problem.
149
150 You also don't need to use C<warnings> and C<strict>, since the
151 definition of a library automatically exports those.
152
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
162 MooseX::Types comes with a library of Moose' built-in types called
163 L<MooseX::Types::Moose>.
164
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
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;
179   use base 'MooseX::Types::Wrapper';
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 
207 L<MooseX::Types::Moose>.
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
247 =head1 METHODS
248
249 =head2 import
250
251 Installs the L<MooseX::Types::Base> class into the caller and 
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
257 =cut
258
259 sub import {
260     my ($class, %args) = @_;
261     my  $callee = caller;
262
263     # everyone should want this
264     strict->import;
265     warnings->import;
266
267     # inject base class into new library
268     {   no strict 'refs';
269         unshift @{ $callee . '::ISA' }, 'MooseX::Types::Base';
270     }
271
272     # generate predeclared type helpers
273     if (my @orig_declare = @{ $args{ -declare } || [] }) {
274         my ($tags, $declare) = filter_tags @orig_declare;
275         my @to_export;
276
277         for my $type (@$declare) {
278
279             croak "Cannot create a type containing '::' ($type) at the moment"
280                 if $type =~ /::/;
281
282             # add type to library and remember to export
283             $callee->add_type($type);
284             push @to_export, $type;
285         }
286
287         $callee->import({ -full => 1, -into => $callee }, @to_export);
288     }
289
290     # run type constraints import
291     return Moose::Util::TypeConstraints->import({ into => $callee });
292 }
293
294 =head2 type_export_generator
295
296 Generate a type export, e.g. C<Int()>. This will return either a
297 L<Moose::Meta::TypeConstraint> object, or alternatively a
298 L<MooseX::Types::UndefinedType> object if the type was not
299 yet defined.
300
301 =cut
302
303 use Data::Dump qw/dump/;
304
305 sub type_export_generator {
306     my ($class, $type, $full) = @_;
307     return sub {
308         ## todo, this needs to be some sort of ->process_args on the actual
309         ## containing type constraints.  This is ugly proof of concept
310         if(my $param = shift @_) {
311             #my @tc_args = map { find_type_constraint($full) } @args;
312             $full = $full .'['.  $param->[0]->name .']';
313         }
314         
315         my $type_constraint = find_type_constraint($full)
316          || MooseX::Types::UndefinedType->new($full);
317
318         return MooseX::Types::TypeDecorator->new(type_constraint=>$type_constraint);
319     };
320 }
321
322 =head2 coercion_export_generator
323
324 This generates a coercion handler function, e.g. C<to_Int($value)>. 
325
326 =cut
327
328 sub coercion_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         my $return = $tobj->coerce($value);
336
337         # non-successful coercion returns false
338         return unless $tobj->check($return);
339
340         return $return;
341     }
342 }
343
344 =head2 check_export_generator
345
346 Generates a constraint check closure, e.g. C<is_Int($value)>.
347
348 =cut
349
350 sub check_export_generator {
351     my ($class, $type, $full, $undef_msg) = @_;
352     return sub {
353         my ($value) = @_;
354
355         # we need a type object
356         my $tobj = find_type_constraint($full) or croak $undef_msg;
357
358         return $tobj->check($value);
359     }
360 }
361
362 =head1 CAVEATS
363
364 A library makes the types quasi-unique by prefixing their names with (by
365 default) the library package name. If you're only using the type handler
366 functions provided by MooseX::Types, you shouldn't ever have to use
367 a type's actual full name.
368
369 =head1 SEE ALSO
370
371 L<Moose>, 
372 L<Moose::Util::TypeConstraints>, 
373 L<MooseX::Types::Moose>,
374 L<Sub::Exporter>
375
376 =head1 AUTHOR AND COPYRIGHT
377
378 Robert 'phaylon' Sedlacek C<E<lt>rs@474.atE<gt>>, with many thanks to
379 the C<#moose> cabal on C<irc.perl.org>.
380
381 =head1 LICENSE
382
383 This program is free software; you can redistribute it and/or modify
384 it under the same terms as perl itself.
385
386 =cut
387
388 1;