我想在调用其BUILD方法后将对象的属性更改为只读。我怎么能这样做?
(上下文:我的程序加载了这个对象,我希望在创建之后用YAML创建它,使用MooseX::YAML,然后在BUILD方法中更改其属性,以解决限制YAML描述here。更具体地说,我的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;
答案 0 :(得分:4)
这是一种可以在BUILD()
sub:
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);
}