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

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.

Related

Can Perl Moose create multiple accessors?

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.

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.

When should I use `use`?

As long as I can remember, whenever I use a module I include a use line at the beginning of my code.
Recently I was writing two Moose object modules that use each other. Look at this over-simplistic example:
One module:
package M1 0.001;
use Moose;
use 5.010;
use namespace::autoclean;
# use M2; ### SEE QUESTION BELOW
has 'name' => (
is => 'ro',
isa => 'Str',
required => 1,
);
has 'very_cool_name' => (
is => 'ro',
lazy => 1,
builder => '_build_very_cool_name',
);
sub _build_very_cool_name {
my ($self) = #_;
my $m2 = M2->new( m1 => $self );
return 'very ' . $m2->cool_name();
}
__PACKAGE__->meta->make_immutable;
1;
Another module:
package M2 0.001;
use Moose;
use 5.010;
use Data::Dumper; # TODO DEBUG REMOVE
use namespace::autoclean;
use M1;
has 'm1' => (
is => 'ro',
isa => 'M1',
required => 1,
);
sub cool_name {
my ($self) = #_;
return 'cool ' . $self->m1->name();
}
__PACKAGE__->meta->make_immutable;
1;
And a short example that uses them:
use strict;
use warnings;
use 5.010;
use M1;
use M2;
my $m1 = M1->new(name => 'dave');
say $m1->very_cool_name();
Now, note the two modules use each other. M1 creates an instance of M2 and use it to generate very_cool_name, while M2 has an instance of M1 as an attribute.
Now, if I uncomment use M2; in M1 my eclipse goes mad. I guess it's because this a the loop created by this 'circular use`.
I commented this use and everything seems to works fine (I think...), but makes me really anxious (I'm using an object without use-ing its class! Is that 'legal'?..). This also which made me wonder:
When do I really need to use use? I think that I was taught to always use it, and surely when I use an object.
Is there anything fundamentally wrong when two modules use each other (in the sense that each uses an object of the other module; I know there some cases where this is logically impossible, but sometimes - as in this case - I think it does make sense).
From a Perl perspective it's fine. use is smart enough to recognize that a module has already been loaded and not load it again. That said, the faint whiff of code smell should make you think about whether mutual dependency is acceptable or if you should refactor your code to eliminate it.
Eclipse, apparently, is not quite as bright. Commenting out one of the use statements to placate the editor will probably work but could create subtle bugs depending on the order the modules are loaded in (by the application) and when they use each others functionality. e.g. if M1 were loaded first, didn't use M2, and had a BEGIN block that needed functionality from M2... boom! If nothing happens until runtime (which is likely) you should be okay.
While it isn't an issue here, you'd also have problems if your use imported symbols from the other package. In that case couldn't remove the use without changing your code to fully qualify all references. (e.g. call MyModule::function() instead of just function())
There's no reason for M2 to use M1. You don't actually have a recursive dependency.
All M2 does is validate some object's classname -- which doesn't require having loaded M1 -- and call a method on it -- which means whoever constructed that object loaded M1.
Your rule about needing to use a class in order to call methods on an object of that class is wrong. use a class when you're going to call methods on the it directly -- for example, new. (This assumes pure OO modules, obviously, not things that export functions/symbols.)
Consider polymorphism. It is a feature that I can make my own subclass of M1 (called, say, M1A) and pass it to M2 without M2 having to know anything about the existence of M1A.

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.