Stevan,
Agreed. Thanks for clearing that up.

PS: Chris, when I call set_value from a trigger, it "triggers" the
trigger again, resulting in recursion.

On Tue, Feb 17, 2009 at 03:58, Stevan Little
<stevan.lit...@iinteractive.com> wrote:
> Chad,
>
> Trigger is called by the accessor, which is why the infinite recursion. I
> wouldn't recommend trigger for this kind of thing, personally I tend to use
> trigger for things like syncing two different attributes and not to clean
> data.
>
> Based on what I am seeing here, I think coercion is your best approach
> (which is the conclusion you came to as well) and for several reasons.
>
> 1) It is by far the most re-usable solution since any other attribute that
> needs a clean string can just use the type
> 2) It is also the most extendable since you can simply subtype the CleanStr
> type if you want to have a clean string that is also HTML encoded
> 3) It is *exactly* the problem that the type-constraint/type-coercion system
> is meant to solve, the prevention of bad data and simple ways to convert
> that bad data into good data.
>
> - Stevan
>
> On Feb 16, 2009, at 1:09 PM, Chad Davis wrote:
>>
>> 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
>
>

Reply via email to