What I am tring to do is the following:
I am writing a perl Moose Class and I want ot have a class attribute that is an Hash and is initialized to default values upon building.
My attempt:
has sweep_prop_configuration => (
is=>'rw',
isa => 'Hash',
reader => 'sweep_prop_configuration',
writer => '_sweep_prop_configuration',
builder => '_build_sweep_prop_configuration',
predicate => 'has_sweep_prop_configuration',
);
sub _build_sweep_prop_configuration {
my $self = shift;
my %hash;
$hash{point_number}=0;
$hash{number_of_sweep}=0;
$hash{backwards}=-1;
$hash{at_end}=-1;
$hash{at_end_val}=0;
$hash{save_all}=-1;
return %hash;
}
I am new to Moose and perl in general, excuse me if I missed something in the documentation.
Moose doesn't define Hash as a type (see Moose::Manual::Types).
It defines HashRef, though. In order to use it, change the builder's last line to
return \%hash
and change the type constraint to
isa => 'HashRef',
It still defines an instance attribute, not a class attribute. To define class attributes, use MooseX::ClassAttribute.
Related
Using Perl and Moose, object data can be accessed in 2 ways.
$self->{attribute} or $self->attribute()
Here is a simple example demonstrating both:
# Person.pm
package Person;
use strict;
use warnings;
use Moose;
has 'name' => (is => 'rw', isa => 'Str');
has 'age' => (is => 'ro', isa => 'Int');
sub HAPPY_BIRTHDAY {
my $self = shift;
$self->{age}++; # Age is accessed through method 1
}
sub HAPPY_BIRTHDAY2 {
my $self = shift;
my $age = $self->age();
$self->age($age + 1); # Age is accessed through method 2 (this will fail)
}
1;
# test.pl
#!/usr/bin/perl
use strict;
use warnings;
use Person;
my $person = Person->new(
name => 'Joe',
age => 23,
);
print $person->age()."\n";
$person->HAPPY_BIRTHDAY();
print $person->age()."\n";
$person->HAPPY_BIRTHDAY2();
print $person->age()."\n";
I know that when you are outside of the Person.pm file it is better to use the $person->age() version since it prevents you from making dumb mistakes and will stop you from overwriting a read only value, but my question is...
Inside of Person.pm is it best to use $self->{age} or $self->age()? Is it considered bad practice to overwrite a read-only attribute within the module itself?
Should this attribute be changed to a read/write attribute if its value is ever expected to change, or is it considered acceptable to override the read-only aspect of the attribute by using $self->{age} within the HAPPY_BIRTHDAY function?
When using Moose, the best practice is to always use the generated accessor methods, even when inside the object's own class. Here are a few reasons:
The accessor methods may be over-ridden by a child class that does something special. Calling $self->age() assures that the correct method will be called.
There may be method modifiers, such as before or after, attached to the attribute. Accessing the hash value directly will skip these.
There may be a predicate or clearer method attached to the attribute (e.g. has_age). Messing with the hash value directly will confuse them.
Hash keys are subject to typos. If you accidentally say $self->{aeg} the bug will not be caught right away. But $self->aeg will die since the method does not exist.
Consistency is good. There's no reason to use one style in one place and another style elsewhere. It makes the code easier to understand for newbs as well.
In the specific case of a read-only attribute, here are some strategies:
Make your objects truly immutable. If you need to change a value, construct a new object which is a clone of the old one with the new value.
Use a read-only attribute to store the real age, and specify a private writer method
For example:
package Person;
use Moose;
has age => ( is => 'ro', isa => 'Int', writer => '_set_age' );
sub HAPPY_BIRTHDAY {
my $self = shift;
$self->_set_age( $self->age + 1 );
}
Update
Here's an example of how you might use a lazy builder to set one attribute based on another.
package Person;
use Moose;
has age => ( is => 'rw', isa => 'Int', lazy => 1, builder => '_build_age' );
has is_baby => ( is => 'rw', isa => 'Bool', required => 1 );
sub _build_age {
my $self = shift;
return $self->is_baby ? 1 : 52
}
The lazy builder is not called until age is accessed, so you can be sure that is_baby will be there.
Setting the hash element directly will of course skip the builder method.
I don't think $self->{age} is a documented interface, so it's not even guaranteed to work.
In this case I'd use a private writer as described in https://metacpan.org/pod/Moose::Manual::Attributes#Accessor-methods:
has 'weight' => (
is => 'ro',
writer => '_set_weight',
);
You could even automate this using 'rwp' from https://metacpan.org/pod/MooseX::AttributeShortcuts#is-rwp:
use MooseX::AttributeShortcuts;
has 'weight' => (
is => 'rwp',
);
Out-of-the-box perl isn't type safe and doesn't have much in the way of encapsulation, so it's easy to do reckless things. Moose imposes some civilization on your perl object, exchanging security and stability for some liberty. If Moose gets too stifling, the underlying Perl is still there so there are ways to work around any laws the iron fist of Moose tries to lay down.
Once you have wrapped your head around the fact that you have declared an attribute read-only, but you want to change it, even though you also said you wanted it to be read-only, and in most universes you declare something read only because you don't want to change it, then by all means go ahead and update $person->{age}. After all, you know what you are doing.
I have a Moose class with an overloaded stringification operator which I would actually like to change at runtime, because I need text output in different formats.
In other words, I would like to be able to do something like this:
$obj = Class->new("a'");
$obj->formatter("A::Formatter");
print "$obj";
# prints "a'"
$obj->formatter("Another::Formatter");
print "$obj";
# prints a1
I would also like the formatters to be modular, so that I can encapsulate and plug in different ones without creating a giant spaghetti mess.
How should I best do this? are roles a good solution for this kind of problem? I have checked MooseX::Object::Pluggable, MooseX::Traits::Pluggable and MooseX::Traits to start but need advice as this is uncharted territory for me.
You don't need to change the overloading at runtime, just how it's implemented. Remember, overloading can be done with a method name as opposed to a subref:
package Foo;
use Moose;
use overload q{""} => '_stringify';
has id => (
is => 'ro',
isa => 'Str',
required => 1,
);
has formatter_class => (
is => 'rw',
isa => 'ClassName',
required => 1,
);
sub _stringify {
my $self = shift;
return $self->formatter_class()->new()->format($self);
}
We use Moose classes that serialize iterators into various output formats. We describe the iterator as an attribute:
has iterator => (
is => 'ro',
isa => 'CodeRef',
required => 1,
);
This has worked fine so far, but we have lately been using Iterator::Simple to prepare iterators for later consumption. This means that we can go about writing this:
has iterator => (
is => 'ro',
isa => 'CodeRef|Iterator::Simple::Iterator',
required => 1,
);
And allow our serializers to accept the iterator class correctly. However, that seems to be a incomplete solution.
Is there a way in Moose to specify the constraint that the attribute must be callable? I suspect it may be possible with Moose::Util::TypeConstraints and using overload::Overloaded on &{} to check, but I'd like to know if anyone has created a module to do this already or if there is a Moose-standard way to test for this.
CodeRef only allows unblessed code references. Fortunately, it's easy to make your own types.
Define Callable as shown below, then use it instead of CodeRef. It allows the following:
Unblessed code references.
Blessed code references.
Objects that pretend to be code references (i.e. those that overload &{}).
use Moose::Util::TypeConstraints;
use overload qw( );
use Scalar::Util qw( );
subtype 'Callable'
=> as 'Ref'
=> where {
Scalar::Util::reftype($_) eq 'CODE'
||
Scalar::Util::blessed($_) && overload::Method($_, "&{}")
}
# Written such that parent's inline_as needs not be prepended.
=> inline_as {'(
(Scalar::Util::reftype('.$_[1].') // "") eq 'CODE'
||
Scalar::Util::blessed('.$_[1].') && overload::Method('.$_[1].', "&{}")
)'};
no Moose::Util::TypeConstraints;
Do you know enough to get from Scalar::Util::reftype?
I have a class where I want to apply string overloading on its id attribute. However, Moose doesn't allow string overloading on attribute accessors. For example:
package Foo;
use Moose;
use overload '""' => \&id, fallback => 1;
has 'id' => (
is => 'ro',
isa => 'Int',
default => 5,
);
package main;
my $foo = Foo->new;
print "$foo\n";
The above will give an error:
You are overwriting a locally defined method (id) with an accessor at C:/perl/site/lib/Moose/Meta/Attribute.pm line 927
I have tried a couple of options to get around this:
Marking id is => bare, and replacing it with my own accessor: sub id {$_[0]->{id}}. But this is just a hack.
Having the string overloader use another method which just delegates back to id: sub to_string {$_[0]->id}.
I'm just wondering if anyone has a better way of doing this?
use overload '""' => sub {shift->id}, fallback => 1;
Works fine for me.
I believe you are getting an error because \&id creates a placeholder for a sub to be defined later, because Perl will need to know the address that sub will have when it is defined to create a reference to it. Moose has it's own checks to try to avoid overwriting methods you define and reports this to you.
Since I think what you really want to do is call the id method when the object is used as a sting like so:
use overload '""' => 'id', fallback => 1;
From the overload documentation
Values specified as strings are interpreted as method names.
I'm working on a serialization tool using Moose to read and write a file that conforms to a nonstandard format. Right now, I determine how to load the next item based on the default values for the objects in the class, but that has its own drawbacks. Instead, I'd like to be able to use information in the attribute meta-class to generate a new value of the right type. I suspect that there's a way to determine what the 'isa' restriction is and derive a generator from it, but I saw no particular methods in Moose::Meta::Attribute or Class::MOP::Attribute that could help me.
Here's a bit further of an example. Let's say I have the following class:
package Example;
use Moose;
use My::Trait::Order;
use My::Class;
with 'My::Role::Load', 'My::Role::Save';
has 'foo' => (
traits => [ 'Order' ],
isa => 'Num',
is => 'rw',
default => 0,
order => 1,
);
has 'bar' => (
traits => [ 'Order' ],
isa => 'ArrayRef[Str]',
is => 'rw',
default => sub { [ map { "" } 1..8 ] }
order => 2,
);
has 'baz' => (
traits => [ 'Order' ],
isa => 'Custom::Class',
is => 'rw',
default => sub { Custom::Class->new() },
order => 3,
);
__PACKAGE__->meta->make_immutable;
1;
(Further explanation: My::Role::Load and My::Role::Save implement the serialization roles for this file type. They iterate over the attributes of the class they're attached to, and look at the attribute classes for an order to serialize in.)
In the My::Role::Load role, I can iterate over the meta object for the class, looking at all the attributes available to me, and picking only those that have my Order trait:
package My::Role::Load;
use Moose;
...
sub load {
my ($self, $path) = #_;
foreach my $attribute ( $self->meta->get_all_attributes ) {
if (does_role($attribute, 'My::Trait::Order') ) {
$self->load_attribute($attribute) # do the loading
}
}
}
Now, I need to know the isa of the attribute that the meta-attribute represents. Right now, I test that by getting an instance of it, and testing it with something that's kind of like this:
use 5.010_001; # need smartmatch fix.
...
sub load_attribute {
my ($self, $attribute, $fh) = #_;
my $value = $attribute->get_value($self); # <-- ERROR PRONE PROBLEM HERE!
if (ref($value) && ! blessed($value)) { # get the arrayref types.
given (ref($value)) {
when('ARRAY') {
$self->load_array($attribute);
}
when('HASH') {
$self->load_hash($attribute);
}
default {
confess "unable to serialize ref of type '$_'";
}
}
}
else {
when (\&blessed) {
confess "don't know how to load it if it doesn't 'load'."
if ! $_->can('load');
$_->load();
}
default {
$attribute->set_value($self, <$fh>);
}
}
}
But, as you can see at # <-- ERROR PRONE PROBLEM HERE!, this whole process relies on there being a value in the attribute to begin with! If the value is undef, I have no indication as to what to load. I'd like to replace the $attribute->get_value($self) with a way to get information about the type of value that needs to be loaded instead. My problem is that the docs I linked to above for the Class::MOP::Attribute and the Moose::Meta::Attribute don't seem to have any way of getting at the type of object that the attribute is supposed to get.
The type information for an attribute is basically what I'm trying to get at.
(Note to future readers: the answer here got me started, but is not the final solution in of itself. You will have to dig into the Moose::Meta::TypeConstraint classes to actually do what I'm looking for here.)
Not sure I follow what you are after and perhaps Coercions might do what you want?
However to get the attributes isa:
{
package Foo;
use Moose;
has 'bar' => ( isa => 'Str', is => 'rw' );
}
my $foo = Foo->new;
say $foo->meta->get_attribute('bar')->type_constraint; # => 'Str'
/I3az/
Out of curiosity why not use/extend MooseX::Storage? It does Serialization, and has for about two and a half years. At the very least MooseX::Storage will help you by showing how a (well tested and production ready) serialization engine for Moose is written.
I'm not quite sure I understand (perhaps you can include some pseudocode that demonstrates what you are looking for), but it sounds like you could possibly get the behaviour you want by defining a new attribute trait: set up your attribute so that a bunch of methods on the class delegate to the attribute's object (isa => 'MySerializer', handles => [ qw(methods) ]).
You might possibly also need to subclass Moose::Meta::Class (or better, add a role to it) which augments the behaviour of add_attribute().
Edit: If you look at the source for Moose::Meta::Attribute (specifically the _process_options method), you will see that the isa option is processed by Moose::Util::TypeConstraints to return the actual type to be stored in the type_constraint field in the object. This will be a Moose::Meta::TypeConstraint::Class object, which you can make calls like is_a_type_of() against.
This field is available via the type_constraint method in Moose::Meta::Attribute. See Moose::Meta::TypeConstraint for all the interfaces available to you for checking an attributes's type.