How do I handle optional parameters in Moose?

2019-06-16 16:56发布

I'm currently starting with Perl OOP using the "Moose" package.

The compiler complains that it "Can't modify non-lvalue subroutine call at Parser.pm line 16."

I don't quite understand why I can't just assign a new object. I guess there is a better or more valid way to do optional parameters with Moose?

    #!/usr/bin/perl -w

package Parser;

use Moose;

require URLSpan;

require WWW::Mechanize;

has 'urlspan' => (is => 'rw', isa => 'URLSpan', required => 1);
has 'mech' => (is => 'rw', isa => 'WWW::Mechanize');

sub BUILD {
    my $self = shift;
    if(!$self->mech) {
        warn("no Mech set for " . $self->urlspan->name);
        $self->mech = WWW::Mechanize->new(agent => 'Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.4',
                                         stack_depth => 1
                                         ); #line 16
        }

}

3条回答
混吃等死
2楼-- · 2019-06-16 17:28

While Perl has provided the ability to use attributes the way you are trying to for a good many years (via what are called lvalue subs), it's not something that was in the first OO Perl releases, and people pretty much learned to do without it. Especially since implementing validation is a little tricky (and inefficient).

You could use MooseX::Meta::Attribute::Lvalue, but (according to the doc) at the cost of not having type checking on some attributes.

I'd recommend just sticking to the $self->attribute( "value" ) style.

查看更多
戒情不戒烟
3楼-- · 2019-06-16 17:42

$self->mech is a method call; you can't really treat it like a field in a C struct. If you want to set it, you need to pass the new object to it.

        $self->mech( 
            WWW::Mechanize->new(
                agent => 'xyz',
                stack_depth => 1
            )
        );
查看更多
霸刀☆藐视天下
4楼-- · 2019-06-16 17:46

Probably the preferred Moose way of doing this is to set lazy_build on the attribute:

has 'mech' => (is => 'rw', isa => 'WWW::Mechanize', lazy_build => 1);
sub _build_mech {
     warn("no Mech set for " . $self->urlspan->name);
     WWW::Mechanize->new(
           agent => 'Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.6)'.
                    ' Gecko/2009011913 Firefox/3.0.4',
           stack_depth => 1
     );
}

This will allow the attribute 'mech' to get populated the first time it is called, unless otherwise set by the constructor or by the accessor (since it's still 'rw').

查看更多
登录 后发表回答