use 5.008;
-our $VERSION = '0.02';
+our $VERSION = '0.04';
$VERSION = eval $VERSION;
use Moose::Util::TypeConstraints;
use MooseX::Types::Moose qw(Str Int ArrayRef);
use MooseX::Types -declare=>[qw(Varchar)];
- ## Create a type constraint that is a string but parameterizes an integer
- ## that is used as a maximum length constraint on that string, similar to
- ## a SQL Varchar database type.
+Create a type constraint that is similar to SQL Varchar type.
subtype Varchar,
as Parameterizable[Str,Int],
},
message { "'$_' is too long" };
- ## Coerce an ArrayRef to a string via concatenation.
+Coerce an ArrayRef to a string via concatenation.
coerce Varchar,
from ArrayRef,
has 'varchar_five' => (isa=>Varchar[5], is=>'ro', coerce=>1);
has 'varchar_ten' => (isa=>Varchar[10], is=>'ro');
- ## Object created since attributes are valid
+Object created since attributes are valid
+
my $object1 = __PACKAGE__->new(
varchar_five => '1234',
varchar_ten => '123456789',
);
- ## Dies with an invalid constraint for 'varchar_five'
+Dies with an invalid constraint for 'varchar_five'
+
my $object2 = __PACKAGE__->new(
varchar_five => '12345678', ## too long!
varchar_ten => '123456789',
);
- ## varchar_five coerces as expected
+varchar_five coerces as expected
+
my $object3 = __PACKAGE__->new(
varchar_five => [qw/aa bb/], ## coerces to "aabb"
varchar_ten => '123456789',
type constraint for all intents and uses is a subclass of a parent type, but
adds additional type parameters which are available to constraint callbacks
(such as inside the 'where' clause of a type constraint definition) or in the
-coercions.
+coercions you define for a given type constraint.
If you have L<Moose> experience, you probably are familiar with the builtin
parameterizable type constraints 'ArrayRef' and 'HashRef'. This type constraint
};
RangedInt([{min=>10,max=>100}])->check(50); ## OK
- RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
+ RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, exceeds max
+
+This is useful since it lets you generate common patterns of type constraints
+rather than build a custom type constraint for all similar cases.
+
+The type parameter must be valid against the 'constrainting' type constraint used
+in the Parameterizable condition. If you pass an invalid value this throws a
+hard Moose exception. You'll need to capture it in an eval or related exception
+catching system (see L<TryCatch> or L<Try::Tiny>.)
-The type parameter must be valid against the type constraint given. If you pass
-an invalid value this throws a hard Moose exception. You'll need to capture it
-in an eval or related exception catching system (see L<TryCatch> or <Try::Tiny>.)
For example the following would throw a hard error (and not just return false)
RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
-If you can't accept a hard exception here, you'll need to test the constraining
-values first, as in:
+In the above case the 'min' value is larger than the 'max', which violates the
+Range constraint. We throw a hard error here since I think incorrect type
+parameters are most likely to be the result of a typo or other true error
+conditions.
+
+If you can't accept a hard exception here, you can either trap it as advised
+above or you need to test the constraining values first, as in:
my $range = {min=>99, max=>10};
if(my $err = Range->validate($range)) {
parameters, so that the above could also be written as:
RangedInt([min=>10,max=>100])->check(50); ## OK
- RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
- RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
+ RangedInt([min=>50, max=>75])->check(99); ## Not OK, exceeds max
+ RangedInt([min=>99, max=>10])->check(10); ## Exception, not valid Range
This is the preferred syntax, as it improve readability and adds to the
-conciseness of your type constraint declarations. An exception wil be thrown if
-your type parameters don't match the required reference type.
+conciseness of your type constraint declarations.
Also note that if you 'chain' parameterization results with a method call like:
such as when you use the type constraint in the options section of a L<Moose>
attribute declaration, or when defining type libraries.
-==head2 Subtyping a Parameterizable type constraints
+=head2 Subtyping a Parameterizable type constraints
When subclassing a parameterizable type you must be careful to match either the
required type parameter type constraint, or if re-parameterizing, the new
shift >= 0;
};
-In this case you'd now have a parameterizable type constraint called which
-would work like:
+In this case you'd now have a parameterizable type constraint which would
+work like:
Test::More::ok PositiveRangedInt([{min=>-10, max=>75}])->check(5);
Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5);
In other words, given the example above, a type constraint of 'RangedInt' would
have a parent of 'Int', not 'Parameterizable' and for all intends and uses you
-could stick it wherever you'd need an Int.
+could stick it wherever you'd need an Int. You can't change the parent, even
+to make it a subclass of Int.
=head2 Coercions
more other types. Please see L<Moose::Cookbook::Basics::Recipe5> for an example
of type coercions if you are not familiar with the subject.
-L<MooseX::Types::Parameterizable> support type coercions in all the ways you
+L<MooseX::Types::Parameterizable> supports type coercions in all the ways you
would expect. In addition, it also supports a limited form of type coercion
inheritance. Generally speaking, type constraints don't inherit coercions since
this would rapidly become confusing. However, since your parameterizable type
types.
For the purposes of this discussion, a parameterizable type is a subtype created
-when you say, "as Parameterizable[..." in your sub type declaration. For example
+when you say, "as Parameterizable[..." in your sub type declaration. For
+example:
subtype Varchar,
as Parameterizable[Str, Int],
},
message { "'$_' is too long" };
-This is the </SYNOPSIS> example, which creates a new parameterizable subtype of
+This is the L</SYNOPSIS> example, which creates a new parameterizable subtype of
Str which takes a single type parameter which must be an Int. This Int is used
to constrain the allowed length of the Str value.
This parameterizable subtype, "Varchar" itself is something you'd never use
directly to constraint a value. In other words you'd never do something like:
- has name => (isa=>Varchar, ...)
+ has name => (isa=>Varchar, ...); ## Why not just use a Str?
You are going to do this:
its parent. This may be a bit surprising to L<Moose> developers, but I believe
this is the actual desired behavior.
-You can of course add extra coercions to a parameterized type:
+You can of course add new coercions to a subtype of a parameterizable type:
- subtype Max40CharStr,
- as Varchar[40];
+ subtype MySpecialVarchar,
+ as Varchar;
- coerce Max40CharStr,
+ coerce MySpecialVarchar,
from ...
-In which case this new parameterized type would inherit coercions from it's parent
-parameterizable type (Varchar), as well as the additional coercions you've added.
+In which case this new parameterizable type would NOT inherit coercions from
+it's parent parameterizable type (Varchar). This is done in keeping with how
+generally speaking L<Moose> type constraints avoid complicated coercion inheritance
+schemes, however I am open to discussion if there are valid use cases.
+
+NOTE: One thing you can't do is add a coercion to an already parameterized type.
+Currently the following would throw a hard error:
+
+ subtype 40CharStr,
+ as Varchar[40];
+
+ coerce 40CharStr, ... # BANG!
+
+This limitation is enforced since generally we expect coercions on the parent.
+However if good use cases arise we may lift this in the future.
+
+In general we are trying to take a conservative approach that keeps in line with
+how most L<Moose> authors expect type constraints to work.
=head2 Recursion
- TBD - Need more tests.
+ TBD - Needs a use case... Anyone?
=head1 TYPE CONSTRAINTS
This type library defines the following constraints.
-=head2 Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint]
+=head2 Parameterizable[ParentTypeConstraint, ConstrainingValueTypeConstraint]
Create a subtype of ParentTypeConstraint with a dependency on a value that can
-pass the ParameterizableValueTypeConstraint. If ParameterizableValueTypeConstraint is empty
+pass the ConstrainingValueTypeConstraint. If ConstrainingValueTypeConstraint is empty
we default to the 'Any' type constraint (see L<Moose::Util::TypeConstraints>).
-
-This creates a type constraint which must be further parameterized at later time
-before it can be used to ->check or ->validate a value. Attempting to do so
-will cause an exception.
+This is useful if you are creating some base Parameterizable type constraints
+that you intend to sub class.
=cut
)
);
+=head1 SEE ALSO
+
+The following modules or resources may be of interest.
+
+L<Moose>, L<Moose::Meta::TypeConstraint>, L<MooseX::Types>
+
=head1 AUTHOR
John Napiorkowski, C<< <jjnapiork@cpan.org> >>