Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Moose, how do I modify an attribute any time it is set?

Tags:

If you have an attribute that needs to be modified any time it is set, is there a slick way of doing this short of writing the accessor yourself and mucking around directly with the content of $self, as done in this example?

package Foo;
use Moose;

has 'bar' => (
    isa => 'Str',
    reader => 'get_bar',
);

sub set_bar {
    my ($self, $bar) = @_;
    $self->{bar} = "modified: $bar";
}

I considered trigger, but it seemed to require the same approach.

Is working directly with the hash reference in $self considered bad practice in Moose, or am I worrying about a non-issue?

like image 212
FMc Avatar asked Sep 12 '09 17:09

FMc


2 Answers

You can use the method modifier 'around'. Something like this:

has 'bar' => (
    isa    => 'Str',
    reader => 'get_bar',
    writer => 'set_bar'
);

around 'set_bar' => sub {
    my ($next, $self, $bar) = @_;
    $self->$next( "Modified: $bar" );
};

And yes, working directly with the hash values is considered bad practice.

Also, please don't assume that the option I presented is necessarily the right one. Using subtypes and coercion is going to be the right solution most of the time - if you think about your parameter in terms of a type that can possibly be reused throughout your application will lead to much better design that the kind of arbitrary modifications that can be done using 'around'. See answer from @daotoad.

like image 170
aaa90210 Avatar answered Oct 15 '22 21:10

aaa90210


I'm not sure what kind of modification you need, but you might be able to achieve what you need by using type coercion:

package Foo;
use Moose;

use Moose::Util::TypeConstraints;

subtype 'ModStr' 
    => as 'Str'
    => where { /^modified: /};

coerce 'ModStr'
    => from 'Str'
    => via { "modified: $_" };

has 'bar' => ( 
    isa => 'ModStr', 
    is  => 'rw', 
    coerce => 1,
);

If you use this approach, not all values will be modified. Anything that passes validation as a ModStr will be used directly:

my $f = Foo->new();
$f->bar('modified: bar');  # Set without modification

This weakness could be OK or it could make this approach unusable. In the right circumstances, it might even be an advantage.

like image 41
daotoad Avatar answered Oct 15 '22 22:10

daotoad