package MooseX::Types;
+use Moose;
=head1 NAME
=cut
-#use warnings;
-#use strict;
-
-use Sub::Uplevel;
use Moose::Util::TypeConstraints;
-use MooseX::Types::Base ();
-use MooseX::Types::Util qw( filter_tags );
+use MooseX::Types::TypeDecorator;
+use MooseX::Types::Base ();
+use MooseX::Types::Util qw( filter_tags );
use MooseX::Types::UndefinedType;
-use Sub::Install qw( install_sub );
-use Moose;
-use namespace::clean;
+use MooseX::Types::CheckedUtilExports ();
+use Carp::Clan qw( ^MooseX::Types );
+use Sub::Name;
+use Scalar::Util 'reftype';
-our $VERSION = 0.01;
+use namespace::clean -except => [qw( meta )];
+use 5.008;
+our $VERSION = '0.20';
my $UndefMsg = q{Action for type '%s' not yet defined in library '%s'};
=head1 SYNOPSIS
- #
- # Library Definition
- #
+=head2 Library Definition
+
package MyLibrary;
- use strict;
# predeclare our own types
use MooseX::Types
- -declare => [qw( PositiveInt NegativeInt )];
+ -declare => [qw(
+ PositiveInt NegativeInt
+ ArrayRefOfPositiveInt ArrayRefOfAtLeastThreeNegativeInts
+ LotsOfInnerConstraints StrOrArrayRef
+ MyDateTime
+ )];
# import builtin types
- use MooseX::Types::Moose 'Int';
+ use MooseX::Types::Moose qw/Int HashRef/;
- # type definition
+ # type definition.
subtype PositiveInt,
as Int,
where { $_ > 0 },
from Int,
via { 1 };
+ # with parameterized constraints.
+
+ subtype ArrayRefOfPositiveInt,
+ as ArrayRef[PositiveInt];
+
+ subtype ArrayRefOfAtLeastThreeNegativeInts,
+ as ArrayRef[NegativeInt],
+ where { scalar(@$_) > 2 };
+
+ subtype LotsOfInnerConstraints,
+ as ArrayRef[ArrayRef[HashRef[Int]]];
+
+ # with TypeConstraint Unions
+
+ subtype StrOrArrayRef,
+ as Str|ArrayRef;
+
+ # class types
+
+ class_type 'DateTime';
+
+ # or better
+
+ class_type MyDateTime, { class => 'DateTime' };
+
+ coerce MyDateTime,
+ from HashRef,
+ via { DateTime->new(%$_) };
+
1;
- #
- # Usage
- #
+=head2 Usage
+
package Foo;
use Moose;
use MyLibrary qw( PositiveInt NegativeInt );
This module will also provide you with some helper functions to make it
easier to use Moose types in your code.
+String type names will produce a warning, unless it's for a C<class_type> or
+C<role_type> declared within the library, or a fully qualified name like
+C<'MyTypeLibrary::Foo'>.
+
=head1 TYPE HANDLER FUNCTIONS
=head2 $type
You will have to define coercions for your types or your library won't
export a L</to_$type> coercion helper for it.
+Note that you currently cannot define types containing C<::>, since
+exporting would be a problem.
+
+You also don't need to use C<warnings> and C<strict>, since the
+definition of a library automatically exports those.
+
=head1 LIBRARY USAGE
You can import the L<"type helpers"|/"TYPE HANDLER FUNCTIONS"> of a
MooseX::Types comes with a library of Moose' built-in types called
L<MooseX::Types::Moose>.
+The exporting mechanism is, since version 0.5, implemented via a wrapper
+around L<Sub::Exporter>. This means you can do something like this:
+
+ use MyLibrary TypeA => { -as => 'MyTypeA' },
+ TypeB => { -as => 'MyTypeB' };
+
=head1 WRAPPING A LIBRARY
You can define your own wrapper subclasses to manipulate the behaviour
=back
+=head1 RECURSIVE SUBTYPES
+
+As of version 0.08, L<Moose::Types> has experimental support for Recursive
+subtypes. This will allow:
+
+ subtype Tree() => as HashRef[Str|Tree];
+
+Which validates things like:
+
+ {key=>'value'};
+ {key=>{subkey1=>'value', subkey2=>'value'}}
+
+And so on. This feature is new and there may be lurking bugs so don't be afraid
+to hunt me down with patches and test cases if you have trouble.
+
+=head1 NOTES REGARDING TYPE UNIONS
+
+L<MooseX::Types> uses L<MooseX::Types::TypeDecorator> to do some overloading
+which generally allows you to easily create union types:
+
+ subtype StrOrArrayRef,
+ as Str|ArrayRef;
+
+As with parameterized constrains, this overloading extends to modules using the
+types you define in a type library.
+
+ use Moose;
+ use MooseX::Types::Moose qw(HashRef Int);
+
+ has 'attr' => (isa=>HashRef|Int);
+
+And everything should just work as you'd think.
+
=head1 METHODS
=head2 import
my ($class, %args) = @_;
my $callee = caller;
+ # everyone should want this
+ strict->import;
+ warnings->import;
+
# inject base class into new library
{ no strict 'refs';
unshift @{ $callee . '::ISA' }, 'MooseX::Types::Base';
# generate predeclared type helpers
if (my @orig_declare = @{ $args{ -declare } || [] }) {
my ($tags, $declare) = filter_tags @orig_declare;
+ my @to_export;
for my $type (@$declare) {
+
+ croak "Cannot create a type containing '::' ($type) at the moment"
+ if $type =~ /::/;
+
+ # add type to library and remember to export
$callee->add_type($type);
- $callee->export_type_into(
- $callee, $type,
- sprintf($UndefMsg, $type, $callee),
- -full => 1,
- );
+ push @to_export, $type;
}
+
+ $callee->import({ -full => 1, -into => $callee }, @to_export);
}
# run type constraints import
- return Moose::Util::TypeConstraints->import({ into => $callee });
+ Moose::Util::TypeConstraints->import({ into => $callee });
+
+ # override some with versions that check for syntax errors
+ MooseX::Types::CheckedUtilExports->import({ into => $callee });
+
+ 1;
}
=head2 type_export_generator
=cut
sub type_export_generator {
- my ($class, $type, $full) = @_;
- return sub {
- return find_type_constraint($full)
- || MooseX::Types::UndefinedType->new($full);
+ my ($class, $type, $name) = @_;
+
+ ## Return an anonymous subroutine that will generate the proxied type
+ ## constraint for you.
+
+ return subname "__TYPE__::$name" => sub {
+ my $type_constraint = $class->create_base_type_constraint($name);
+
+ if(defined(my $params = shift @_)) {
+ ## We currently only allow a TC to accept a single, ArrayRef
+ ## parameter, as in HashRef[Int], where [Int] is what's inside the
+ ## ArrayRef passed.
+ if(reftype $params eq 'ARRAY') {
+ $type_constraint = $class->create_arged_type_constraint($name, @$params);
+ } elsif(!defined $type_constraint) {
+ croak "Syntax error in type definition (did you forget a comma"
+ . " after $type?)";
+ } else {
+ croak "Argument must be an ArrayRef to create a parameterized "
+ . "type, Eg.: ${type}[Int]. Got: ".ref($params)."."
+ }
+ }
+
+ $type_constraint = defined($type_constraint) ? $type_constraint
+ : MooseX::Types::UndefinedType->new($name);
+
+ my $type_decorator = $class->create_type_decorator($type_constraint);
+
+ ## If there are additional args, that means it's probably stuff that
+ ## needs to be returned to the subtype. Not an ideal solution here but
+ ## doesn't seem to cause trouble.
+
+ if(@_) {
+ return ($type_decorator, @_);
+ } else {
+ return $type_decorator;
+ }
};
}
+=head2 create_arged_type_constraint ($name, @args)
+
+Given a String $name with @args find the matching typeconstraint and parameterize
+it with @args.
+
+=cut
+
+sub create_arged_type_constraint {
+ my ($class, $name, @args) = @_;
+ my $type_constraint = Moose::Util::TypeConstraints::find_or_create_type_constraint("$name");
+ my $parameterized = $type_constraint->parameterize(@args);
+ # It's obnoxious to have to parameterize before looking for the TC, but the
+ # alternative is to hard-code the assumption that the name is
+ # "$name[$args[0]]", which would be worse.
+ # This breaks MXMS, unfortunately, which relies on things like Tuple[...]
+ # creating new type objects each time.
+ # if (my $existing =
+ # Moose::Util::TypeConstraints::find_type_constraint($parameterized->name)) {
+ # return $existing;
+ # }
+ # Moose::Util::TypeConstraints::register_type_constraint($parameterized);
+ return $parameterized;
+}
+
+=head2 create_base_type_constraint ($name)
+
+Given a String $name, find the matching typeconstraint.
+
+=cut
+
+sub create_base_type_constraint {
+ my ($class, $name) = @_;
+ return find_type_constraint($name);
+}
+
+=head2 create_type_decorator ($type_constraint)
+
+Given a $type_constraint, return a lightweight L<MooseX::Types::TypeDecorator>
+instance.
+
+=cut
+
+sub create_type_decorator {
+ my ($class, $type_constraint) = @_;
+ return MooseX::Types::TypeDecorator->new($type_constraint);
+}
+
=head2 coercion_export_generator
This generates a coercion handler function, e.g. C<to_Int($value)>.
=head1 CAVEATS
+The following are lists of gotcha's and their workarounds for developers coming
+from the standard string based type constraint names
+
+=head2 Uniqueness
+
A library makes the types quasi-unique by prefixing their names with (by
default) the library package name. If you're only using the type handler
functions provided by MooseX::Types, you shouldn't ever have to use
a type's actual full name.
+=head2 Argument separation ('=>' versus ',')
+
+The Perlop manpage has this to say about the '=>' operator: "The => operator is
+a synonym for the comma, but forces any word (consisting entirely of word
+characters) to its left to be interpreted as a string (as of 5.001). This
+includes words that might otherwise be considered a constant or function call."
+
+Due to this stringification, the following will NOT work as you might think:
+
+ subtype StrOrArrayRef => as Str|ArrayRef;
+
+The 'StrOrArrayRef' will have it's stringification activated this causes the
+subtype to not be created. Since the bareword type constraints are not strings
+you really should not try to treat them that way. You will have to use the ','
+operator instead. The author's of this package realize that all the L<Moose>
+documention and examples nearly uniformly use the '=>' version of the comma
+operator and this could be an issue if you are converting code.
+
+Patches welcome for discussion.
+
+=head2 Compatibility with Sub::Exporter
+
+If you want to use L<Sub::Exporter> with a Type Library, you need to make sure
+you export all the type constraints declared AS WELL AS any additional export
+targets. For example if you do:
+
+ package TypeAndSubExporter; {
+
+ use MooseX::Types::Moose qw(Str);
+ use MooseX::Types -declare => [qw(MyStr)];
+ use Sub::Exporter -setup => { exports => [ qw(something) ] };
+
+ subtype MyStr,
+ as Str;
+
+ sub something {
+ return 1;
+ }
+
+ } 1;
+
+ package Foo; {
+ use TypeAndSubExporter qw(MyStr);
+ } 1;
+
+You'll get a '"MyStr" is not exported by the TypeAndSubExporter module' error.
+Upi can workaround by:
+
+ - use Sub::Exporter -setup => { exports => [ qw(something) ] };
+ + use Sub::Exporter -setup => { exports => [ qw(something MyStr) ] };
+
+This is a workaround and I am exploring how to make these modules work better
+together. I realize this workaround will lead a lot of duplication in your
+export declarations and will be onerous for large type libraries. Patches and
+detailed test cases welcome. See the tests directory for a start on this.
+
=head1 SEE ALSO
-L<Moose>, L<Moose::Util::TypeConstraints>, L<MooseX::Types::Moose>
+L<Moose>,
+L<Moose::Util::TypeConstraints>,
+L<MooseX::Types::Moose>,
+L<Sub::Exporter>
+
+=head1 ACKNOWLEDGEMENTS
+
+Many thanks to the C<#moose> cabal on C<irc.perl.org>.
+
+=head1 AUTHOR
+
+Robert "phaylon" Sedlacek <rs@474.at>
+
+=head1 CONTRIBUTORS
+
+jnapiorkowski: John Napiorkowski <jjnapiork@cpan.org>
+
+caelum: Rafael Kitover <rkitover@cpan.org>
+
+rafl: Florian Ragwitz <rafl@debian.org>
-=head1 AUTHOR AND COPYRIGHT
+hdp: Hans Dieter Pearcey <hdp@cpan.org>
-Robert 'phaylon' Sedlacek C<E<lt>rs@474.atE<gt>>, with many thanks to
-the C<#moose> cabal on C<irc.perl.org>.
+=head1 COPYRIGHT & LICENSE
-=head1 LICENSE
+Copyright (c) 2007-2009 Robert Sedlacek <rs@474.at>
This program is free software; you can redistribute it and/or modify
it under the same terms as perl itself.