Subject: | How to produce proper error msg when Type coercion fails due to user-defined constraint |
Date: | Sun, 27 Mar 2016 22:40:40 -0700 |
To: | bug-moose [...] rt.cpan.org |
From: | Jim Avera <jim.avera [...] gmail.com> |
Hello,
I'm trying to write something which will make Moose classes "just work" when
used by programs which happen to have "use bigint" or similar in scope.
Classes
defined using Moose do not work by default, if attributes use type Int
or Num,
because ordinary literal numbers like 42 in the calling program are
compiled into
Math::BigInt (or BigFloat or BigRat) when "use big*" is in effect.
The goal is to auto-convert Math::Big* values so they can be passed for
Int or Num
attributes, and issue a comprehensible error message if the value can
not be
converted without loss of information.
I tried creating a subtype with a "where" clause which checks that the
value is
convertible, and then define a coercion from that subtype to Int.
However it appears that the subtype is simply _ignored_ if the "where"
clause
fails, rather than causing an error using the subtype's message {...} .
The result is that if the value it too large, a generic "wrong type"
error occurs
which is confusing.
I tried doing the range check inside the "via" code of the coerce, and
die'ing if the test failed.
This made my error message appear, but without any context, i.e.,
without indicating where in the program the error occurred or which
attribute had the bad value.
==> So: Is there a way for a *coerce* to throw an error which will be
displayed with context information, or alternatively, to _force_ a
coerce to execute a subtype cast such that if the "where" condition
of the subtype fails, then the "message" from the subtype is displayed?
use Moose::Util::TypeConstraints;
subtype 'CoercableBigInt', as 'Math::BigInt',
where { use bigint; abs($_) < 0x7fffffff },
message { "$_ is too large to coerce to Int" } # this message
does NOT appear
;
coerce 'Int', from 'CoercableBigInt', via { $_->numify() };
package MyClass;
use Moose;
has 'i' => (is => 'rw', isa => 'Int', coerce => 1);
package main;
use bigint;
# this works, as expected
my $obj = MyClass->new(i => 42);
# This produces a confusing generic "wrong type" error, not the
# message in the message{...} block of the CoercableBigInt subtype
my $obj2 = MyClass->new(i => 42*1000000*1000000);