X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=gitmo%2FMooseX-Dependent.git;a=blobdiff_plain;f=README;h=fffa7e6ae3d815d785c8c7a052678e94af43f62a;hp=4f5099d304ad87e59bdde879c58128fe726aefc7;hb=HEAD;hpb=afdaaf5243063b5bb9230df03bf07aad5c71207a diff --git a/README b/README index 4f5099d..fffa7e6 100644 --- a/README +++ b/README @@ -11,9 +11,7 @@ SYNOPSIS 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 - ## an SQL Varchar type. + Create a type constraint that is similar to SQL Varchar type. subtype Varchar, as Parameterizable[Str,Int], @@ -23,6 +21,8 @@ SYNOPSIS }, message { "'$_' is too long" }; + Coerce an ArrayRef to a string via concatenation. + coerce Varchar, from ArrayRef, via { @@ -32,22 +32,25 @@ SYNOPSIS 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', + 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/], + varchar_five => [qw/aa bb/], ## coerces to "aabb" varchar_ten => '123456789', ); @@ -56,13 +59,20 @@ SYNOPSIS DESCRIPTION A MooseX::Types library for creating parameterizable types. A parameterizable type constraint for all intents and uses is a subclass - of a parent type, but adds a secondary type parameter which is available - to constraint callbacks (such as inside the 'where' clause) or in the - coercions. + 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 you define for a given + type constraint. - This allows you to create a type that has additional runtime advice, - such as a set of numbers within which another number must be unique, or - allowable ranges for a integer, such as in: + If you have Moose experience, you probably are familiar with the builtin + parameterizable type constraints 'ArrayRef' and 'HashRef'. This type + constraint lets you generate your own versions of parameterized + constraints that work similarly. See Moose::Util::TypeConstraints for + more. + + Using this type constraint, you can generate new type constraints that + have additional runtime advice, such as being able to specify maximum + and minimum values for an Int (integer) type constraint: subtype Range, as Dict[max=>Int, min=>Int], @@ -80,15 +90,30 @@ DESCRIPTION }; 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. - This throws a hard Moose exception. You'll need to capture it in an eval - or related exception catching system (see TryCatch or .) + 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 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)) { @@ -103,25 +128,23 @@ DESCRIPTION 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 not that if you 'chain' parameterization results with a method call - like: + Also note that if you 'chain' parameterization results with a method + call like: TypeConstraint([$ob])->method; You need to have the "(...)" around the ArrayRef in the Type Constraint - parameters. This seems to have something to do with the precendent level - of "->". Patches or thoughts welcomed. You only need to do this in the - above case which I imagine is not a very common case. - - ==head2 Subtyping a Parameterizable type constraints + parameters. You can skip the wrapping parenthesis in the most common + cases, such as when you use the type constraint in the options section + of a Moose attribute declaration, or when defining type libraries. + 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 type constraints are a subtype of the parent. @@ -143,7 +166,18 @@ DESCRIPTION shift >= 0; }; - Or you could have done the following instead: + 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); + + Of course the above is somewhat counter-intuitive to the reader, since + we have defined our 'RangedInt' in such as way as to let you declare + negative ranges. For the moment each type constraint rule is apply + without knowledge of any other rule, nor can a rule 'inform' existing + rules. This is a limitation of the current system. However, you could + instead do the following: ## Subtype of Int for positive numbers subtype PositiveInt, @@ -161,113 +195,126 @@ DESCRIPTION subtype PositiveRangedInt, as RangedInt[PositiveRange]; + This would constrain values in the same way as the previous type + constraint but have the bonus that you'd throw a hard exception if you + try to use an incorrect range: + + Test::More::ok PositiveRangedInt([{min=>10, max=>75}])->check(15); ## OK + Test::More::ok !PositiveRangedInt([{min=>-10, max=>75}])->check(-5); ## Dies + Notice how re-parameterizing the parameterizable type 'RangedInt' works slightly differently from re-parameterizing 'PositiveRange' Although it initially takes two type constraint values to declare a parameterizable type, should you wish to later re-parameterize it, you only use a - subtype of the second type parameter (the parameterizable type - constraint) since the first type constraint sets the parent type for the - parameterizable type. 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. - - subtype NameAge, - as Tuple[Str, Int]; - - ## re-parameterized subtypes of NameAge containing a Parameterizable Int - subtype NameBetween18and35Age, - as NameAge[ - Str, - PositiveRangedInt[min=>18,max=>35], - ]; - - One caveat is that you can't stick an unparameterized parameterizable - type inside a structure, such as MooseX::Types::Structured since that - would require the ability to convert a 'containing' type constraint into - a parameterizable type, which is a capacity we current don't have. + subtype of the extra type parameter (the parameterizable type + constraints) since the first type constraint sets the parent type for + the parameterizable type. - Coercions - Parameterizable types have some limited support for coercions. Several - things must be kept in mind. The first is that the coercion targets the - type constraint which is being made parameterizable, Not the - parameterizable type. So for example if you create a Parameterizable - type like: + 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. You + can't change the parent, even to make it a subclass of Int. - subtype RequiredAgeInYears, - as Int; + Coercions + A type coercion is a rule that allows you to transform one type from one + or more other types. Please see Moose::Cookbook::Basics::Recipe5 for an + example of type coercions if you are not familiar with the subject. + + 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 is intended to become parameterized in order to be + useful, we support inheriting from a 'base' parameterizable type + constraint to its 'child' parameterized sub 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: - subtype PersonOverAge, - as Parameterizable[Person, RequiredAgeInYears] + subtype Varchar, + as Parameterizable[Str, Int], where { - my ($person, $required_years_old) = @_; - return $person->years_old > $required_years_old; - } + my($string, $int) = @_; + $int >= length($string) ? 1:0; + }, + message { "'$_' is too long" }; - This would validate the following: + This is the "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. - my $person = Person->new(age=>35); - PersonOverAge([18])->check($person); + Now, this new sub type, "Varchar", is parameterizable since it can take + a type parameter. We can apply some coercions to it: - You can then apply the following coercion + coerce Varchar, + from Object, + via { "$_"; }, ## stringify the object + from ArrayRef, + via { join '',@$_ }; ## convert array to string - coerce PersonOverAge, - from Dict[age=>int], - via {Person->new(%$_)}, - from Int, - via {Person->new(age=>$_)}; + 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: - This coercion would then apply to all the following: + has name => (isa=>Varchar, ...); ## Why not just use a Str? - PersonOverAge([18])->check(30); ## via the Int coercion - PersonOverAge([18])->check({age=>50}); ## via the Dict coercion + You are going to do this: - However, you are not allowed to place coercions on parameterizable types - that have had their constraining value filled, nor subtypes of such. For - example: + has name => (isa=>Varchar[40], ...) - coerce PersonOverAge[18], - from DateTime, - via {$_->years}; + Which is actually useful. However, "Varchar[40]" is a parameterized + type, it is a subtype of the parameterizable "Varchar" and it inherits + coercions from its parent. This may be a bit surprising to Moose + developers, but I believe this is the actual desired behavior. - That would generate a hard exception. This is a limitation for now until - I can devise a smarter way to cache the generated type constraints. - However, I doubt it will be a significant limitation, since the general - use case is supported. + You can of course add new coercions to a subtype of a parameterizable + type: - Lastly, the constraining value is available in the coercion in much the - same way it is available to the constraint. + subtype MySpecialVarchar, + as Varchar; - ## Create a type constraint where a Person must be in the set - subtype PersonInSet, - as Parameterizable[Person, PersonSet], - where { - my ($person, $person_set) = @_; - $person_set->find($person); - } - - coerce PersonInSet, - from HashRef, - via { - my ($hashref, $person_set) = @_; - return $person_set->create($hash_ref); - }; + coerce MySpecialVarchar, + from ... + + 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 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 Moose authors expect type constraints to work. Recursion - TBD + TBD - Needs a use case... Anyone? TYPE CONSTRAINTS This type library defines the following constraints. - Parameterizable[ParentTypeConstraint, ParameterizableValueTypeConstraint] + Parameterizable[ParentTypeConstraint, ConstrainingValueTypeConstraint] Create a subtype of ParentTypeConstraint with a dependency on a value - that can pass the ParameterizableValueTypeConstraint. If - ParameterizableValueTypeConstraint is empty we default to the 'Any' type - constraint (see Moose::Util::TypeConstraints). + that can pass the ConstrainingValueTypeConstraint. If + ConstrainingValueTypeConstraint is empty we default to the 'Any' type + constraint (see Moose::Util::TypeConstraints). This is useful if you are + creating some base Parameterizable type constraints that you intend to + sub class. + +SEE ALSO + The following modules or resources may be of interest. - 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. + Moose, Moose::Meta::TypeConstraint, MooseX::Types AUTHOR John Napiorkowski, ""