Search code examples
perltriggersmoose

Moose trigger caller


Is there any way of knowing the trigger caller attribute in Moose ?

For example, taking the example from Moose::Manual::Attributes:

has 'size' => (
  is      => 'rw',
  trigger => \&_size_set,
);

sub _size_set {
  my ( $self, $size, $old_size ) = @_;

  my $msg = $self->name;

  if ( @_ > 2 ) {
      $msg .= " - old size was $old_size";
  }

  $msg .= " - size is now $size";
  warn $msg;
}

Is it possible in _set_size to know that the attribute size called it, without needing to specify the name of the caller attribute explicitly?

EDIT: updated per comment.


Solution

  • Here's what @RsrchBoy refers to as the "proper way"...

    use v5.14;
    use strict;
    use warnings;
    
    BEGIN {
        package MooseX::WhatTheTrig::Trait::Attribute
        {
            use Moose::Role;
            use Scope::Guard qw(guard);
            after _process_trigger_option => sub
            {
                my $class = shift;
                my ($name, $opts) = @_;
                return unless exists $opts->{trigger};
    
                my $orig = delete $opts->{trigger};
                $opts->{trigger} = sub
                {
                    my $self = shift;
                    my $guard = guard {
                        $self->meta->_set_triggered_attribute(undef);
                    };
                    $self->meta->_set_triggered_attribute($name);
                    $self->$orig(@_);
                };
            }
        }
    
        package MooseX::WhatTheTrig::Trait::Class
        {
            use Moose::Role;
            has triggered_attribute => (
                is     => 'ro',
                writer => '_set_triggered_attribute',
            );
        }
    }
    
    
    package Example
    {
        use Moose -traits => ['MooseX::WhatTheTrig::Trait::Class'];
    
        has [qw(foo bar)] => (
            traits   => ['MooseX::WhatTheTrig::Trait::Attribute'],
            is       => 'rw',
            trigger  => sub {
                my ($self, $new, $old) = @_;
                $_ //= 'undef' for $old, $new;
                my $attr = $self->meta->triggered_attribute;
                say "Changed $attr for $self from $old to $new!";
            }
        );
    }
    
    my $obj = Example->new(foo => 1, bar => 2);
    $obj->foo(3);
    $obj->bar(4);
    

    You'll notice that the "foo" and "bar" attributes share a trigger, but that the trigger is able to differentiate between the two attributes.

    Moose::Exporter has some sugar for making this a little less ugly. I might have a play at turning this into a CPAN module some time.