New & Improved: MooseX::Role::BuildInstanceOf

November 9th, 2011
PerlNew and ImprovedMooseX::Role::BuildInstanceOfDBIx::Class::DeploymentHandler

New & Improved: MooseX::Role::BuildInstanceOf

New and Improved!

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?