Commit | Line | Data |
8af0a70d |
1 | package MooseX::TypeLibrary; |
2 | |
3 | =head1 NAME |
4 | |
5 | MooseX::TypeLibrary - Organise your Moose types in libraries |
6 | |
7 | =cut |
8 | |
9 | use warnings; |
10 | use strict; |
11 | |
12 | use Sub::Uplevel; |
13 | use Moose::Util::TypeConstraints; |
14 | use MooseX::TypeLibrary::Base; |
e211870f |
15 | use MooseX::TypeLibrary::Util qw( filter_tags ); |
16 | use MooseX::TypeLibrary::UndefinedType; |
17 | use Sub::Install qw( install_sub ); |
8af0a70d |
18 | use namespace::clean; |
19 | |
20 | our $VERSION = 0.01; |
21 | |
22 | my $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 | |
88 | The types provided with L<Moose> are by design global. This package helps |
89 | you to organise and selectively import your own and the built-in types in |
90 | libraries. As a nice side effect, it catches typos at compile-time too. |
91 | |
92 | However, the main reason for this module is to provide an easy way to not |
93 | have conflicts with your type names, since the internal fully qualified |
94 | names of the types will be prefixed with the library's name. |
95 | |
96 | This module will also provide you with some helper functions to make it |
97 | easier to use Moose types in your code. |
98 | |
99 | =head1 TYPE HANDLER FUNCTIONS |
100 | |
101 | =head2 $type |
102 | |
103 | A constant with the name of your type. It contains the type's fully |
104 | qualified name. Takes no value, as all constants. |
105 | |
106 | =head2 is_$type |
107 | |
108 | This handler takes a value and tests if it is a valid value for this |
109 | C<$type>. It will return true or false. |
110 | |
111 | =head2 to_$type |
112 | |
113 | A handler that will take a value and coerce it into the C<$type>. It will |
114 | return a false value if the type could not be coerced. |
115 | |
116 | B<Important Note>: This handler will only be exported for types that can |
117 | do type coercion. This has the advantage that a coercion to a type that |
118 | cannot hasn't defined any coercions will lead to a compile-time error. |
119 | |
120 | =head1 LIBRARY DEFINITION |
121 | |
122 | A MooseX::TypeLibrary is just a normal Perl module. Unlike Moose |
123 | itself, it does not install C<use strict> and C<use warnings> in your |
124 | class by default, so this is up to you. |
125 | |
126 | The only thing a library is required to do is |
127 | |
128 | use MooseX::TypeLibrary -declare => \@types; |
129 | |
130 | with C<@types> being a list of types you wish to define in this library. |
131 | This line will install a proper base class in your package as well as the |
132 | full set of L<handlers|/"TYPE HANDLER FUNCTIONS"> for your declared |
133 | types. It will then hand control over to L<Moose::Util::TypeConstraints>' |
134 | C<import> method to export the functions you will need to declare your |
135 | types. |
136 | |
137 | If you want to use Moose' built-in types (e.g. for subtyping) you will |
138 | want to |
139 | |
140 | use MooseX::TypeLibrary::Moose @types; |
141 | |
142 | to import the helpers from the shipped L<MooseX::TypeLibrary::Moose> |
143 | library which can export all types that come with Moose. |
144 | |
145 | You will have to define coercions for your types or your library won't |
146 | export a L</to_$type> coercion helper for it. |
147 | |
148 | =head1 LIBRARY USAGE |
149 | |
150 | You can import the L<"type helpers"|/"TYPE HANDLER FUNCTIONS"> of a |
151 | library by C<use>ing it with a list of types to import as arguments. If |
152 | you want all of them, use the C<:all> tag. For example: |
153 | |
154 | use MyLibrary ':all'; |
155 | use MyOtherLibrary qw( TypeA TypeB ); |
156 | |
157 | MooseX::TypeLibrary comes with a library of Moose' built-in types called |
158 | L<MooseX::TypeLibrary::Moose>. |
159 | |
160 | =head1 METHODS |
161 | |
162 | =head2 import |
163 | |
e211870f |
164 | Installs the L<MooseX::TypeLibrary::Base> class into the caller and |
165 | exports types according to the specification described in |
166 | L</"LIBRARY DEFINITION">. This will continue to |
167 | L<Moose::Util::TypeConstraints>' C<import> method to export helper |
168 | functions you will need to declare your types. |
169 | |
8af0a70d |
170 | =cut |
171 | |
172 | sub import { |
173 | my ($class, %args) = @_; |
174 | my $callee = caller; |
175 | |
176 | # inject base class into new library |
177 | { no strict 'refs'; |
178 | unshift @{ $callee . '::ISA' }, 'MooseX::TypeLibrary::Base'; |
179 | } |
180 | |
181 | # generate predeclared type helpers |
e211870f |
182 | if (my @orig_declare = @{ $args{ -declare } || [] }) { |
183 | my ($tags, $declare) = filter_tags @orig_declare; |
184 | |
185 | for my $type (@$declare) { |
8af0a70d |
186 | $callee->add_type($type); |
187 | $callee->export_type_into( |
188 | $callee, $type, |
189 | sprintf($UndefMsg, $type, $callee), |
190 | -full => 1, |
191 | ); |
192 | } |
193 | } |
194 | |
195 | # run type constraints import |
de3e7243 |
196 | return Moose::Util::TypeConstraints |
197 | ->import({ into => $callee }); |
198 | # return uplevel 1, |
199 | # Moose::Util::TypeConstraints->can('import'), |
200 | # 'Moose::Util::TypeConstraints'; |
8af0a70d |
201 | } |
202 | |
203 | =head2 type_export_generator |
204 | |
e211870f |
205 | Generate a type export, e.g. C<Int()>. This will return either a |
206 | L<Moose::Meta::TypeConstraint> object, or alternatively a |
207 | L<MooseX::TypeLibrary::UndefinedType> object if the type was not |
208 | yet defined. |
209 | |
8af0a70d |
210 | =cut |
211 | |
212 | sub type_export_generator { |
213 | my ($class, $type, $full) = @_; |
e211870f |
214 | return sub { |
215 | return find_type_constraint($full) |
216 | || MooseX::TypeLibrary::UndefinedType->new($full); |
217 | }; |
8af0a70d |
218 | } |
219 | |
220 | =head2 coercion_export_generator |
221 | |
e211870f |
222 | This generates a coercion handler function, e.g. C<to_Int($value)>. |
223 | |
8af0a70d |
224 | =cut |
225 | |
226 | sub coercion_export_generator { |
227 | my ($class, $type, $full, $undef_msg) = @_; |
228 | return sub { |
229 | my ($value) = @_; |
230 | |
231 | # we need a type object |
232 | my $tobj = find_type_constraint($full) or croak $undef_msg; |
233 | my $return = $tobj->coerce($value); |
234 | |
235 | # non-successful coercion returns false |
236 | return unless $tobj->check($return); |
237 | |
238 | return $return; |
239 | } |
240 | } |
241 | |
242 | =head2 check_export_generator |
243 | |
e211870f |
244 | Generates a constraint check closure, e.g. C<is_Int($value)>. |
245 | |
8af0a70d |
246 | =cut |
247 | |
248 | sub check_export_generator { |
249 | my ($class, $type, $full, $undef_msg) = @_; |
250 | return sub { |
251 | my ($value) = @_; |
252 | |
253 | # we need a type object |
254 | my $tobj = find_type_constraint($full) or croak $undef_msg; |
255 | |
256 | return $tobj->check($value); |
257 | } |
258 | } |
259 | |
e211870f |
260 | =head1 CAVEATS |
261 | |
262 | A library makes the types quasi-unique by prefixing their names with (by |
263 | default) the library package name. If you're only using the type handler |
264 | functions provided by MooseX::TypeLibrary, you shouldn't ever have to use |
265 | a type's actual full name. |
266 | |
8af0a70d |
267 | =head1 SEE ALSO |
268 | |
269 | L<Moose>, L<Moose::Util::TypeConstraints>, L<MooseX::TypeLibrary::Moose> |
270 | |
271 | =head1 AUTHOR AND COPYRIGHT |
272 | |
273 | Robert 'phaylon' Sedlacek C<E<lt>rs@474.atE<gt>>, with many thanks to |
274 | the C<#moose> cabal on C<irc.perl.org>. |
275 | |
276 | =head1 LICENSE |
277 | |
278 | This program is free software; you can redistribute it and/or modify |
279 | it under the same terms as perl itself. |
280 | |
281 | =cut |
282 | |
283 | 1; |