1 package ## Hide from PAUSE
2 MooseX::Meta::TypeConstraint::Parameterizable;
5 use Moose::Util::TypeConstraints ();
6 use MooseX::Meta::TypeCoercion::Parameterizable;
7 use Scalar::Util qw(blessed);
11 extends 'Moose::Meta::TypeConstraint';
15 MooseX::Meta::TypeConstraint::Parameterizable - Parameterizable Meta Class.
19 See L<MooseX::Types::Parameterizable> for how to use parameterizable
20 types. This class is a subclass of L<Moose::Meta::TypeConstraint> which
21 provides the gut functionality to enable parameterizable type constraints.
23 You probably won't need to subclass or consume this class directly.
27 This class defines the following attributes.
29 =head2 parent_type_constraint
31 The type constraint whose validity is being made parameterizable.
35 has 'parent_type_constraint' => (
37 isa=>Moose::Util::TypeConstraints::class_type('Moose::Meta::TypeConstraint'),
39 Moose::Util::TypeConstraints::find_type_constraint("Any");
45 =head2 constraining_value_type_constraint
47 This is a type constraint which defines what kind of value is allowed to be the
48 constraining value of the parameterizable type.
52 has 'constraining_value_type_constraint' => (
54 isa=>Moose::Util::TypeConstraints::class_type('Moose::Meta::TypeConstraint'),
56 Moose::Util::TypeConstraints::find_type_constraint("Any");
61 =head2 constraining_value
63 This is the actual value that constraints the L</parent_type_constraint>
67 ## TODO, this is where we probably should break out Parameterized stuff from
70 has 'constraining_value' => (
72 predicate=>'has_constraining_value',
77 This class defines the following methods.
81 Do some post build stuff, mostly make sure we set the correct coercion object.
86 my ($new, $class, @args) = @_;
87 my $self = $class->$new(@args);
88 my $coercion = MooseX::Meta::TypeCoercion::Parameterizable->new(type_constraint => $self);
89 $self->coercion($coercion);
93 =head2 parameterize (@args)
95 Given a ref of type constraints, create a parameterized constraint
101 my $class = ref $self;
103 Moose->throw_error("$self already has a constraining value.") if
104 $self->has_constraining_value;
106 if(blessed $_[0] && $_[0]->isa('Moose::Meta::TypeConstraint')) {
109 if(blessed $_[0] && $_[0]->isa('Moose::Meta::TypeConstraint')) {
110 my $arg2 = shift @_ || $self->constraining_value_type_constraint;
112 ## TODO fix this crap!
113 Moose->throw_error("$arg2 is not a type constraint")
114 unless $arg2->isa('Moose::Meta::TypeConstraint');
116 Moose->throw_error("$arg1 is not a type of: ".$self->parent_type_constraint->name)
117 unless $arg1->is_a_type_of($self->parent_type_constraint);
119 Moose->throw_error("$arg2 is not a type of: ".$self->constraining_value_type_constraint->name)
120 unless $arg2->is_a_type_of($self->constraining_value_type_constraint);
122 Moose->throw_error('Too Many Args! Two are allowed.') if @_;
124 my $name = $self->_generate_subtype_name($arg1, $arg2);
125 if(my $exists = Moose::Util::TypeConstraints::find_type_constraint($name)) {
128 my $type_constraint = $class->new(
131 constraint => $self->constraint,
132 parent_type_constraint=>$arg1,
133 constraining_value_type_constraint => $arg2,
135 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint($type_constraint);
136 return $type_constraint;
139 Moose->throw_error("$arg1 is not a type of: ".$self->constraining_value_type_constraint->name)
140 unless $arg1->is_a_type_of($self->constraining_value_type_constraint);
142 my $name = $self->_generate_subtype_name($self->parent_type_constraint, $arg1);
143 if(my $exists = Moose::Util::TypeConstraints::find_type_constraint($name)) {
146 my $type_constraint = $class->new(
149 constraint => $self->constraint,
150 parent_type_constraint=>$self->parent_type_constraint,
151 constraining_value_type_constraint => $arg1,
153 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint($type_constraint);
154 return $type_constraint;
159 ## Jump through some hoops to let them do tc[key=>10] and tc[{key=>10}]
162 if($self->constraining_value_type_constraint->is_a_type_of('HashRef')) {
172 ## TODO: Is there a use case for parameterizing null or undef?
173 Moose->throw_error('Cannot Parameterize null values.');
176 if(my $err = $self->constraining_value_type_constraint->validate($args)) {
177 Moose->throw_error($err);
182 $sig = Digest::MD5::md5_hex(Data::Dump::dump($args));
184 my $name = $self->name."[$sig]";
185 if(my $exists = Moose::Util::TypeConstraints::find_type_constraint($name)) {
191 constraint => $self->constraint,
192 constraining_value => $args,
193 parent_type_constraint=>$self->parent_type_constraint,
194 constraining_value_type_constraint => $self->constraining_value_type_constraint,
195 message => $self->message,
202 =head2 _generate_subtype_name
204 Returns a name for the parameterizable type that should be unique
208 sub _generate_subtype_name {
209 my ($self, $parent_tc, $constraining_tc) = @_;
212 $parent_tc, $constraining_tc,
216 =head2 create_child_type
218 modifier to make sure we get the constraint_generator
222 around 'create_child_type' => sub {
223 my ($create_child_type, $self, %opts) = @_;
224 if($self->has_constraining_value) {
225 $opts{constraining_value} = $self->constraining_value;
227 return $self->$create_child_type(
230 parent_type_constraint=>$self->parent_type_constraint,
231 constraining_value_type_constraint => $self->constraining_value_type_constraint,
235 =head2 equals ($type_constraint)
237 Override the base class behavior so that a parameterizable type equal both the parent
238 type and the overall parameterizable container. This behavior may change if we can
239 figure out what a parameterizable type is (multiply inheritance or a role...)
243 around 'equals' => sub {
244 my ( $equals, $self, $type_or_name ) = @_;
246 my $other = defined $type_or_name ?
247 Moose::Util::TypeConstraints::find_type_constraint($type_or_name) :
248 Moose->throw_error("Can't call $self ->equals without a parameter");
250 Moose->throw_error("$type_or_name is not a registered Type")
253 if(my $parent = $other->parent) {
254 return $self->$equals($other)
255 || $self->parent->equals($parent);
257 return $self->$equals($other);
263 Method modifier to make sure we match on subtype for both the parameterizable type
264 as well as the type being made parameterizable
268 around 'is_subtype_of' => sub {
269 my ( $is_subtype_of, $self, $type_or_name ) = @_;
271 my $other = defined $type_or_name ?
272 Moose::Util::TypeConstraints::find_type_constraint($type_or_name) :
273 Moose->throw_error("Can't call $self ->equals without a parameter");
275 Moose->throw_error("$type_or_name is not a registered Type")
278 return $self->$is_subtype_of($other)
279 || $self->parent_type_constraint->is_subtype_of($other);
285 As with 'is_subtype_of', we need to dual dispatch the method request
289 around 'check' => sub {
290 my ($check, $self, @args) = @_;
292 $self->parent_type_constraint->check(@args) &&
299 As with 'is_subtype_of', we need to dual dispatch the method request
303 around 'validate' => sub {
304 my ($validate, $self, @args) = @_;
306 $self->parent_type_constraint->validate(@args) ||
307 $self->$validate(@args)
311 =head2 _compiled_type_constraint
313 modify this method so that we pass along the constraining value to the constraint
314 coderef and also throw the correct error message if the constraining value does
315 not match it's requirement.
319 around '_compiled_type_constraint' => sub {
320 my ($method, $self, @args) = @_;
321 my $coderef = $self->$method(@args);
323 if($self->has_constraining_value) {
324 $constraining = $self->constraining_value;
329 if(my $err = $self->constraining_value_type_constraint->validate($constraining)) {
330 Moose->throw_error($err);
332 $coderef->(@local_args, $constraining);
338 More method modification to support dispatch coerce to a parent.
342 around 'coerce' => sub {
343 my ($coerce, $self, @args) = @_;
344 if($self->has_constraining_value) {
345 push @args, $self->constraining_value;
347 if(@{$self->coercion->type_coercion_map}) {
348 my $coercion = $self->coercion;
349 my $coerced = $coercion->coerce(@args);
350 if(defined $coerced) {
353 my $parent = $self->parent;
354 return $parent->coerce(@args);
357 my $parent = $self->parent;
358 return $parent->coerce(@args);
364 The following modules or resources may be of interest.
366 L<Moose>, L<Moose::Meta::TypeConstraint>
370 John Napiorkowski, C<< <jjnapiork@cpan.org> >>
372 =head1 COPYRIGHT & LICENSE
374 This program is free software; you can redistribute it and/or modify
375 it under the same terms as Perl itself.
379 __PACKAGE__->meta->make_immutable(inline_constructor => 0);