Moose:在构造对象后将读写属性更改为只读

Moose: change read-write attributes to read-only after constructing the object

我想在调用 BUILD 方法后将对象的属性更改为只读。我该怎么做?

(上下文:我的程序使用 MooseX::YAML, and then changes its attributes in the BUILD method, to work around the limitation of YAML described here 从 YAML 加载这个对象,我想在它创建后将其刻在石头上。更具体地说,我的 YAML 代码声明了一个目录和一堆它下面的文件,但似乎无法在 YAML 中表达所有这些文件都必须在该目录中。当然,我可以将此目录名添加到所有这些文件名之前,使它们成为绝对文件名,但这意味着 1 ) 当我改变关于目录位置的想法时,我必须更改这些文件的所有条目,而不是仅更改目录名称,并且 2) 在创建对象之前从程序内部更改目录名称会很痛苦并且容易出错。)

(稍后添加:)一个最小的工作示例。

yaml1:

# invalid YAML, unfortunately:
dir: &dir /here
file1: *dir/foo
file2: *dir/bar
# ... and a lot more

yaml2:

# works, but requires repeating '/here/':
dir: /here
file1: /here/foo
file2: /here/bar
# ...

yaml3:

# works, but requires making changes to attribute values:
dir: /here
file1: foo
file2: bar
# ...

program.perl:

use strict;
use warnings;
use lib '.';
use MooseX::YAML qw (LoadFile);
use Try::Tiny;

foreach (1..3) {
  my $datafile = "yaml$_";
  print STDERR "Loading $datafile...\n";
  try {
    LoadFile ("yaml$_");
  } catch {
    print STDERR "$_";
  };
}

在目录中创建所有这些文件并从那里 运行 "perl program.perl",我得到以下输出:

Loading yaml1...
YAML::XS::Load Error: The problem:

    did not find expected alphabetic or numeric character

was found at document: 1, line: 2, column: 12
while scanning an alias at line: 2, column: 8
Loading yaml2...
Loading yaml3...

文件 'mypkg.pm' 显示了我在使用 yaml3 时必须对属性进行的更改。

mypkg.pm:

package mypkg;
use Moose;
use File::Spec;

has 'dir' => (isa => 'Str', is => 'ro');
# have to allow modifying these values in BUILD
#                                              (!!)
has [qw (file1 file2)] => (isa => 'Str', is => 'rw');

sub BUILD {
  my ($self) = @_;
  $self->file1 (File::Spec->catfile ($self->dir, $self->file1));
  $self->file2 (File::Spec->catfile ($self->dir, $self->file2));
  # ... etc.
  # everything done, I would like all further changes to the attribute 
  # values to be impossible from now on
}

__PACKAGE__->meta->make_immutable;

您可以通过以下方式写入 BUILD() 子中的 read-only 属性:

package mypkg;
use Moose;
use File::Spec;

has [qw (file1 file2)] => (isa => 'Str', is => 'ro');
has dir => (isa => 'Str', is => 'ro');

sub BUILD {
    my ($self) = @_;
    # Note that we use $self->{file1} instead of $self->file1 in order to 
    # fool Moose and write to its read-only attributes
    $self->{file1} = File::Spec->catfile($self->dir, $self->file1);
    $self->{file2}  = File::Spec->catfile($self->dir, $self->file2);
}

另见