mareoraft
mareoraft

Reputation: 3902

adding new attributes using moose

I recently learned about Moose. When I create a new attribute in a subclass, it seems to somehow override other functions that should be working...

use strict; use warnings;

################################### VEHICLE ####################################
package Vehicle;

sub new{
    my $classname = shift;
    bless { wheels=>'unknown', color=>'unknown', @_ } => $classname
}

sub wheels{
    my $vehicle = shift;
    return $$vehicle{wheels}
}

sub setWheels{
    my $vehicle = shift;
    $$vehicle{wheels} = $_[0];
}

##################################### CAR ######################################

package Car;
use Moo; extends 'Vehicle';

sub new{
    my $classname = shift;
    my $vehicle = vehicle->new( @_ );
    $vehicle->setWheels(4);
    bless $vehicle => $classname
}

has 'spoiler' => ( is=>'rw', reader=>'rspoil', writer=>'setSpoiler' );

1

The issue is that when I create a Car object, it does not have 4 wheels. It has 'unknown' wheels. If I comment out the "has 'spoiler' => ..." statement at the bottom, it works just fine.

What is causing the issue?

What is the recommended way to do what I am doing?

Upvotes: 0

Views: 193

Answers (2)

Oesor
Oesor

Reputation: 6642

Moo bakes in the extending non-Moo classes. Assuming that for your example you're working with a Vehicle class that isn't yours, but trying to write the child class in Moo, here's how to do it.

In Moo*, you don't declare a new. It handles that for you. You can mutate state by declaring a BUILD subroutine - this will get run after instantiation on the instantiated object from parent to child. Thus:

use strict; use warnings;

################################### VEHICLE ####################################
package Vehicle;

sub new{
    my $classname = shift;
    bless { wheels=>'unknown', color=>'unknown', @_ } => $classname
}

sub wheels{
    my $vehicle = shift;
    return $$vehicle{wheels}
}

sub setWheels{
    my $vehicle = shift;
    $$vehicle{wheels} = $_[0];
}

##################################### CAR ######################################

package Car;
use Moo; extends 'Vehicle';

sub BUILD {
    my $self = shift;
    if ($self->wheels eq 'unknown') {
        $self->setWheels(4);
    }
}

has 'spoiler' => ( is=>'rw', reader=>'rspoil', writer=>'setSpoiler' );

package Main;

use strict;
use warnings;
use Data::Printer;

p(Car->new(spoiler => 'big', color => 'bright red'));

my $strangecar = Car->new(spoiler => 'piddly', color => 'yellow', wheels => 3);
p($strangecar);
$strangecar->setWheels(6);
$strangecar->setSpoiler('not so piddly');
p($strangecar);

Output

Car  {
    Parents       Vehicle
    public methods (4) : BUILD, new, rspoil, setSpoiler
    private methods (0)
    internals: {
        color     "bright red",
        spoiler   "big",
        wheels    4
    }
}
Car  {
    Parents       Vehicle
    public methods (4) : BUILD, new, rspoil, setSpoiler
    private methods (0)
    internals: {
        color     "yellow",
        spoiler   "piddly",
        wheels    3
    }
}
Car  {
    Parents       Vehicle
    public methods (4) : BUILD, new, rspoil, setSpoiler
    private methods (0)
    internals: {
        color     "yellow",
        spoiler   "not so piddly",
        wheels    6
    }
}

To use Moo for both parent and child, you would do:

use strict; use warnings;

################################### VEHICLE ####################################
package Vehicle;
use Moo;

has 'wheels' => ( is=>'rw', writer=>'setWheels', default => sub { 'unknown' });
has 'color' => (is => 'rw', default => sub { 'unknown' });

##################################### CAR ######################################

package Car;
use Moo; extends 'Vehicle';

has 'spoiler' => ( is=>'rw', reader=>'rspoil', writer=>'setSpoiler' );
has '+wheels' => ( default => sub {4} );

package Main;

use strict;
use warnings;
use Data::Printer;

p(Car->new(spoiler => 'big', color => 'bright red'));

my $strangecar = Car->new(spoiler => 'piddly', color => 'yellow', wheels => 3);
p($strangecar);
$strangecar->setWheels(6);
$strangecar->setSpoiler('not so piddly');
p($strangecar);

Which yields similar output to the above code.

Upvotes: 3

tobyink
tobyink

Reputation: 13664

Firstly, if you're writing a class using Moose, you should never define your own method called new. See Moose best practices.

Secondly, if you're using Moose to extend a non-Moose class, you probably want to use MooseX::NonMoose which is able to make that all work pretty smoothly.

Upvotes: 3

Related Questions