Allow replacing Error::TypeTiny::Assertion
Ovid opened this issue · 6 comments
In lib/Type/Tiny.pm
, we have this (some of it truncated to show structure):
sub _failed_check {
require Error::TypeTiny::Assertion;
my ( $self, $name, $value, %attrs ) = @_;
$self = $ALL_TYPES{$self} if defined $self && !ref $self;
my $exception_class =
delete( $attrs{exception_class} ) || "Error::TypeTiny::Assertion";
my $callback = delete( $attrs{on_die} );
if ( $self ) {
return $exception_class->throw_cb( ... );
);
}
else {
return $exception_class->throw_cb( ... );
}
} #/ sub _failed_check
However, every place that calls this does so like this: $self->_failed_check( "$self", $_ );
. So that attributes are not passed to _failed_check
and we can't replace the exception class.
(Of course, exception_class
isn't documented, so this is for some future code?)
A place you theoretically could use it would be subclassing Type::Params::Signature to override _make_constraint_fail
.
Under what circumstances were you hoping to override the class? On a per-type-constraint basis? In a particular lexical scope?
We have a very weird edge case where we're doing things like this with Moo(se):
has some_value => (
is => 'ro',
isa => SomeConstraint,
required => 1,
);
This is in some OpenAPI code. It would be nice if we could have SomeConstraint
, for this attribute, throw an HTTP::Throwable exception instead of Error::TypeTiny::Assertion
. With that, our code magically just works. Otherwise, we've done this:
around 'BUILDARGS' => sub {
my ( $orig, $class, @args ) = @_;
my $arg_for = $class->$orig(@args);
unless ( SomeConstraint->check($arg_for->{some_value}) ) {
... throw the exception we want, not the exception we'd get
}
...
So we have to write a bunch of fragile boilerplate when all we want is a different exception.
Oh, and many of these are in-house constraints, not just default ones shipped with Type::Tiny
. And we'd need different exceptions for different types of constraints. If I were to coerce an order from a an order id, it would be nice to have an invalid order id throw a 500, but if we don't find an order for that id, have it throw a 404.
I'd suggest blessing SomeConstraint
into a subclass of Type::Tiny and overriding _failed_check
, but _failed_check
is usually called as a function rather than a method. (Because that way it will still work if the original type constraint has gone out of scope. Yes, that can happen.) So that's not really an option.
I guess the best option for your use case would be for _failed_check
to do something like:
my $exception_class = delete( $attrs{exception_class} )
|| $self->{exception_class}
|| "Error::TypeTiny::Assertion";
With the above patch, something like this should work:
isa => HashRef->of( Num )->create_child_type( exception_class => 'My::Exception::Class' ),
This is included in Type::Tiny 2.003_000 on CPAN. I should release a stable version some time this month.