http://qs321.pair.com?node_id=1211958


in reply to Moose triggers & method modifiers

This is the first time I see a modifier modifying a method defined in the same class. I've always used modifiers with inheritance or roles, modifying a parent or required method. Nevertheless, you can't wrap a trigger coming from a parent class, either.

That's because the trigger is defined as a code reference, not as a sub name. It means you can modify e.g. a predicate, because it's defined by name, because modifiers redefine a method of the given name.

I see two ways how to wrap a trigger in a more Moosey way: either define a new trigger in the overridden attribute, or define the trigger as a sub that calls a method that you later modify (predicate overriding shown in the example, too):

#!/usr/bin/perl use warnings; use strict; use feature qw{ say }; { package My; use Moose; has a1 => ( is => 'rw', trigger => \&a1_changed, predicate => 'has_attr' ); has a2 => ( is => 'rw', trigger => sub { $_[0]->a2_changed(@_[ 1 .. $#_ ]) } ) +; sub a1_changed { print ref shift, ": a1 changed.\n"; } sub a2_changed { print ref shift, ": a2 changed.\n"; } __PACKAGE__->meta->make_immutable; no Moose; } { package My::Child; use Moose; extends 'My'; has '+a1' => ( trigger => \&a1_changed ); sub a1_changed { print "Trigger 1 wrapped.\n"; $_[0]->SUPER::a1_changed; }; around a2_changed => sub { print "Trigger 2 wrapped\n"; $_[0]->(@_[ 1 .. $#_ ]); }; around has_attr => sub { print "Predicate wrapped\n"; }; __PACKAGE__->meta->make_immutable; no Moose; } my $o = 'My'->new; my $ch = 'My::Child'->new; $o->a1(2); $ch->a1(2); say $o->has_attr; say $ch->has_attr; $o->a2(3); $ch->a2(3);

($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

Replies are listed 'Best First'.
Re^2: Moose triggers & method modifiers
by bobf (Monsignor) on Mar 29, 2018 at 22:17 UTC

    Thanks very much for the excellent explanation and examples! Between the Moose IRC channel and my own experimentation I cobbled together similar solutions, but the one you provided is much more clear.

    In my real code, I use modifiers in child classes and roles (as you suggested), but when I was working on the example for this post I thought it might remove one more variable if I put everything in the same class. A weird design, I admit, but it demonstrated the issue.

    Initially, I implemented your first method (extending the attribute in the child class). It seemed to work ok, but this part of the docs (Moose::Manual::Attributes) made me worry about fragility and future breakage:

    Attribute Inheritance and Method Modifiers

    When an inherited attribute is defined, that creates an entirely new set of accessors for the attribute (reader, writer, predicate, etc.). This is necessary because these may be what was changed when inheriting the attribute.

    As a consequence, any method modifiers defined on the attribute's accessors in an ancestor class will effectively be ignored, because the new accessors live in the child class and do not see the modifiers from the parent class.

    In particular, I didn't want to remember which changes in the parent/child class would be ineffectual in a subset of classes that extend the attribute in question.

    As a result, I ended up using option 2. I like having "x_changed" as a named method, so I changed the trigger definition to be a coderef to an anonymous sub that calls "x_changed". Now I can safely modify that method without having to think too hard.

    Thanks again. ++