如何在构建过程中设置 ro 属性?

How to set ro attributes during build?

我正在编写一个脚本来帮助我精通 Moose。我有以下代码:

package Dir;
use Moose;
use Modern::Perl;
use File;

has 'dirs' =>             (is => 'ro', isa => 'HashRef[Dir]' );  
has 'files' =>            (is => 'ro', isa => 'HashRef[File]'); 
has 'dir_class' =>        (is => 'ro', isa => 'ClassName', default => 'Dir');
has 'file_class' =>       (is => 'ro', isa => 'ClassName', default => 'File');

sub BUILD {
  my $self = shift;
  my $path = $self->path;
  my $name = $self->name;
  my (%dirs, %files);

  # populate dirs attribute with LaborData::Data::Dir objects
  opendir my $dh, $path or die "Can't opendir '$path': $!";

  # Get files and dirs and separate them out
  my @dirs_and_files = grep { ! m{^\.$|^\.\.$} } readdir $dh;
  closedir $dh or die "Can't closedir '$path': $!";
  my @dir_names         = grep { -d "$path/$_" } grep { !m{^\.}  } @dirs_and_files;
  my @file_names        = grep { -f "$path/$_" } grep { !m{^\.}  } @dirs_and_files;

  # Create objects
  map { $dirs{$_}         = $self->dir_class->new  ( path => $path . '/' . $_ ) } @dir_names;
  map { $files{$_}        = $self->file_class->new ( path => $path . '/' . $_ ) } @file_names;

  # Set attributes
  $self->dirs         ( \%dirs );
  $self->files        ( \%files );
}

代码导致以下错误:died: Moose::Exception::CannotAssignValueToReadOnlyAccessor (Cannot assign a value to a read-only accessor at reader Dir::dirs

为了解决这个错误,我可以为 dirsfiles 属性创建属性 rw 或使用 builder 方法。前一种方案不可取,后一种方案需要重复代码(比如目录需要打开两次),同样不可取。

这个问题的最佳解决方案是什么?

我找到了一个可能的解决方案,尽管它不受欢迎:

  # Set attributes
  $self->{dirs}  =    \%dirs;
  $self->{files} =    \%files;

您可以将 a writer 分配给您的 read-only 属性并在您的 BUILD 内部使用它。用 _ 命名它以表明它是内部的。

package Foo;
use Moose;

has bar => ( is => 'ro', writer => '_set_bar' );

sub BUILD {
    my $self = shift;

    $self->_set_bar('foobar');
}

package main;
Foo->new;

这不会抛出异常。

它与rw本质上是一样的,但现在编写器与reader不是同一个访问器。 _ 表示它是内部的,因此它比仅使用 rw 更不受欢迎。请记住,无论如何您都无法真正保护 Perl 中的任何内容。如果您的用户想要了解内部结构,他们会的。