{
package Example;
use Moose;
has foo => (setter => { set_foo => sub { ... } }),
}
my $object = Class->new(foo => 10);
$object->foo(20);
In the above example, the setter is called when we set foo to 20, but it is not
called when the initial value of 10 is set. Instead, this basically ends up
being a direct call to set_slot_value.
This seems a minor inconvenience, but there are a lot of things it prevents.
For one, it means that you can't validate an initial value without a type
constraint. You could put it in the setter, but the setter isn't called for
the initial value.
Also, consider something like this:
{ # it's slightly pseudo
package Registered;
use Moose::Role;
has registry => (required => 1, is => 'ro');
sub BUILD { $self->registry->register($self); }
}
This is, I think, a pretty reasonable, though slightly contrived, example. It
isn't possible, though, because Roles cannot contribute BUILD. Also, triggers
are not called when setting the initial value. A simple solution would be to
allow the code that sets the initial value to be replaced with something that
does something special.
I have created two branches, one in Class-MOP and one in Moose, that add a new
attribute to Attribute: initializer. The initializer is used to set the
initial value of an attribute, either when the instance is initialized (for
Class::MOP and Moose) or when an lazy attribute is initialized by getting (for
Moose only).
The initializer may be a coderef or a method name (referring to a method on the
instance.) It's called like this:
$instance->$initializer($value, $name, $callback);
The callback, when called, will set the instance slot to the passed-in value.
It makes it easy to have your initializer do the "hard" work without having to
worry about going through the several chains of methods needed to set a slot
value. The example in the tests should look something like this:
package Example;
use Moose;
has foo => (
is => 'rw',
initializer => sub {
my ($self, $value, $name, $callback) = @_;
$callback->($value * 2);
}
);
With that class...
my $obj = Example->new(foo => 2);
$obj->foo; # returns 4
$obj->foo(2);
$obj->foo; # returns 2
Because of the way the initializer is called, this should work:
package Example;
use Moose;
has foo => (
is => 'rw',
initializer => 'method_name',
);
I think, but have not tested, that this will also work:
package Example;
use Moose;
has foo => (
is => 'rw',
setter => 'set_foo',
initializer => 'set_foo',
);
Thoughts?
--
rjbs
pgpqSnp2ZPcF1.pgp
Description: PGP signature
