=pod

=encoding utf-8

=head1 NAME

Type::Tiny::Manual::UsingWithClassTiny - use of Type::Tiny with Class::Tiny

=head1 MANUAL

L<Class::Tiny> is an even-smaller-than-Moo class builder.

Let's translate the classic Horse class from Moo to Class::Tiny.

Moo:

  package Horse {
    use Moo;
    use Types::Standard qw( Str Num ArrayRef );
    use namespace::autoclean;
    
    has name       => ( is => 'ro', isa => Str, required => 1 );
    has gender     => ( is => 'ro', isa => Str );
    has age        => ( is => 'rw', isa => Num );
    has children   => (
      is       => 'ro',
      isa      => ArrayRef,
      default  => sub { return [] },
    );
  }

Class::Tiny:

  package Horse {
    use Class::Tiny qw( gender age ), {
      name     => sub { die "name is required"; },
      children => sub { return [] },
    };
    use Types::Standard qw( Str Num ArrayRef Dict Optional Slurpy Any Object );
    use Type::Params qw( signature_for );
    use namespace::autoclean;
    
    # type checks
    signature_for BUILD => (
      method => Object,
      named  => [
        name       => Str,
        gender     => Optional[Str],
        age        => Optional[Num],
        children   => Optional[ArrayRef],
        ()         => Slurpy[Any],
      ],
      fallback => 1,
    );
    signature_for [ 'name', 'gender', 'children' ] => (
      method     => Object,
      positional => [],
    );
    signature_for age => (
      method     => Object,
      positional => [ Optional[Num] ],
    );
  }

What's going on here?

Well, Class::Tiny, after it has built a new object, will do this:

  $self->BUILD($args);

(Technically, it calls C<BUILD> not just for the current class, but for all
parent classes too.) We can hook onto this in order to check type constraints
for the constructor.

We use C<signature_for> from L<Type::Params> to wrap the original C<BUILD>
method (which doesn't exist, so C<< fallback => 1 >> will just assume an
empty sub) with a type check for its arguments. The type check is just a
B<Dict> that checks the class's required and optional attributes and includes
B<< Slurpy[Any] >> at the end to be flexible for subclasses adding new
attributes.

Then we wrap the C<name>, C<gender>, and C<children> methods with checks
to make sure they're only being called as getters, and we wrap C<age>,
allowing it to be called as a setter with a B<Num>.

There are also a couple of CPAN modules that can help you out.

=head2 Class::Tiny::ConstrainedAccessor

L<Class::Tiny::ConstrainedAccessor> creates a C<BUILD> and accessors that
enforce Type::Tiny constraints.  Attribute types are passed to
Class::Tiny::ConstrainedAccessor; attribute defaults are passed to Class::Tiny.

  package Horse {
    use Types::Standard qw( Str Num ArrayRef );
    use Class::Tiny::ConstrainedAccessor {
      name     => Str,
      gender   => Str,
      age      => Num,
      children => ArrayRef,
    };
    use Class::Tiny qw( gender age ), {
      name     => sub { die "name is required"; },
      children => sub { return [] },
    };
  }

=head2 Class::Tiny::Antlers

L<Class::Tiny::Antlers> provides Moose-like syntax for Class::Tiny, including
support for C<isa>.  You do not also need to use Class::Tiny itself.

  package Horse {
    use Class::Tiny::Antlers qw(has);
    use Types::Standard qw( Str Num ArrayRef );
    use namespace::autoclean;
    
    has name       => (
      is        => 'ro',
      isa       => Str,
      default   => sub { die "name is required" },
    );
    has gender     => ( is => 'ro',    isa => Str );
    has age        => ( is => 'rw',    isa => Num );
    has children   => (
      is        => 'ro',
      isa       => ArrayRef,
      default   => sub { return [] },
    );
  }

=head1 NEXT STEPS

Here's your next step:

=over

=item * L<Type::Tiny::Manual::UsingWithOther>

Using Type::Tiny with Class::InsideOut, Params::Check, and Object::Accessor.

=back

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=head1 COPYRIGHT AND LICENCE

This software is copyright (c) 2013-2014, 2017-2023 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=cut

