(Update: As LanX also said in the meantime,) See the documentation for BUILDARGS in Moo:
use warnings;
use strict;
package MyDate {
use Moo;
use Types::Standard qw/ Int /;
use Carp;
use Date::Calc qw/Today/;
use namespace::clean;
has year => ( is=>'ro', required=>1, isa=>Int );
has month => ( is=>'ro', required=>1, isa=>Int );
has day => ( is=>'ro', required=>1, isa=>Int );
around BUILDARGS => sub {
my ( $orig, $class, %args ) = @_;
my %newargs;
# NOTE: This does not handle incorrect arguments!
if ( keys %args ) {
if ( keys %args == 3 ) {
%newargs = %args;
}
elsif ( $args{date} ) {
@newargs{qw/year month day/}
= $args{date}=~/^(\d{4})(\d\d)(\d\d)$/
or croak "bad date $args{date}";
}
}
else {
my ($year,$month,$day) = Today();
%newargs = (year=>$year, month=>$month, day=>$day);
}
return $class->$orig(%newargs);
};
sub ymd {
my $self = shift;
return $self->year.'-'.$self->month.'-'.$self->day;
}
}
print "1. ", MyDate->new()->ymd, "\n";
print "2. ", MyDate->new(year=>2019, month=>5, day=>14)->ymd, "\n";
print "3. ", MyDate->new(date=>20190512)->ymd, "\n";
__END__
1. 2019-5-16
2. 2019-5-14
3. 2019-05-12
Update: To be clear, this is just a template (as requested), since it doesn't really do any checking of the constructor args. Personally, I also like to add use MooX::StrictConstructor; after use namespace::clean; to prevent typos.