Matthew King
and 1 contributors

NAME

MooseX::AccessorsOnly - React when users root around inside your objects

SYNOPSIS

  package Foo;

  use Moose;
  use MooseX::AccessorsOnly;

  sub BUILD {
    my $self = shift;
    my %saved = %$self;
    tie %$self, "MooseX::AccessorsOnly";
    %$self = %saved;
  }

DESCRIPTION

Call a function every time the elements of the hash which underlies a regular Moose object are accessed directly.

BUGS

There should be no need to write the BUILD sub's boilerplate.
It is almost certainly too slow.
Not compatible with Moo.
Edge cases have undoubtedly been missed.
There are no tests.

USAGE

The simplest way to use this module is to copy the BUILD sub from the SYNOPSIS into your class.

If you can be sure that none of your attributes have a default value (lazy attributes with a builder should be fine) then there is no need to save and restore its contents; only the tie line is necessary:

  sub BUILD { tie %{$_[0]}, "MooseX::AccessorsOnly" }

ADVANCED USAGE

You may optionally pass a callback as the third option to tie() will be called instead of emitting the regular warning. It will be called with three argument: The package and line number from which the errant access took place, the access type being attempted and the key that is being accessed, or undef:

  $cb->($who, $how, $what);

The default callback is simply:

  sub {
    my ($who, $how, $what) = @_;
    carp "DIRECT ACCESS from $who: $how " . $what || 'NOKEY'
  };

Example:

  sub BUILD {
    my $self = shift;
    tie %$self, 'MooseX::AccessorsOnly',
      sub { $self->log("DEPRECATED API USED", @_) };
  }

As a shortcut, you can pass any of the strings carp, croak, confess, cluck, die or warn as tie's 3rd argument and the default callback will be modified to use that function to do the reporting.

WHY

I'm shepherding a terrible codebase and attempting to drag it into at least the 20th if not the 21st century. We've all been there. A significant part of that work involved converting our ancient modules to use Moo but of course out of the 300,000 lines I'm bound to miss some places where the code reaches into the hash directly.

This module exists so that I have partially-converted code deploy4ed to production and have it report all the places I missed. I can then trawl through the logs and make the necessary repairs. After enough time passes without any messages being logged I can remove this module and continue to refactor the code safely.

(And convert the module back to using Moo)

Moo

Unfortunately because of the magic Moo uses to eke more speed out of its accessors I could not get this technique to work with it. I'm sure it's possible but I have work to do so this will have to suffice.

Conveniently, thanks to the hard work of Matt Trout and others, Moo is entirely compatible with Moose. Provided you're disciplined not to use Moose-specific features in your class, you can simply take the speed hit during the conversion period and then switch back to Moo when you've finished.

If anybody can come up with a way to write MooX::AccessorsOnly, I will gladly include it in this package.

HISTORY

MooseX::AccessorsOnly 1

First Moose-only implementation.

SEE ALSO

Moo

Moose

perltie

AUTHOR

Matthew King (cpan:CHOHAG) <chohag@jtan.com>

COPYRIGHT

Copyright © 2017 Matthew King

LICENSE

This library is free software and may be distributed under the terms of the Do What the Fuck You Want To Public License version 2 or, at your option, any version of any license you choose.