New & Improved: MooseX::Role::BuildInstanceOf

Posted in: Technical Track

Okay, so MooseX::Role::BuildInstanceOf is not one of my modules. And while I submited the patch, the feature itself was born out of fREW‘s DBIx::Class::DeploymentHandler. So, in all this, my role was at best to be the pollinator agent between two beautiful flowers. But, hey, it’s not like a little bit of noise is going to hurt any of the involved parties, sooo… let’s see what the buzz’s about.

Insanely Quick Intro to MooseX::Role::BuildInstanceOf

MooseX::Role::BuildInstanceOf is a Moose module that aims at reducing the amount of boilerplate code needed for dealing with sub-objects. It’s a little meta-scary at first, but once one understanding settles in, it’s humongously handy. Without going into the details, with that module, you can turn that chunk of code:

package MyShip;
use Moose;
has engine => (
    is => 'ro',
    lazy_build => 1,
);
has engine_args => (
    isa => 'ArrayRef',
    is => 'ro',
);
sub build_engine {
    my $self = shift;
    return MyShip::Engine->new(
        @{ $self->engine_args || [ fuel => 100 ] },
        max_speed => 10,
    );
}
1;

into

package MyShip;
use Moose;
with 'MooseX::Role::BuildInstanceOf' => {
    target => '::Engine',
    args => [ fuel => 100 ],
    fixed_args => [ max_speed => 10 ],
};
1;

Both versions will do just what you think it will do when you write

# damned Ferengies never fill up the tank
my $warbird = MyShip->new( engine_args => [ fuel => 20 ] );

There is a lot more to MooseX::Role::BuildInstanceOf than that, but we can agree that it’s already pretty sweet.

So, What’s New?

In the last few days, I’ve been deep-diving into DBIx::Class::DeploymentHandler. Within that distribution, fREW kinda rolled out a module similar to MooseX::Role::BuildInstanceOf: DBIx::Class::DeploymentHandler::WithApplicatorDumple. It is acknowledged in the source of the module that its implementation is a little on the ghetto side, and should probably be refactored with Role::subsystem or, ah AH!, MooseX::Role::BuildInstanceOf.

Me, eternal meddler, couldn’t resist looking into that, and quickly saw that WithApplicatorDumple had something that MX::R::BIO was missing: the ability to automatically push down to the sub-objects some attributes of the main object. Very handy, that. So I cracked my knuckles, went to work… and by now the code

package MyShip;
use Moose;
has captain => ( is => 'ro' );
has intercom => (
    is => 'ro',
    isa => 'Log::Dispatchouli',
    default => sub {
        ...
    },
);
has coordinates => (
    is => 'ro',
    isa => 'MyShip::Coord',
    default => sub {
        MyShip::Coord->new( 0, 0 );
    },
);
has engine => (
    is => 'ro',
    lazy_build => 1,
);
has engine_args => (
    isa => 'ArrayRef',
    is => 'ro',
);
sub build_engine {
    my $self = shift;
    return MyShip::Engine->new(
        @{ $self->engine_args || [ fuel => 100 ] },
        max_speed => 10,
        coordinates => $self->coordinates,
        bridge_monkey => $self->captain,
        intercom => $self->intercom->proxy({ proxy_prefix => 'Engine' });
    );
}
1;

can all be replaced by

package MyShip;
use Moose;
has captain => ( is => 'ro' );
has intercom => (
    is => 'ro',
    isa => 'Log::Dispatchouli',
    default => sub {
        ...
    },
);
has coordinates => (
    is => 'ro',
    isa => 'MyShip::Coord',
    default => sub {
        MyShip::Coord->new( 0, 0 );
    },
);
with 'MooseX::Role::BuildInstanceOf' => {
    target => '::Engine',
    args => [ fuel => 100 ],
    fixed_args => [ max_speed => 10 ],
    inherited_args => [
        'coordinates',
        {
            bridge_monkey => 'captain',
            intercom => sub {
                my $self = shift;
                $self->intercom->proxy({ proxy_prefix => 'Engine' });
            },
        }
    ],
};
1;

Nifty, isn’t?

email
Want to talk with an expert? Schedule a call with our team to get the conversation started.

No comments

Leave a Reply

Your email address will not be published. Required fields are marked *