In Moose, how do I create a rw proxy attribute for another rw attribute

70 Views Asked by At

In a Moose class I have a rw attribute referring to a position within a declared list.

has 'option_index' => (
  isa => 'Int',
  is => 'rw',
);

my $options = [qw(one two three)];

has 'option' => (
  is => 'rw',
  # ???
  # should take 'one' and set option_index to 0, ...
  # should return $options->[$self->option_index]
);

I want to create a proxy attribute, that gets the value at option_index of the list and sets option_index to the position where the argument is found.

I can create a simple sub that does that, but I would want to get into the benefit of Moose type checking and therefore declare it as an attribute.

Can this be done?

  • I don't care for the implementation, only for the Moose set up and
  • option_index has to remain
1

There are 1 best solutions below

3
Håkon Hægland On

Here is an example using a trigger function:

package Foo;
use Moose;

has 'option_index' => (
  isa => 'Int',
  is => 'rw',
  default => 0,
);

my $options = [qw(one two three)];

has 'option' => (
    is => 'rw',
    isa => 'Str',
    trigger => \&_set_option_trigger,
);


sub _set_option_trigger {
    my ( $self, $new_value, $old_value ) = @_;

    for my $idx (0..$#$options) {
        if ($options->[$idx] eq $new_value) {
            $self->option_index($idx);
            return;
        }
    }
    die "Unknown option '$new_value'";
}

no Moose;
__PACKAGE__->meta->make_immutable;

package main;
use strict;
use warnings;
use feature qw(say);

my $foo = Foo->new();
$foo->option('three');
say "Option is: ", $foo->option;
say "Option index is : ", $foo->option_index;
$foo->option('four');

Output:

Option is: three
Option index is : 2
Unknown option 'four' at ./p.pl line 30.

Edit:

To have the option attribute have a default value that is determined from the option_index attribute, you can try add lazy and a default sub like this:

has 'option' => (
    is => 'rw',
    isa => 'Str',
    lazy => 1,
    default => sub { my $self = shift; $options->[$self->option_index] },
    trigger => \&_set_option_trigger,
);