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