Manual
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
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
signature_for
from
Type::Params
to wrap the original
BUILD
method (which doesn't exist, so
fallback => 1
will just assume an empty sub) with a type check for its arguments. The type check is just a
Dict
that checks the class's required and optional attributes and includes
Slurpy[Any]
at the end to be flexible for subclasses adding new attributes.
Then we wrap the
name
,
gender
, and
children
methods with checks to make sure they're only being called as getters, and we wrap
age
, allowing it to be called as a setter with a
Num
.
There are also a couple of CPAN modules that can help you out.
Class::Tiny::ConstrainedAccessor
Class::Tiny::ConstrainedAccessor
creates a
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 [] },
};
}
Class::Tiny::Antlers
Class::Tiny::Antlers
provides Moose-like syntax for Class::Tiny, including support for
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 [] },
);
}
Next Steps
Here's your next step:
-
Type::Tiny::Manual::UsingWithOther
Using Type::Tiny with Class::InsideOut, Params::Check, and Object::Accessor.