views:

469

answers:

3

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
        }

}
+13  A: 

$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
            )
        );
friedo
+5  A: 

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.

ysth
Don't use that module, it's broken by design.
Dave Rolsky
@Dave Rolsky: in the way I noted, or in some other way?
ysth
+6  A: 

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').

perigrin
Thanks for coming back to this question and your explanation!