Perl/Moose object initialization with coercion int

2019-08-06 11:37发布

Need make a module what accepts one or more paths, and coerce them to array of Class::Path. In the CPAN exists a module MooseX::Types::Path::Class. From it's source I discovered than the module defined subtypes Dir and File.

My example module:

package Web::Finder;
use namespace::sweep;
use Modern::Perl;

use Moose;
use MooseX::Types::Path::Class qw(Dir);  #??? - is this correct?
use Method::Signatures::Simple;
use Path::Class;

#should accept one or more directories as Str or as Path::Class::Dir
has 'volumes' => (
    is => 'ro',         
    isa => 'ArrayRef[Path::Class::Dir]', #Can I use here a simple 'ArrayRef[Dir]' ?
    required => 1,
    coerce => 1,
);

has 'fs' => (
    is => 'ro',
    isa => 'ArrayRef[File::System]',
    lazy => 1,
    builder => '_create_volumes',
);

#is the next correct?
method _create_volumes {
    push $self->fs, File::System->new('Real', root => $_->stringify) for ($self->volumes);
}

and my script

#!/usr/bin/env perl
use Modern::Perl;
use Web::Finder;

my $f = Web::Finder->new(volumes => .... ???

What I should change in the above module to accept the next initializations?

my $f = My::Module->new(volumes => "/some/path");

and

my $f = My::Module->new(volumes => [qw(/some/path1 /another/path2)] );

or something like - so: one or more paths...

From the error message I understand than me doing something wrong... ;)

You cannot coerce an attribute (volumes) unless its type (ArrayRef[Path::Class::Dir]) has a coercion at Web/Finder.pm line 14.
    require Web/Finder.pm called at x line 2
    main::BEGIN() called at Web/Finder.pm line 0
    eval {...} called at Web/Finder.pm line 0
Attribute (volumes) does not pass the type constraint because: Validation failed for 'ArrayRef[Path::Class::Dir]' with value ARRAY(0x7f812b0040b8) at constructor Web::Finder::new (defined at Web/Finder.pm line 33) line 42.
    Web::Finder::new("Web::Finder", "volumes", ARRAY(0x7f812b0040b8)) called at x line 6

The next part of question is how to create for each volume one File::System instance. Is the builder method correct?

Will be happy with any help and/or comments.

标签: perl moose
1条回答
该账号已被封号
2楼-- · 2019-08-06 11:57
  1. To coerce from one or more paths, you need to declare coercions from those types. MooseX::Types::Path::Class does define coercions but not ones that you need (they will only coerce to an individual Path::Class object, not an array of them).

    subtype 'ArrayOfDirs', as 'ArrayRef[Path::Class::Dir]';
    
    coerce 'ArrayOfDirs',
        # for one path
        from 'Str',           via { [ Path::Class::Dir->new($_) ] },
        # for multiple paths
        from 'ArrayRef[Str]', via { [ map { Path::Class::Dir->new($_) } @$_ ] }; 
    
    has 'volumes' => (
        is => 'ro',
        isa => 'ArrayOfDirs',
        required => 1,
        coerce => 1,
    );
    
  2. The builder method is expected to return an ArrayRef of File::System objects.

    sub _create_volumes {
        my ($self) = @_;
        return [
            map { File::System->new('Real', root => $_->stringify) }
                $self->volumes
        ];
    }
    
  3. Can you declare volumes as 'ArrayRef[Dir]'? No, not in that form. Dir in MooseX::Types::Path::Class is defined as a MooseX::Types type, which means that it needs to be used as a bareword, not a string.

    use MooseX::Types::Moose qw( ArrayRef );
    use MooseX::Types::Path::Class qw( Dir );
    
    has 'volumes' => (
        is => 'ro',
        isa => ArrayRef[Dir],
        required => 1,
        coerce => 1,
    );
    
查看更多
登录 后发表回答