chrestomanci has asked for the wisdom of the Perl Monks concerning the following question:

Wise Brothers, I seek you advice.

I am working on a Moose based class, that is to have a set of mutually exclusive boolean attributes. (Think radio buttons in a web form).

The attributes represent status, which can be 'success', 'error', 'pending' or undefined.

I have currently implemented this in Moose with one attribute that holds status, and a number of old style access methods to get and set the value:

package MyCompay::Result; use Moose; has '_status' => ( is => 'rw', isa => 'Str', ); sub success { my($self, $arg) = @_; if( defined $arg ) { $self->_status( (!!$arg) ? 'success' : undef ); } return ( 'success' eq $self->_status() ); } sub error { my($self, $arg) = @_; if( defined $arg ) { $self->_status( (!!$arg) ? 'error' : undef ); } return ( 'error' eq $self->_status() ); } sub pending { my($self, $arg) = @_; if( defined $arg ) { $self->_status( (!!$arg) ? 'pending' : undef ); } return ( 'pending' eq $self->_status() ); } 1;

The thing is, the code above is quite long and repetitive, which is just the sort of thing that Moose is designed to avoid. There is also nothing on the _status attribute to prevent something accessing it directly and setting it to an invalid value

Is there a more efficient way to achieve the same result? Can I declare virtual fields in Moose, and provide reader and writer methods, that just test or change the value of _status? Can I put guard on the _status field to enforce it's value so that it acts as an enum?

Replies are listed 'Best First'.
Re: Moose: mutualy exclusive bolean attributes.
by Arunbear (Prior) on Dec 23, 2010 at 15:44 UTC
    This is perhaps a more Moose-ish approach:
    #!/usr/bin/perl package Foo; use Moose; use Moose::Util::TypeConstraints; enum 'Status' => qw(success error pending); has 'status' => (is => 'rw', isa => 'Status|Undef'); package main; use strict; use warnings; my $o = Foo->new; foreach (undef, 'success', 'bar', 'error') { $o->status($_); printf "status: %s\n", $o->status; }
    See Moose::Util::TypeConstraints

      It's worth noting that your method doesn't prevent individual accessors.

      has 'status' => ( is => 'rw', isa => 'Status|Undef', handles => { success => sub { ( $_[0]->status() // '' ) eq 'success' }, error => sub { ( $_[0]->status() // '' ) eq 'error' }, pending => sub { ( $_[0]->status() // '' ) eq 'pending' }, }, );

        Thank you. ikegami

        That looks like it handles the requirement for a getter for the success|error|pending attributes. I guess if I want a setter as well, I can expand those subs to larger ones that read the value of $_[1] as well.

        Combined with Arunbear's suggestion to use Moose::Util::TypeConstraints to get an enum on the underlying status field, I think I am three quarters of the way to what I want to achieve.

Re: Moose: mutualy exclusive bolean attributes.
by ikegami (Patriarch) on Dec 23, 2010 at 16:00 UTC

    There is also nothing on the _status attribute to prevent something accessing it directly and setting it to an invalid value

    That shouldn't be a consideration because you'll never be able to prevent someone from going around the normal process. For example,

    $obj->{'_status'} = 'junk';