Changing writer prefix when (is => “rwp”)

400 Views Asked by At

If I want to change write protected attribute ie.

use Moops;

class foo {
  has attr => (is => "rwp");
}

one have to use _set_attr().

Is it possible to change that to _attr() without using explicit writer?

Tried use MooseX::::AttributeShortcuts -writer_prefix => 'prefix'; but it did not work.

1

There are 1 best solutions below

5
On BEST ANSWER

No, you need to do that yourself by setting the writer.

TLDR: At the bottom is a monkey-patch to do it anyway.


The Moops docs say (emphasys mine):

Moops uses MooseX::MungeHas in your classes so that the has keyword supports some Moo-specific features, even when you're using Moose or Mouse. Specifically, it supports is => 'rwp', is => 'lazy', builder => 1, clearer => 1, predicate => 1, and trigger => 1.

Now let's go look at Moo. In the has section of the doc, it says (emphasys mine):

rwp stands for "read-write protected" and generates a reader like ro, but also sets writer to _set_${attribute_name} for attributes that are designed to be written from inside of the class, but read-only from outside. This feature comes from MooseX::AttributeShortcuts.

Ok, on to MooseX::AttributeShortcuts:

Specifying is => 'rwp' will cause the following options to be set:

is     => 'ro'
writer => "_set_$name"

However, this is just where it was inspired. It is actually implemented in Moo in Method::Generate::Accessor1.

  } elsif ($is eq 'rwp') {
    $spec->{reader} = $name unless exists $spec->{reader};
    $spec->{writer} = "_set_${name}" unless exists $spec->{writer};
  } elsif ($is ne 'bare') {

And even more actually, that is also not where it is done in Moops. In fact, that happens in MooseX::MungeHas, which Moops uses, but only if the caller is not Moo:

            push @code, '  if ($_{is} eq q(rwp)) {';
            push @code, '    $_{is}     = "ro";';
            push @code, '    $_{writer} = "_set_$_" unless exists($_{writer});';
            push @code, '  }';

Looks pretty clear. It's in generated code. The below solution might work if it uses only Moo, but I don't know how to force that.


You are indeed able to change that in Moo by hooking into Moo's Method::Generate::Accessor using Class::Method::Modifiers and adding a bit of logic in an around modifier to generate_method. This does not work works for Moops as long as there is no Moose-stuff involved.

use Moops;

BEGIN {
  require Method::Generate::Accessor; # so it's in %INC;
  require Class::Method::Modifiers;
  Class::Method::Modifiers::around( 'Method::Generate::Accessor::generate_method' => sub {
    my $orig = shift;

    #      0      1      2      3      4
    #  my ($self, $into, $name, $spec, $quote_opts) = @_;

    if ($_[3]->{is} eq 'rwp') {
      $_[3]->{writer} = "_explicitly_set_$_[2]" unless exists $_[3]->{reader};
    }

    $orig->(@_);
  });
}

class Foo {
    has attr => ( is => "rwp" );
}

use Data::Printer;

my $foo = Foo->new( attr => 1 );
p $foo;

Output:

Foo  {
    Parents       Moo::Object
    public methods (2) : attr, new
    private methods (1) : _explicitly_set_attr
    internals: {
        attr   1
    }
}

1) I found that using grep.cpan.me.