I'm re-posting this to the Moose list. Hopefully some of the Moose experts can offer their insight.
The following is about cleaning-up attributes, before allowing them to be set, e.g. HTML encoding or removing invalid characters from a string input. The question is which Moose approach is best to accomplish this: before, after, around, trigger, BUILD, or coerce. >> At a higher level, I wasn't sure when it's more appropriate to use >> before/after/around vs. triggers. >> E.g. before/after/around are not called for attributes set in the >> constructor. In that case, trigger does work, though. However, for doing >> cleanup of attribute values before allowing them to be set (e.g. HTML >> encoding a string), trigger does not seem to get access to @_ So, if I use >> trigger to recall $self->myattribute(), I'll have infinite recursion. In >> the >> end, I found coercion to be the best (only?) way. This kind of >> attribute-cleanup wrapper might be something for the cookbook. > > I'm lost here. Triggers _are_ passed the value of the attribute set in the > constructor, or at least that's what the docs say ;) > > Triggers don't have access to _all_ of the arguments passed to the > constructor, though. For that, use BUILD or BUILDARGS. > I admit that trigger does get @_, like you said, but if I call the accessor from the trigger I get infinite recursion: package Cleaner; use Moose; has 'myattr' => ( is => 'rw', isa => 'Str', # trigger => \&triggercleaner, ); sub triggercleaner { my ($self, $arg) = @_; $arg =~ s/\s//g; $self->myattr($arg); # <--- infinite recursion } 'Around' would be the ideal solution: around 'myattr' => \&aroundcleaner; sub aroundcleaner { my ($orig, $self, $val) = @_; return $self->$orig() unless $val; $val =~ s/\s//g; return $self->$orig($val); } Except that these two bits of code produce different results, depending on where myattr is set. So I cannot enforce that the value is always clean. my $x = new Cleaner(myattr=>"around not called from constructor"); print "Spaces still there:", $x->myattr, "\n"; # But then calling this does call aroundcleaner() $x->myattr($x->myattr); print "Spaces removed ", $x->myattr, "\n"; So, I need a BUILD, because the constructor is a special case: sub BUILD { my ($self) = @_; # either this: aroundcleaner(\&myattr, $self, $self->myattr); # or this will work: $self->myattr($self->myattr); } But that needs to be done for any further attributes that need cleaning. If this were classic OO, it would just be: sub myattr { my ($self, $val) = @_; return $self->{'myattr'} unless defined $val; $val =~ s/\s//g; return $self->{'myattr'} = $val; } Which is why I'm slightly perplexed, because this seems to be the one use case where Moose requires more code than classic OO. I could just break the interface and do: $self->{'myattr'} = $val inside the trigger, but none of us wants that. I finally settled on coercion to do something like: use Moose::Util::TypeConstraints; subtype 'CleanStr' => as 'Str'; coerce 'CleanStr' => from 'Str' => via { $_ =~ s/\s//g; $_ }; has 'myattr' => ( is => 'rw', isa => 'CleanStr', coerce => 1, ); But this also seems like a round-about solution, since I all I want is to insert somewhere: s/\s//g Is someone still following that might also have a good solution for preventing unwanted values from getting into an attribute? Cheers, Chad