Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow replacing Error::TypeTiny::Assertion #133

Closed
Ovid opened this issue Mar 1, 2023 · 6 comments
Closed

Allow replacing Error::TypeTiny::Assertion #133

Ovid opened this issue Mar 1, 2023 · 6 comments
Labels
enhancement implemented Implemented in repo, but not released yet
Milestone

Comments

@Ovid
Copy link

Ovid commented Mar 1, 2023

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?)

@tobyink
Copy link
Owner

tobyink commented Mar 1, 2023

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?

@Ovid
Copy link
Author

Ovid commented Mar 1, 2023

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.

@Ovid
Copy link
Author

Ovid commented Mar 1, 2023

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.

@tobyink
Copy link
Owner

tobyink commented Mar 1, 2023

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";

@tobyink tobyink added this to the 2.4.0 milestone Mar 6, 2023
@tobyink tobyink added the implemented Implemented in repo, but not released yet label Apr 1, 2023
@tobyink
Copy link
Owner

tobyink commented Apr 1, 2023

With the above patch, something like this should work:

isa => HashRef->of( Num )->create_child_type( exception_class => 'My::Exception::Class' ),

tobyink added a commit that referenced this issue Apr 1, 2023
@tobyink
Copy link
Owner

tobyink commented Apr 2, 2023

This is included in Type::Tiny 2.003_000 on CPAN. I should release a stable version some time this month.

@tobyink tobyink closed this as completed Apr 5, 2023
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this issue Jun 30, 2023
Upstream changes:
2.004000        2023-04-05

 [ Documentation ]
 - Document that the `BoolLike` type is unstable.
 - Minor pod changes to Types::Standard.

 [ Packaging ]
 - Summarized the change log for versions prior to Type::Tiny 2.000000. If
   you need more information, see the Changes file included with Type::Tiny
   2.002001.
   <https://metacpan.org/release/TOBYINK/Type-Tiny-2.002001/source/Changes>

2.003_000       2023-04-02

 [ Documentation ]
 - Add SYNOPSIS for Type::Tiny::Class.
 - Add SYNOPSIS for Type::Tiny::Duck.
 - Add SYNOPSIS for Type::Tiny::Enum.
 - Add SYNOPSIS for Type::Tiny::Intersection.
 - Add SYNOPSIS for Type::Tiny::Role.
 - Add SYNOPSIS for Type::Tiny::Union.
 - Add documentation and tests for the combination of the `goto_next` and
   `multiple` options when used with `signature_for`.
 - Add example of `signature_for` applying a signature to multiple
   functions at once.
 - Document changes to `make_immutable` in Type::Library v2.x.
   <tobyink/p5-type-tiny#129>

 [ Other ]
 - Added: Type::Tiny now has an `exception_class` attribute, allowing a
   type to throw exceptions using a custom class. These classes should
   usually be a subclass of Error::TypeTiny::Assertion.
   <tobyink/p5-type-tiny#133>
 - Added: Type::Tiny::Bitfield class.
   <tobyink/p5-type-tiny#131>
 - Added: Types::TypeTiny::BoolLike type constraint.
   <tobyink/p5-type-tiny#137>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement implemented Implemented in repo, but not released yet
Projects
None yet
Development

No branches or pull requests

2 participants