Have the next package
package MyTest;
use warnings;
use Moose;
use Types::Path::Tiny qw(AbsPath AbsFile);
has 'file' => (
is => 'ro',
isa => AbsPath,
required => 1,
coerce => 1,
);
no Moose;
__PACKAGE__->meta->make_immutable;
1;
works (nearly) ok, so when use it
use strict;
use warnings;
use feature 'say';
use Mytest;
use DDP;
my $t1 = MyTest->new(file => './t'); # the ./t is existing file in the filesystem
say $t1 ? "ok" : "no";
my $t2 = MyTest->new(file => './nonexistent_file');
say $t2 ? "ok" : "no";
p $t2;
says "ok" for both. and the $t2->file
is isa "Path::Tiny
But, I don't want create the object if the file isn't really exists in the filesystem. So, the second ($t2
) invocation should return undef
.
Changing the
isa => AbsPath,
to
isa => AbsFile,
will check the existence of the file, but if it isn't exists - the script will die with the
Attribute (file) does not pass the type constraint because: File '/tmp/nonexistent_file' does not exist
I don't want die, only want not create the MyTest
instance and return undef if the file isn't exists or it isn't a plain file. If the file exists the file should be a Path::Tiny
instance. (coerced from Str).
Can somebody help me?
The simplest way would be to catch and discard expected errors:
use Try::Tiny;
my $instance = try {
MyTest->new(file => './nonexistent_file');
} catch {
# only mute the constraint errors
return undef if /\AAttribute [(]\w+[)] does not pass the type constraint/;
die $_; # rethrow other errors
};
Mucking around with the constructor so that it returns undef
on failure is not a good idea, as there is an implied contract that ->new
will always return a valid instance. In older Perl code, returning a special value on failure is considered OK, but this forces additional checks on the caller – and checks can be forgotten. Moose has taken a more robust route by using exeptions instead (thus forcing that they be handled), although in this specific case this does add a bit boilerplate.
If you want to hide this boilerplate, consider writing a factory method.