Using sub reference with Moose builder - perl

Moose documentation mentions that builder property for attribute in class definiton should be a string that contains name for function that will be called to build relevant attribute. Simple testing however shows that sub reference works as well:
has 'some_attribute' => (
is => 'ro',
lazy => 1,
builder => sub {
require SomeModule::Heavy;
return SomeModule::Heavy->new($_[0]);
},
);
Did I miss something in docs? Is usage of sub reference officially supported for builder?

Moose manual says:
You can also provide a subroutine reference for default. This reference will be called as a method on the object. […] As an alternative to using a subroutine reference, you can supply a builder method for your attribute. This has several advantages. First, it moves a chunk of code to its own named method, which improves readability and code organization. Second, because this is a named method, it can be subclassed or provided by a role.
So, if you use subroutine reference for builder then you lose these advantages. I think subroutine reference works as side effect and has no practical application.

Related

How do I create a custom write accessor when using Moose?

Edit: Answer added below, question left here for historical purposes only.
Moose documentation states that:
If you want, you can also explicitly specify the method names to be
used for reading and writing an attribute's value. This is
particularly handy when you'd like an attribute to be publicly
readable, but only privately settable. For example:
has 'weight' => (
is => 'ro',
writer => '_set_weight',
);
This might be useful if weight is calculated based on other methods.
For example, every time the eat method is called, we might adjust
weight. This lets us hide the implementation details of weight
changes, but still provide the weight value to users of the class.
Based on this example, I wrote the following code:
has '_current_url' => (
is => 'ro',
isa => 'URI',
writer => '_write_URI_to_current_url',
);
# Thus we ensure only a URI object gets written to current_url always
sub _current_url
{
my $self = shift;
$self->_write_URI_to_current_url(URI->new_abs($_[0], $self->start_url));
}
My intention was to ensure that setting current_url always sets it to a URI object even if it was called with a simple string. However, when I try to run the code, I get:
Cannot assign a value to a read-only accessor of _current_url
at the place (within my class) where I'm trying to set the current_url (with $self->_current_url($str);).
This is my second day with Moose so I'm quite confused as to what's going on here. To my understanding the is => 'ro' only asks Moose not to create a default write accessor with the same name as the attribute, is that understanding correct? How can I achieve my goal here?
Ok, I believe I've found the issue.
The _current_url method I've pasted above got overridden by Moose's generated read-only accessor of the same name, so when I tried to call $self->_current_url with a value, it throws the above error to indicate that the read-only accessor cannot set values.
I guess the error message should ideally be Cannot assign a value **through** a read-only accessor of _current_url, not **to** a read-only accessor.
Changing the name of the sub to _set_current_url solved the problem. I guess another way to achieve the same would be to tell Moose that _current_url is => 'rw' and then create an around '_current_url'. I haven't tried this approach.
Late to the game but just saw this post and ran into the same thing a while back. '_set_current_url' really seems like an attribute accessor instead of an attribute. Might want to consider:
has '_current_url' => {
is => 'rw',
isa => 'URI',
writer => 'set_current_url',
reader => 'get_current_url'
}
A bit cleaner this way since the attribute is the original '_current_url' and you have accessors to get/set the attribute.

Perl Classes, what is with Blessing Hashes?

I don't understand why a Perl constructor needs all this blessing, and why it is always done with a hash (it is possible to use other variable types, apparently).
How does it make any sense that a constructor would return a blessed reference to a hash when I create a new instance of a class? I could understand "return (this);" or something along those lines, but returning some other random variable just mystifies me (particularly when you are supposed to use a hash).
my ?var = new Test("foo");
package Test;
our $localvar;
sub new{
localvar = $_[1];
}
OK, so I have this basic class. I can set a class variable when I initialize it and then later use it like $var::localvar. But to have this actually compile and work in Perl I need to add in the line "return bless {}, shift;"???
It seems that this hash is sort of used as the instance of that class, with the methods being rather static. But you can still have class variables. It sounds like you are just linking a data object to a list of methods that take that object as a argument. But I am not sure why every tutorial would imply that you always use a hash if that was all that was happening. And I am confused why you have both this hash and any "our"s you declared in the class, they seem like mutually exclusive concepts?
It sounds like you are just linking a data object to a list of methods that take that object as a argument.
That's exactly what OO is, yes. The thing you return from the constructor is the instance. Unlike some other languages where the language creates a new "object instance" behind the scenes, and your constructor is just in charge of filling in slots, in Perl your constructor method gets to do the entire job. {} creates a reference to a new anonymous hash which will become the storage for the object, and bless is what actually turns that reference into an object, by tagging it with your class name.
But I am not sure why every tutorial would imply that you always use a hash if that was all that was happening.
A class can be any kind of reference, but a hash reference is the most useful and convenient, because hashes have named slots, so you can refer to your object's properties by name. There are examples out there of globref objects (filehandles and sockets), arrayref objects (rare, usually used for objects with only a few fields when the author is very concerned about speed and memory usage), and scalarref objects (often used to encapsulate a pointer returned by some C library). But hashref objects are "standard".
I can set a class variable when I initialize it and then later use it like ?var::localvar.
But why would you? Class variables are almost entirely useless, and there's no reason to bother with them until you have a grasp on more basic, and useful things.
How does it make any sense that a constructor would return a blessed reference to a hash when I create a new instance of a class?
Well, it would be a rather useless constructor if you didn't return the object you've created.
I could understand "return (this);" or something along those lines
Then what's the confusion? That's exactly what you should be returning. (Except the convention is to call it $self.)
sub new {
my ($class, ...) = #_;
my $self = bless({}, $class); # Or: my $self = $class->SUPER::new(...);
$self->{attribute1} = ...;
$self->{attribute2} = ...;
return $self;
}
It seems that this hash is sort of used as the instance of that class,
The blessed hash is the instance of that class aka the object.
Questions from the comments
Why do I have to [do my $self = bless({}, $class);] instead of just referencing the class instance. Like: $self = bless($class)
{} allocates a variable and bless associates it with a class. Two necessary steps in the creation of an object.
By having the two separate, you have the option of using different base variables for your object. For example, IO::Socket::INET uses a glob instead of a hash.
But most objects anyone would make would have no use for a hash in my opinion. A hash is a pretty specific data structure that is not needed or helpful for 99% of coding.
The point isn't to use a hash table; it's to use an associative array (where each element is an attribute).
Associative arrays (hash tables or otherwise) are both "needed and helpful" in far far more than 1% of coding.

Rebuilding lazily-built attribute when an underlying attribute changes in Moose

I've got a Moose class with a lazy_build attribute. The value of that attribute is a function of another (non-lazy) attribute.
Suppose somebody instantiates the class with a value of 42 for the required attribute. Then they request the lazy attribute, which is calculated as a function of 42. Then, they have the nerve to change the first attribute!
The lazy one has already been built, so the builder will not get called again, and the lazy attribute is now out-of-date.
I have a solution now where I maintain a "dirty" flag on the required attribute, and an accessor on the lazy one checks the dirty flag and rebuilds it if needed.
However, this seems like a lot of work. Is there a way to handle this within Moose, e.g. using traits?
My typical solution:
has 'attr1' => (
...
trigger => \&clear_attr2,
);
i.e. when attr1 is updated, attr2 is cleared and will be rebuilt when it is next accessed. clear_attr2 comes for free when you use lazy_build. As long as you use the accessor methods, you don't need a 'dirty' flag.
This is a common pattern - some kind of trait to handle 'derived' attributes would be nice.

How can I access a method of the consumer class inside a method created at runtime in a method of the parameterized role using Moose with Perl?

I define a method inside a parametrized role that needs to create a new class at run time
using Moose::Meta::Class->create and apply that exact parametrized role to it. I am also making a new method for that role using
$new_class->meta->add_method( some_name => sub {
my ($self) = #_;
...
})
inside the sub {...} I want to access a method of the consumer class and use it for something, I have tried using $self->get_method, it didn't work, how do I do this?
Please notice that the $self inside the sub above is MooseX::Role::Parameterized::Meta::Role::Parameterizable
I also have another question, if I do this:
my $object = Moose::Meta::Class->create(
"some_type",
);
Why isn't $object of type some_type and it's some ugly MooseX::Role::Parameterized::Meta::Role::Parameterizable and how do I get to the object of type some_type?
To answer your second question, the reason is because Perl's OO doesn't allow you to add a method to just one instance of a class, so Moose has to fake it by creating a subclass with the extra method and reblessing the unique object into that subclass.
Note that, if you are doing things correctly and doing your introspection with isa, has, and/or does rather than by trying to rely on the name of the object's blessed package, this doesn't matter. The object still isa some_type, has all of some_type's attributes, and does all of some_type's roles even though it's now blessed into a package with an ugly auto-generated name.
It sounds like your underlying problem is nearly exactly what I described at this question: from within the role definition, you need to get at the class (and its meta-class) of the object or class the role is being applied to. This isn't possible from within normal roles, but it's possible through parameterized roles.
I'm not quite sure what you're trying to do here. Let's assume you have
my $new_class = Moose::Meta::Class->create('FooBar');
then $new_class is the meta object for FooBar. So, if you want to add a method to FooBar you would say
$new_class->add_method(foo => sub { … });
which would basically be the same as
FooBar->meta->add_method(foo => sub { … });
You should also probably use find_meta() from Moose::Util. This will return the correct meta object (if there is one) even if your class doesn't have a meta method or it uses it for something else.
As said, I'm not sure this answers your question.

Why doesn't Perl's SUPER call use the arrow method?

I noticed that when you call a superclass's methods, you need to do something like this :
my $self = $class->SUPER::new();
Why isn't that:
my $self = $class->SUPER->new();
I suspect because $class->SUPER->new() would normally be the same as $class->SUPER()->new(). But there isn't a $class->SUPER() function, and its not clear what that would return.
On the other hand, $class->Foo::Bar has always been a valid way to call a method directly by full name, so making a special package-like thing — SUPER — fits in better. (I suspect that you could actually implement SUPER as a package, and maybe it historically was, I don't know)
PS: Take a look at the mro package, and $self->next::method. Also, take a look at Moose if you're going to do serious OO work in Perl.
In short, SUPER isn't a method. It's a virtual package. It's documented in perlobj under the "Method Invocation" section.
Note, however, that SUPER bases itself on the current package, not the package of the instance you used it with.
Method calls have a number of forms:
Calls method, possibly inherited:
->method()
Explicitly calls sub Package::method, whether that's in the inheritance tree or not:
->Package::method()
Explicitly calls the referred-to sub, whether that's in the inheritance tree or not:
->$coderef()
Calls the method that would have been called by __PACKAGE__->method() if there were no sub method in __PACKAGE__ (N.B. the class or object on the left of -> is irrelevant):
->SUPER::method()
Any of the above, depending on the contents of $method:
->$method()
(Legal even under use strict;.)
While the first form is the most common, it's worth learning about the others and how they work.
To add to what derobert said:
You're calling 'new' in the 'SUPER' namespace but passing it the object (or string), '$class'.
You don't have to use SUPER, as you can give the full package name of the parent (useful in cases of diamond inheritance):
sub init {
my $self = shift;
$self->ParentClass1::init();
$self->ParentClass2::init();
}