Search code examples
perlmoose

How to auto generate a bunch of setters / getters tied to a network service in Moose?


By way of teaching myself Moose, I'm working on a Moose object that interfaces to a particular piece of hardware. Said hardware takes a number of different commands that set various properties of the hardware, all of the form PROPERTYNAME=VALUE for a setter, and PROPERTYNAME? for a getter (note that these 'setters' and 'getters' are on the network interface to the hardware). What I want to do is create an object where all of these properties of the hardware are implemented with an attribute-like interface. Since getting and setting the various properties takes the same form for all properties, is there a way to automatically generate the setters and getters from a list of those properties?

I.E.: Rather than this:

Package MyHardware;
use Moose;
has property1 => (
    'is' => 'rw',
    'reader' => 'set_property1',
    'writer' => 'get_property1',
);

has property2 => (
    'is' => 'rw',
    'reader' => 'set_property2',
    'writer' => 'get_property2',
);

# ...

has propertyN => (
    'is' => 'rw',
    'reader' => 'set_propertyN',
    'writer' => 'get_propertyN',
);

Is there something I can do like this:

Package MyHardware;
use Moose;

attributes => (
    'is' => 'rw',
    'names' => [qw/property1 property2 ... propertyN/],
    'reader' => sub {
        my $self = shift;
        my $property = shift;
        return $self->_send_command("$property?");
    },
    'writer' => sub {
        my $self = shift;
        my $property = shift;
        my $value = shift;
        return $self->_send_command("$property=$value");
    },
);

EDIT: Here's what I want to happen:

# CALLER:
my $hw = MyHardware->new();
$hw->property1('foo');
print $hw->property2 . "\n";

And "under the hood":

$hw->property1('foo');
# Becomes 
sub { return $hw->_send_command('property1=foo'); }

# And

$hw->property2();
# Becomes
sub { return $hw->_send_command('property2?'); }

Solution

  • Figured it out. I realize that I shouldn't be using attributes at all to do this. Instead, I'll dynamically generate methods using Class::MOP::Class like so:

    my $meta = Class::MOP::Class->initialize(__PACKAGE__);
    foreach my $prop (qw/property1 property2 property3/) {
        $meta->add_method(qq/set_$prop/, sub { 
                my $self = shift;
                my $value = shift;
                return $self->_send_command(qq/$prop=$value/);
            }
        );
        $meta->add_method(qq/get_$prop/, sub { 
                my $self = shift;
                return $self->_send_command(qq/$prop?/);
            }
        );
    }
    

    Doing it with calls to has() would have effectively put the object state in two places - on the hardware and in the instance - and I only want it in one.