Search code examples
perlmoose

Watch for changes of a Moose attribute


Is there any way in Moose of triggering a callback when the content of an attribute is changed via reference instead of setting its value via mutator?

Let's assume the following code:

has _changed  => ( is => 'rw' , isa=>'Bool' ) ;
has attribute => ( 
    is=>'rw', isa=>'Maybe[HashRef]', 
    default => sub { { a => 1 , b => 2 } },     
    trigger => sub { shift->_changed(1) } 
) ;

the trigger works as expected setting the attribute value through mutator:

$self->attribute({ a => 2 , b => 2 }) ; # OK

but setting directly a value through its key then the trigger doesn't fires (of course):

$self->attribute->{a} = 3 ; # KO

I discarded the idea of creating (and comparing) a digest of serialized attribute's content, because it can be a very huge hashref with several nesting levels, and making a digest at every attribute access can produce a performance issue.

A tied hashref (as attribute value) could be a possible solution? Any idea or suggestion is very appreciated.

NOTE: The structure of contained hashref is not known (I'm writing an ORM class, so the struct can vary depending on documents stored on NOSQL db side).


Solution

  • Once you change the hash ref directly rather than using accessor methods, Moose is no longer involved. Having your attributes return a reference to a tied hash would be the only strategy to make changes to the hash observable, yet this is not a particularly attractive solution. Tied variables are rare and likely to trigger bugs in some code. They are comparatively difficult to implement. And they imply a performance overhead for every hash access.

    Strongly consider whether you can change your design to avoid exposing the internal hash. E.g. have a getter that only returns a (shallow) copy of the hash, and a setter for individual elements in the hash. You may be able to autogenerate some of these accessors using the handles and traits mechanisms, e.g. see Moose::Meta::Attribute::Native::Trait::Hash.