%PDF- %PDF-
Direktori : /home/opti/perl5/lib/perl5/Type/Tiny/Manual/ |
Current File : /home/opti/perl5/lib/perl5/Type/Tiny/Manual/Libraries.pod |
=pod =for stopwords smush smushed =encoding utf-8 =head1 NAME Type::Tiny::Manual::Libraries - how to build a type library with Type::Tiny, Type::Library and Type::Utils =head1 SYNOPSIS A type library is a collection of type constraints, optionally with coercions. The following is an example type library: package Example::Types; use Type::Library -base, -declare => qw( Datetime DatetimeHash EpochHash ); use Type::Utils -all; use Types::Standard -types; class_type Datetime, { class => "DateTime" }; declare DatetimeHash, as Dict[ year => Int, month => Optional[ Int ], day => Optional[ Int ], hour => Optional[ Int ], minute => Optional[ Int ], second => Optional[ Int ], nanosecond => Optional[ Int ], time_zone => Optional[ Str ], ]; declare EpochHash, as Dict[ epoch => Int ]; coerce Datetime, from Int, via { "DateTime"->from_epoch(epoch => $_) }, from Undef, via { "DateTime"->now }, from DatetimeHash, via { "DateTime"->new(%$_) }, from EpochHash, via { "DateTime"->from_epoch(%$_) }; 1; =head1 DESCRIPTION Here's a line by line description of what's going on in the type library. package Example::Types; Type libraries are packages. It is recommended that re-usable type libraries be given a name in the C<< Types::* >> namespace. For application-specific type libraries, assuming your application's namespace is C<< MyApp::* >> then name the type library C<< MyApp::Types >>, or if more than one is needed, use the C<< MyApp::Types::* >> namespace. use Type::Library -base, -declare => qw( Datetime DatetimeHash EpochHash ); The C<< -base >> part is used to establish inheritance. It makes C<Example::Types> a child class of C<Type::Library>. Declaring the types we're going to define ahead of their definition allows us to use them as barewords later on. (Note that in code which I<uses> our type library, the types will always be available as barewords. The declaration above just allows us to use them within the library itself.) use Type::Utils -all; Imports some utility functions from L<Type::Utils>. These will be useful for defining our types and the relationships between them. use Types::Standard -types; Here we import a standard set of type constraints from L<Types::Standard>. There is no need to do this, but it's often helpful to have a base set of types which we can define our own in terms of. Note that although we've imported the types to be able to use in our library, we haven't I<added> the types to our library. We've imported C<Str>, but other people won't be able to re-import C<Str> from our library. If you actually want your library to I<extend> another library, do this instead: BEGIN { extends "Types::AnotherLibrary" }; (Note: if your code breaks here when you upgrade from version 0.006 or below, saying that the 'extends' keyword has not been declared, just add '-all' after use Type::Utils.) OK, now we're ready to declare a few types. class_type Datetime, { class => "DateTime" }; This creates a type constraint named "Datetime" which is all objects blessed into the L<DateTime> package. Because this type constraint is not anonymous (it has a name), it will be automagically installed into the type library. The next two statements declare two further types constraints, using type constraints from the Types::Standard library. Let's look at C<EpochHash> in more detail. This is a hashref with one key called "epoch" and a value which is an integer. declare EpochHash, as Dict[ epoch => Int ]; C<EpochHash> inherits from the C<Dict> type defined in Types::Standard. It equally could have been defined as: declare EpochHash, as HashRef[Int], where { scalar(keys(%$_))==1 and exists $_->{epoch} }; Or even: declare EpochHash, where { ref($_) eq "HASH" and scalar(keys(%$_))==1 and exists $_->{epoch} }; Lastly we set up coercions. It's best to define all your types before you define any coercions. coerce Datetime, from Int, via { "DateTime"->from_epoch(epoch => $_) }, from Undef, via { "DateTime"->now }, from DatetimeHash, via { "DateTime"->new(%$_) }, from EpochHash, via { "DateTime"->from_epoch(%$_) }; These are simply coderefs that will be fired when you want a Datetime, but are given something else. For more information on coercions, see L<Type::Tiny::Manual::Coercions>. =head2 Using Your Library Use a custom types library just like you would L<Types::Standard>: package MyClass; use Moose; use DateTime; use Example::Types qw( Datetime ); # import the custom type has 'sometime' => ( is => 'rw', isa => Datetime, coerce => 1, ); Type libraries defined with L<Type::Library> are also able to export some convenience functions: use Example::Types qw( is_Datetime to_Datetime assert_Datetime ); my $dt = Foo::get_datetime; unless ( is_Datetime $dt ) { $dt = to_Datetime $dt; } assert_Datetime $dt; These functions act as shortcuts for: use Example::Types qw( Datetime ); my $dt = Foo::get_datetime; unless ( Datetime->check($dt) ) { $dt = Datetime->coerce($dt); } Datetime->assert_return($dt); Pick whichever style you think is clearer! Type::Library-based libraries provide a shortcut for importing a type constraint along with all its associated convenience functions: # Shortcut for qw( DateTime is_Datetime to_Datetime assert_Datetime ) # use Example::Types qw( +Datetime ); See L<Type::Tiny::Manual> for other ways to make use of type libraries. =head1 ADVANCED TOPICS =head2 Messages It is sometimes nice to be able to emit a more useful error message than the standard: Value "Foo" did not pass type constraint "Bar" It is possible to define custom error messages for types. declare MediumInteger, as Integer, where { $_ >= 10 and $_ < 20 }, message { return Integer->get_message($_) if !Integer->check($_); return "$_ is too small!" if $_ < 10; return "$_ is so very, very big!"; }; =head2 Parameterized Constraints Parameterized type constraints are those that can generate simple child type constraints by passing parameters to their C<parameterize> method. For example, ArrayRef in Types::Standard: use Types::Standard; my $ArrayRef = Types::Standard::ArrayRef; my $Int = Types::Standard::Int; my $ArrayRef_of_Ints = $ArrayRef->parameterize($Int); Type libraries provide some convenient sugar for this: use Types::Standard qw( ArrayRef Int ); my $ArrayRef_of_Ints = ArrayRef[Int]; Unlike L<Moose> which has separate meta classes for parameterizable, parameterized and non-parameterizable type constraints, L<Type::Tiny> handles all that in one. To create a parameterizable type constraint, you'll need to pass an extra named parameter to C<declare>. Let's imagine that we want to make our earlier C<NonEmptyHash> constraint accept a parameter telling it the minimum size of the hash. For example C<< NonEmptyHash[4] >> would need to contain at least four key-value pairs. Here's how you'd do it: declare NonEmptyHash, as HashLike, where { scalar values %$_ }, inline_as { my ($constraint, $varname) = @_; return sprintf( '%s and scalar values %%{%s}', $constraint->parent->inline_check($varname), $varname, ); }, # Generate a new "where" coderef... constraint_generator => sub { my ($minimum) = @_; die "parameter must be positive" unless int($minimum) > 0; return sub { scalar(values(%$_)) >= int($minimum); }; }, # Generate a new "inline_as" coderef... inline_generator => sub { my ($minimum) = @_; return sub { my ($constraint, $varname) = @_; return sprintf( '%s and scalar(values(%%{%s})) >= %d', $constraint->parent->inline_check($varname), $varname, $minimum, ); }; }; =head1 SEE ALSO Some type libraries on CPAN: =over =item * L<Types::Standard> =item * L<Types::Path::Tiny> =item * L<Types::XSD> / L<Types::XSD::Lite> =item * L<Types::Set> =item * L<more|https://github.com/tobyink/p5-type-tiny/wiki/Type-libraries>! =back =head1 AUTHOR Toby Inkster E<lt>tobyink@cpan.orgE<gt>. =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2013-2014, 2017-2018 by Toby Inkster. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 DISCLAIMER OF WARRANTIES THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. =cut