Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 317
cfi Avatar asked Jan 08 '23 12:01

cfi


2 Answers

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+.

like image 171
Hunter McMillen Avatar answered May 16 '23 00:05

Hunter McMillen


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.

like image 33
cfi Avatar answered May 15 '23 23:05

cfi