Taming the Moose: Method modifiers instead of overrides in object-oriented Perl
Tags: perl, oop, moose, traits
Published: 2021-08-31 15:56:37

---
title: Taming the Moose: Method modifiers instead of overrides in object-oriented Perl
published: true
date: 2021-08-31 14:00:00 UTC
tags: Perl,OOP,Moose,traits
canonical_url: https://phoenixtrap.com/2021/08/31/taming-the-moose-method-modifiers-instead-of-overrides-in-object-oriented-perl/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wuadtxg6oevzmwkqmhy3.jpeg
series: Taming the Moose
---

Last month [I wrote about using Moose’s `override` function](https://phoenixtrap.com/2021/07/13/taming-the-moose-picking-the-best-way-to-subclass-perl-methods/) to, well, override a superclass’s method. [Chris Prather](https://chris.prather.org/) on the [#moose IRC channel](https://kiwiirc.com/nextclient/#irc://irc.perl.org/moose) suggested soon after that the [`around` method modifier](https://metacpan.org/dist/Moose/view/lib/Moose/Manual/MethodModifiers.pod#Around-modifiers) (or its little sisters [`before` and `after`](https://metacpan.org/dist/Moose/view/lib/Moose/Manual/MethodModifiers.pod#Before-and-after-Modifiers)) might be a better choice if you’re also calling the original method inside. He noted that “at a minimum `override` only works if you’re subclassing, `around` will apply to composed methods too.”

His point was that when you decide to compose [roles](https://metacpan.org/pod/Moose::Manual::Roles) (also know as [traits](https://en.wikipedia.org/wiki/Trait_(computer_programming))) instead of or in addition to more traditional inheritance, `override` simply doesn’t work: only a method modifier will do. (And as [Graham Knop](https://haarg.org/) and [Karen Etheridge](https://github.com/karenetheridge) later remarked on IRC, `override` isn’t even an option if you’re using [Moo](https://metacpan.org/pod/Moo) as an alternative to Moose.)

Modifying a role’s method with `around` might look like this:

```perl
#!/usr/bin/env perl

use v5.12; # for strict and say
use warnings;

package Local::Role::Hungry;
use Moose::Role;
requires 'name';

sub wants_food {
    my $self = shift;
    say $self->name, ' is hungry!';
    return;
}

package Local::GuineaPig;
use Moose;
has name => (is => 'ro');
with 'Local::Role::Hungry';

around wants_food => sub {
    my ($orig, $self) = splice @_, 0, 2;
    say $self->name, ' runs to the front of the cage!';
    $self->$orig(@_);
    say 'Wheek!';
    return;
};

package Local::Dog;
use Moose;
has name => (is => 'ro');
with 'Local::Role::Hungry';

around wants_food => sub {
    my ($orig, $self) = splice @_, 0, 2;
    say $self->name, ' runs to the kitchen!';
    $self->$orig(@_);
    say 'Woof!';
    return;
};

before wants_food => sub {
    my $self = shift;
    say $self->name, ' is jumping!';
};

package main;
my $dog = Local::Dog->new(name => 'Seamus');
my @pigs = map { Local::GuineaPig->new(name => $_) }
  qw<Cocoa Ginger Pepper>;

for my $animal ($dog, @pigs) {
    $animal->wants_food();
}
```

Running the above yields:

```
Seamus runs to the kitchen!
Seamus is hungry!
Woof!
Cocoa runs to the front of the cage!
Cocoa is hungry!
Wheek!
Ginger runs to the front of the cage!
Ginger is hungry!
Wheek!
Pepper runs to the front of the cage!
Pepper is hungry!
Wheek!
```

It’s a little more involved than overriding a [`sub`](https://perldoc.perl.org/functions/sub), since method modifiers are passed both the consumed role’s original method (`$orig` above) and the instance (`$self` above) as parameters. It has the same effect, though, and you can call the original method by saying `$self->$orig( parameters... )`. That’s why I used the [`splice` function](https://perldoc.perl.org/functions/splice) so I could pass any remaining parameters as the original [`@_` array](https://perldoc.perl.org/variables/@_).

If all you want to do is have something happen either before or after the original method, just use `before` or `after`:

```perl
before wants_food => sub {
    my $self = shift;
    say $self->name, ' is jumping!';
};
```

Note that there’s no return value in a `before` or `after` modifier, as those are handled by the original method.

Modifiers aren’t limited to consuming classes; they can be in roles and modify their consumers’ methods. They also have a couple of other tricks:

- You can pass an [array reference](https://perldoc.perl.org/perlref#Square-Brackets) to modify multiple methods at once.
- You can use the contents of a variable to specify the modified method name, and use that same variable in the modifier itself.
- You can use a [regular expression](https://perldoc.perl.org/perlre) to select methods. (Beware if you’re using Moo that its [Class::Method::Modifiers](https://metacpan.org/pod/Class::Method::Modifiers) module doesn’t support this.)

Putting these together gives you constructs like these:

```perl
after qw<foo bar baz> => sub {
    say 'Something got called';
};

for my $method_name (qw<foo bar baz>) {
    before $method_name => sub {
        say "Calling $method_name...";
    };
}

before qr/^request_/ => sub {
    my ($self, @args) = @_;
    $self->is_valid(@args) or die 'Invalid arguments';
};
```

Moose comes with great introductory manuals for [method modifiers](https://metacpan.org/dist/Moose/view/lib/Moose/Manual/MethodModifiers.pod) and [roles](https://metacpan.org/dist/Moose/view/lib/Moose/Manual/Roles.pod), so be sure to check those out. There’s a lot more to them and a blog can only cover so much.