felima
felima

Reputation: 91

Does setting a writer name for a Moo read-only attribute make it read-write?

I'm studying Moo and bumped into this basic question. If I set an accessor or a writer name for a read-only attribute the attribute becomes writable. Do accessors or writers imply that the attribute is writable even if it's set to read-only (is => 'ro')?

Here is the Class code:

#!/home/fl/perl5/perlbrew/perls/perl-5.26.1/bin/perl
package MooPerson;
use Moo;
use namespace::clean;

has firstname => (
    is     => 'rw',
    reader => 'get_firstname',
    writer => 'set_firstname',
);

has surname => (
    is      => 'ro',
    reader  => 'get_surname',
    writer  => 'set_surname',
);

sub get_fullname {
    my ($self) = @_;
    my $firstname = $self->get_firstname;
    my $surname   = $self->get_surname;
    return "$firstname $surname";
}
1;

Object code:

#!/home/fl/perl5/perlbrew/perls/perl-5.26.1/bin/perl
use lib 'lib';
use MooPerson;
use feature 'say';
use strict;
use warnings;

say "new object person";
my $person = MooPerson->new(
    firstname => 'Homer',
    surname   => 'Simpson',
);

say "person->get_firstname: " . $person->get_firstname();
say "person->get_surname: " . $person->get_surname();

say "\nchange firstname and surname";
$person->set_firstname('Aristotle');
$person->set_surname('Amadopolis');

say "person->get_firstname: " . $person->get_firstname();
say "person->get_surname: " . $person->get_surname();

Result:

fl@dancertest:~/perltest$ ./firstMoo.pl
new object person
person->get_firstname: Homer
person->get_surname: Simpson

change firstname and surname
person->get_firstname: Aristotle
person->get_surname: Amadopolis

The same behavior occurs when I use accessor. (is => 'ro') only works if I use the auto generated accessor name, in this case "surname".

Is it an intended behavior or a bug?

Thank you very much.

Upvotes: 2

Views: 298

Answers (2)

ikegami
ikegami

Reputation: 386706

Yes, because is doesn't make anything read-only; it simply creates an accessor.

  • is => 'rw' is just a shortcut for accessor => 'attribute_name'.
  • is => 'ro' is just a shortcut for reader => 'attribute_name'.

If you specify a writer, you will be able to write to the attribute, even if you used is => 'ro'.

If I want a public reader and a private/protected writer, I use

has surname => (
    reader => 'get_surname',
    writer => '_set_surname',
);

or

has surname => (
    reader => 'surname',
    writer => '_set_surname',
);

Upvotes: 4

Håkon Hægland
Håkon Hægland

Reputation: 40778

I think this is intended behavior and not a bug. As soon as you create a writer with writer => 'set_surname' the attribute is no longer read-only in the sense that you can now modify it through the writer. If you want a private writer and a public reader you could put an underscore in front of the name of the writer to indicate that it is internal to the class:

has surname => (
    is      => 'ro',
    reader  => 'get_surname',
    writer  => '_set_surname',
);

Putting an underscore in front of a method name is the common way in Perl to give a hint to the user that an attribute is intended for private use only.

Upvotes: 2

Related Questions