Joel
Joel

Reputation: 3483

How to override a sub in a Moose::Role?

I'm trying to implement a Moose::Role class that behaves like an abstract class would in Java. I'd like to implement some methods in the Role, but then have the ability to override those methods in concrete classes. If I try this using the same style that works when I extend classes I get the error Cannot add an override method if a local method is already present. Here's an example:

My abstract class:

package AbstractClass;

use Moose::Role;

sub my_ac_sub {

    my $self = shift;

    print "In AbstractClass!\n";
    return;
}

1;

My concrete class:

package Class;

use Moose;

with 'AbstractClass';

override 'my_ac_sub' => sub {

    my $self = shift;

    super;
    print "In Class!\n";
    return;
};

__PACKAGE__->meta->make_immutable;
1;

And then:

use Class;

my $class = Class->new;
$class->my_ac_sub;

Am I doing something wrong? Is what I'm trying to accomplish supposed to be done a different way? Is what I'm trying to do not supposed to be done at all?

Upvotes: 7

Views: 2612

Answers (2)

Joel
Joel

Reputation: 3483

Turns out I was using it incorrectly. I opened a ticket and was shown the correct way of doing this:

package Class;

use Moose;

with 'AbstractClass';

around 'my_ac_sub' => sub {

    my $next = shift;
    my $self = shift;

    $self->$next();
    print "In Class!\n";
    return;
};

__PACKAGE__->meta->make_immutable;
1;

Making this change has the desired effect.

Upvotes: 5

Sinan Ünür
Sinan Ünür

Reputation: 118138

Some time ago, I did this by having a role that consists solely of requires statements. That forms the abstract base class. Then, you can put your default implementations in another class and inherit from that:

#!/usr/bin/env perl

use 5.014;

package AbstractClass;

use Moose::Role;

requires 'my_virtual_method_this';
requires 'my_virtual_method_that';

package DefaultImpl;

use Moose;
with 'AbstractClass';

sub my_virtual_method_this {
    say 'this';
}

sub my_virtual_method_that {
    say 'that'
}

package MyImpl;

use Moose;
extends 'DefaultImpl';
with 'AbstractClass';

override my_virtual_method_that => sub {
    super;
    say '... and the other';
};

package main;

my $x = MyImpl->new;

$x->my_virtual_method_this;
$x->my_virtual_method_that;

If you want to provide default implementations for only a few methods define in the role, remove the requires from DefaultImpl.

Output:

$ ./zpx.pl
this
that
... and the other

Upvotes: 2

Related Questions