Changes in MooX::Role::Parameterized

What is it good for?

If you’ve never worked with MooX::Role::Parameterized or MooseX::Role::Parameterized, you might wonder what is a parameterized role at all?

Roles are used when you need to share behaviour among several classes that don’t have to be related by inheritance. Normally, a role just adds a bunch of methods to the class that consumes it (there’s more, you can for example specify which other methods the role expects to already exist).

A parameterized role makes it possible to provide parameters for the consumed role. This way, you can adjust the behaviour for each consuming class.

The old syntax

The standard syntax to apply a role to a class before version 0.100 of the module was to use the apply class method:

# My/Role.pm
package My::Role;
use Moo::Role;
use MooX::Role::Parameterized;

role {
    my ($params, $mop) = @_;
    $mop->has($params->{name} => is => $params->{is});
}
# My/Obj.pm
package My::Obj;
use Moo;
use My::Role;

'My::Role'->apply({
    name => 'size',
    is   => 'ro',
});

If we now created an object $o using my $o = 'My::Obj'->new(size => 2), we could get the value of the attribute size using the $o->size getter: the role created a new read-only attribute size for us.

The old experimental syntax

What I didn’t like about applying a role to a class the old standard way was it wasn’t declarative. You could easily overlook it as a block of code happening at runtime, while the meaning of the code was This is how a role is consumed. Therefore, I used the alternative experimental syntax:

package My::Obj;
use Moo;
use MooX::Role::Parameterized::With 'My::Role' => {
    name => 'size',
    is   => 'ro',
};

It's part of a use clause, so it’s clear that it’s happening at compile time.

The new syntax

I promoted one of my side-jobs to a full-time job recently. They gave me a new computer where I had to install all my code base to start working on it 8 hours a day instead of a couple a month.

Imagine my surprise when the code stopped with an error:

Can't locate object method "size" via package "My::Obj" at ./run.pl line 37.

Line 37 was where I called $o->size!

When installing the dependencies for my code, the most recent version of MooX::Role::Parameterized was installed from CPAN (0.501). The experimental syntax is no longer documented and as I found out, doesn’t work anymore.

The old non-experimental syntax still works, but there’s a new syntax, too. It uses the with keyword that looks like the one that can be used to consume a Moo::Role, but if we first use MooX::Role::Parameterized::With, it can also accept parameters for the role application.

package My::Obj;
use Moo;
use MooX::Role::Parameterized::With 0.501;

with 'My::Role' => {
    name => 'size',
    is   => 'ro',
};

Moreover, we should change the definition of the role, too. Parameters should be predeclared using the parameter keyword (similarly to MooseX::Role::Parameterized), and they can be then accessed via getters instead of peeking inside a parameter hash reference.

package My::Role;
use Moo::Role;
use MooX::Role::Parameterized 0.501;

parameter name => (is => 'ro');
parameter is   => (is => 'ro');

role {
    my ($params, $mop) = @_;
    $mop->has($params->name => is => $params->is);
}

Leave a comment

About E. Choroba

user-pic I blog about Perl.