【问题标题】:How to set ro attributes during build?如何在构建过程中设置 ro 属性?
【发布时间】:2017-02-04 19:05:15
【问题描述】:

我正在编写一个脚本来帮助我精通 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

要解决此错误,我可以创建属性 rw 或对 dirsfiles 属性使用 builder 方法。前一种方案不可取,后一种方案需要重复代码(例如,目录需要打开两次),因此也不可取。

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

【问题讨论】:

  • 我想知道StevensPerlTools 做了什么。 :)

标签: perl moose


【解决方案1】:

您可以将a writer 分配给您的只读属性,并在您的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 相同,但现在作者与阅读者不是同一个访问者。 _ 表示它是内部的,因此它比仅使用 rw 更不受欢迎。请记住,无论如何,您无法真正保护 Perl 中的任何内容。如果您的用户想要了解内部信息,他们会这样做。

【讨论】:

  • 好的,非常感谢。这似乎是实现这一目标的最佳方式。
  • @simbabque:所以每个ro属性必须被赋予writer,否则它将永远保持未初始化状态?另外,请解释一下你上面的笑话,因为我今晚似乎特别愚蠢。
  • @Borodin 在编辑之前曾经有一个use StevesPerlTools; 行。
  • @Borodin 通常通过构造函数初始化ro 属性。 Foo->new( bar => 123 ); 将设置它。这里的重点是在BUILD 期间构造了一些属性,这发生在构造对象之后(BUILDARGS 之前)。因此,如果其中一些没有传入(故意、意外或因为您删除了init_arg)并且您仍想设置它们,您可以为属性添加writer_ 表示其他人应该远离。查看笑话问题的编辑历史。 :)
  • @Borodin ro 属性可以通过构造函数 (Foo->new(bar => 42))、使用 default 值或 builder 方法来初始化。如果这些都不适用,则属性将为remain unset。如果没有私人writer,是的,它将永远保持这种状态。
【解决方案2】:

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

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

【讨论】:

  • 不要那样做。如果你想学习 Moose,不要乱搞内部结构。
猜你喜欢
  • 2016-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多