Can Perl Moose create multiple accessors? - perl

So, Perl's standard naming convention is snake_case, but I'm writing a module to interface with a REST API that uses camelCase, creating objects with the Moose infrastructure. I'd rather make the objects work with either case, but I can't seem to get multiple Moose-y accessors. The following is the only way I could come up with.
has 'full_name' => (
is => 'rw',
isa => 'Str',
);
sub fullName {return shift->full_name(#_)};
Any better way to do this with Moose's built-ins?

Bah, easy answer. I completely overlooked MooseX::Aliases that allows you to do this easily:
has 'full_name' => (
is => 'rw',
isa => 'Str',
alias => 'fullName', # or alias => [qw(fullName)] for even more
);
Not built-in Moose like I was thinking there would be, but definitely sufficient.

Related

Why is the "lazy_build" feature in Moose discouraged?

The documentation for the lazy_build feature in Moose has this to say:
Note that use of this feature is strongly discouraged. Some documentation used to encourage use of this feature as a best practice, but we have changed our minds.
However, it does not explain what the reasoning for this is, and either my google-fu is terrible or there is no public explanation for why this is discouraged.
What's the problem with lazy_build that makes it discouraged today?
This is in Moose::Manual::BestPractices:
Avoid lazy_build
As described above, you rarely actually need a clearer or a predicate. lazy_build adds both to your public API, which exposes you to use cases that you must now test for. It's much better to avoid adding them until you really need them - use explicit lazy and builder options instead.
So what it's saying is that instead of using the property:
has attribute => (
...,
lazy_build => 1, # creates a builder called _build_attribute
);
You should instead be more explicit:
has attribute => (
...,
lazy => 1,
builder => '_build_attribute',
);
As that doesn't implicitly add clearer and predicate methods.

How to efficiently apply a regex substitution on a Moose attribute?

I have a
package Test;
use Moose;
has 'attr' => ( is => 'rw', isa => 'Str' );
Inside a method I'd like to apply a s/pattern/string/g on the attribute. For reasons documented in Moose (basically to properly support polymorphism) I do not want to access the $self->{attr} directly, so a simple:
$self->{attr} =~ s/pattern/string/g;
is not an option. How can I do this efficiently in speed and little but clear code with Moose?
Options I came up with are:
1) Use a temporary variable, and the usual getter/setter method:
my $dummy = $self->attr;
$dummy =~ s/pattern/string/g;
$self->attr($dummy);
2) Using the attr getter/setter on the left hand side:
$self->attr($dummy) =~ s/pattern/string/g;
But this obviously throws an error
Can't modify non-lvalue subroutine call at Test.pm
line 58, line 29
Is there a way to use Moose accessors as lvalue subs?
3) Use the String traits
Redefine the attribute:
has 'attr' => ( is => 'rw', isa => 'Str', traits => ['String'],
handles => { replace_attr => 'replace'} );
Then in the method use:
$self->replace_attr('pattern', 'string');
However the docs explicitly say, there's no way to specify the /g flag.
Any elegant, simple, somewhat efficient method available out of the box?
I have used this approach in the past and I think it seems suitable to me for general use in terms of efficiency and cleanliness. It also works with the /g modifier.
$self->attr( $self->attr =~ s/pattern/string/gr );
I suspect that under the hood this is the same as your first example with the temporary variable, it is just hidden from us.
Please note that the to use the /r modifier, which returns the result of the substitution without modifying the original, requires Perl 5.14+.
My Option (2) and this question provide the idea to use MooseX::LvalueAttributes:
package Test;
use Moose;
use MooseX::LvalueAttribute 'lvalue';
has 'attr' => ( is => 'rw', isa => 'Str', traits => [lvalue] );
This allows the straightforward syntax:
$self->attr($dummy) =~ s/pattern/string/g;
Internally this uses Variable::Magic and the perlsub lvalue feature, so there is a performance overhead to this approach which affects every access to the 'traited' attribute, not just the ones where it's used as a left hand side. Thanks to LeoNerd and ikegami for their correcting comments on my earlier statements.
Therefore, and confirmed by the module's documentation, Moose's type checking still works and triggers are fired.

Moo, lazy attributes, and default/coerce invocation

My Moo based class has both lazy & non-lazy attributes which have both default and coerce subs. If I don't initialize the attributes I'm finding that both default and coerce subs are called for the normal attribute, but only default is called for the lazy attribute. That seems inconsistent. Here's sample code:
package Foo;
use Moo;
has nrml => ( is => 'ro',
default => sub { print "nrml default\n" },
coerce => sub { print "nrml coerce\n" }
);
has lazy => ( is => 'ro',
lazy => 1,
default => sub { print "lazy default\n" },
coerce => sub { print "lazy coerce\n" }
);
my $q = Foo->new( );
$q->lazy;
The output is:
nrml default
nrml coerce
lazy default
I only expect coerce to run if I provide a value in the constructor. More importantly I expect the same sequence of execution (either default or default and coerce) from both lazy and normal attributes.
So, are my expectations off, is this a bug, or what? Thanks!
Current status: fix shipped in 009014
One of those two is a bug.
In fact, thinking about it, one could argue either way about whether coercions -should- be fired on defaults but since Moose does do so, and since coercions are structural (unlike type checks, which are often used for assertion-like things and should always pass except in the presence of a bug), I think it falls that way.
... in fact, the problem is that Method::Generate::Accessor when it fires _use_default always wraps it in _generate_simple_set, when it's _generate_set that provides the isa+coerce+trigger wrapping - and I'm fairly sure that Moose fires all three when it's applying a default, so we need to too.
It's not an entirely trivial fix to make though, because I didn't parameterise _generate_set to take a value indicating how to generate the value to set. I'll try and sort it out tomorrow since I'm planning to cut a release then.
If you want support for Moo from the developers, please contact bugs-Moo#rt.cpan.org or join #web-simple on irc.perl.org - it's sheer luck that somebody on the IRC channel saw this question and asked about it :)
That would qualify as a bug to me. Either the value from default is expected to be of the right type, or it's not. Having and enforcing the expectation only half of the time makes no sense.

Is there a Perl module for converting YAML files into Moose objects dynamically at runtime?

I've been trying to find a Perl module that converts a YAML file into moose objects without having to pre-declare the structure as you seem to need to do when using MooseX::YAML. Does anyone know of such a module (or script)?
Don't.
Moose classes, their attributes, and whatever else belongs to them, have a lot of meta-data attached to them. You can't infer all of that meta-data from the data of a single instance.
I'm assuming, given a yaml document as
---
foo: 42
bar: ['moo', 'kooh']
you'd expect and object back that responds to calls to a foo and a bar method, returning the respective values. But how should those accessors behave? Should they be simple reader-methods, or also allow writing? Should they validate against any kind of typeconstraint? etc.
If all you really need is something that makes some unblessed data-structure accessible like an object, have a look at Data::Hive, Hash::AsObject, and similar modules instead.
If you really want to build proper Moose classes, and are either alright with the guesswork that'd be involved, or happen to have the necessary meta-data available somewhere, you can just use the meta-protocol.
my $class = Moose::Meta::Class->create_anon_class(
attributes => [map {
# your particular set of assumptions here
Moose::Meta::Attribute->new($_ => (is => 'ro', ...))
} keys %{ $deserialized_yaml }],
);
my $instance = $class->name->new($deserialized_yaml);
$instance->$some_key_in_the_yaml_document;
If you don't want to do anything special in your YAML, you can just set up the
proper coercions for your Moose classes and then pass the loaded data to
new(). For example:
package My::Types;
use Moose::Util::TypeConstraints;
class_type 'My::Related::Class';
coerce 'My::Related::Class', from 'HashRef',
via { My::Related::Class->new(%$_) };
package My::Class;
use Moose;
has name => (is => 'ro', isa => 'Str');
has related => (is => 'ro', isa => 'My::Related::Class', coerce => 1);
package My::Related::Class;
use Moose;
has flavor => (is => 'ro', isa => 'Str');
Then, in some YAML:
name: "An instance of My::Class"
related:
flavor: "Dangerberry!"
Then, in some code:
my $obj = My::Class->new(Load($yaml_data));
print ref $obj->related; # My::Related::Class
print $obj->related->flavor; # Dangerberry!
This isn't roundtrippable, obviously, without some more customization of your Moose classes -- it's just for constructing objects. Also, you need to know what classes the root objects are.
That's probably enough for a lot of simple uses, though.

perl Moose accessor madness - can't define only a reader or writer accessor!

So I'm just trying to do a very simple thing: define a custom reader accessor for a moose attribute. So I try this:
has 'compiled_regex' => (
isa => 'RegexpRef',
is => 'rw',
reader => 'get_compiled',
);
but get_compiled never gets called, presumably because compiled_regex is read/write. Ok, no problem. I next try this:
has 'compiled_regex' => (
isa => 'RegexpRef',
writer => '_compile',
reader => 'get_compiled',
);
and that produces the following error:
Can't locate object method "compiled_regex" via package "PrettyRegex" at ../lib/Pretty/Regexs.pm line 39.
which refers to this line which is in the _compile method:
$self->compiled_regex(qr/$self->regex/);
Now I haven't gotten much sleep in the past 3 days so maybe I'm confused, but it seems that even if this did work, it would create an infinite regress since I've defined the writer as _compile ... so what am I missing here?
tried Sinan answer but still getting:
Can't locate object method "compiled_regex" via package "PrettyRegex" at ../lib/Pretty/Regexs.pm line 41.
I'm unclear on what you're trying to do. reader and writer are methods that Moose creates for you, not methods that you write and it calls.
I think you need to restate your question to explain the higher-level problem that you're trying to solve. I expect there's a better way to do it than you've currently thought of, but we can't be sure without knowing what you're really trying to do.
If you're trying to get your custom method called when the attribute is read, just name the reader something else (like _get_compiled_regex), and name your method compiled_regex. Or use a method modifier on the reader method. (That's probably better, because then you won't forget to die if somebody passes a parameter to your reader method, trying to set the attribute.)
You also might want to have a trigger on some other attribute that clears this one.
I keep guessing at what the actual question is, but I have a feeling the following corresponds to it:
package My::M;
use Moose;
use namespace::autoclean;
has 'compiled_regex' => (
isa => 'RegexpRef',
is => 'ro',
writer => '_set_compiled_regex',
);
sub compile {
my $self = shift;
my ($pat) = #_;
$self->_set_compiled_regex(qr/$pat/);
return;
}
__PACKAGE__->meta->make_immutable;
package main;
use strict; use warnings;
my $m = My::M->new;
$m->compile( '^\W+\z' );
if ( '##$%%$' =~ $m->compiled_regex ) {
print "Hmph!\n";
}
Er? If your reader is called get_compiled, and your writer is called _compile, then you don't have a method named compiled_regex, and it should be obvious why a call to that nonexistent method would fail. You need to take several steps back and explain what you're trying to do, instead of what's going wrong with the way you've tried to do it.