I want to create a type, based on the string, which will have upper length limit, and - optionally - lower length limit. I.e., parameterized type, where length range would be a parameter.
What I want in my implementation:
- A separate type for string length range.
- Not using MooseX::Types::Parameterizable
- A sugar of parametrizing the type straight with arrayref, NOT hashref:
- This:
isa=>Varchar[1, 15]
- Not this:
isa=>Varchar[{min=>1, max=>15,}]
- This:
That's what I have so far:
File MyTypesTiny.pm
package MyTypesTiny;
use strict;
use warnings;
use Type::Library
-base,
-declare => qw( VarcharRange Varchar );
use Type::Utils -all;
use Types::Standard -types;
use MooseX::Types::Common::Numeric qw( PositiveOrZeroInt );
declare VarcharRange,
as HashRef [PositiveOrZeroInt],
where {
return 0 if ( grep { $_ ne 'min' && $_ ne 'max' } keys %{$_} );
return ( $_->{min} <= $_->{max} )
if ( defined $_->{max} && defined $_->{min} );
return 1;
}, message { "$_" };
coerce VarcharRange, from ArrayRef [PositiveOrZeroInt], via {
my $result;
my @keys = qw(min max);
foreach my $val ( reverse @$_ ) {
my $key = pop @keys // 'bad_range';
$result->{$key} = $val;
}
return $result;
};
1;
File test_varchar.pl
#!/usr/bin/env perl
package MyClass;
use Moose;
use MyTypesTiny qw( VarcharRange );
has 'my_range' => (isa=>VarcharRange, is=>'ro', coerce=>1);
package main;
use MyClass;
my $check = MyClass->new(
my_range => [1, 15], # works, as expected
# my_range => [1, 0], # fails, as expected
# my_range => [0, 1, 2], # fails, as expected
);
Ok, VarcharRange works.
Now I have to add Varchar itself. And that's where I get stuck instantly:
added to MyTypesTiny.pm:
declare Varchar, as Str, where {}, constraint_generator => sub {
# here I have @_ which is an ArrayRef
# and I want to create a VarcharRange object $range from it
# but what exactly should I do?
return sub {
my $len = length($_);
return 0 if ( $range->{min} && $len < $range->{min} );
return 0 if ( $range->{max} && $len > $range->{max} );
return 1;
};
};
My brain is boiling. I have my ArrayRef ready. All I need is a VarcharRange (which is basically a HashRef) object to be made from it. But VarcharRange is a type - a name marking set of constraints and coercion rules. It does not correspond to an object per se. Objects for types are created when class attributes are created, but I don't have any class in play here.
This is an answer that gives you the ability to give parameters to the "Varchar" type. The magic that enables parameterised types is to provide a
constraint_generator
to the type. This solution does not have the intermediate hashref, and it only has one type.MyTypesTiny.pm:
MyClass.pm:
tester.pl:
That's what I ended up with. Had to introduce an extra class. It works, and I'll probably stop here.
Class for string length range:
Parametrizable type:
And test template to play with: