1

I now know that the proper way to access Moose class attributes is to ALWAYS go through the accessor method that is automatically generated by Moose.

See Friedo's answer to my previous question for the reasons why.

However this raises a new question... How do you ensure Moose class attributes are handled correctly within regular expressions?


Take the following for example:

Person.pm

package Person;

use strict;
use warnings;
use Moose;

has 'name' => (is => 'rw', isa => 'Str');
has 'age'  => (is => 'rw', isa => 'Int');

# Make package immutable
__PACKAGE__->meta->make_immutable;

test.pl

#!/usr/bin/perl

use strict;
use warnings;
use Person;

my $person = Person->new(
    name => 'Joe',
    age  => 23,
);

my $age = 23;

# Access age the "proper" way
if ($age =~ m/$person->age()/) {
    print "MATCH 1!\n";
}

# Access age the "improper" way
if ($age =~ m/$person->{age}/) {
    print "MATCH 2!\n";
}

This code will output the following:

MATCH 2!

It seems that Perl does not parse the accessor method correctly when it is placed in a regex... What is the proper way to handle this?

I know I could just do this:

my $compare_age = $person->age();
if ($age =~ m/$compare_age/) {
    # MATCH!
}

But how can I get it to work without the extra step of storing it in a separate variable, while still going through the accessor?

Community
  • 1
  • 1
tjwrona1992
  • 8,614
  • 8
  • 35
  • 98
  • 1
    I realize this is a contrived example, but generally you shouldn't be using a regex for integers anyway. Can you explain what you're actually using the regex for? – ThisSuitIsBlackNot Sep 16 '15 at 15:44
  • I have objects that store CVS file information and I'm using regex's to compare file data. I came up with a generic problem to express the problem as simply as possible. (The attribute could really be any arbitrary string, not necessarily an integer). – tjwrona1992 Sep 16 '15 at 15:45
  • For example I may want to see if a string contains a file name so I would use something like this: `if ($string =~ m/$file->name()/) { }` – tjwrona1992 Sep 16 '15 at 15:48
  • 1
    See [How do I expand function calls in a string?](http://perldoc.perl.org/perlfaq4.html#How-do-I-expand-function-calls-in-a-string?) in perlfaq4 (the contents of `m//` are interpolated similar to `qq`). Personally, I find using an intermediate variable to be more readable. – ThisSuitIsBlackNot Sep 16 '15 at 16:10

1 Answers1

3

First of all, /$compare_age/ is wrong since 23 =~ /2/ matches. Fixed:

$age =~ /^\Q$compare_age\E\z/

There is a trick to evaluate an arbitrary expression within double-quote string literals and regex literals.

$age =~ /^\Q${\( $person->age() )}\E\z/

Buy what you should be using is the following:

$age == $person->{age}
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • I know matching an integer is a stupid example, but that's not the point. The point is to be able to go through the accessor for the attribute in the regex which would mean using `$person->age()` instead of `$person->{age}` and getting it to be recognized correctly as a regex. – tjwrona1992 Sep 16 '15 at 16:10
  • That was answered, but you asked what was proper. And that's not proper. – ikegami Sep 16 '15 at 16:10
  • Thats so syntactically ugly... lol, is there really no better way? Also for the sake of example I don't care if 23 =~ /2/ comes out to a match. I just want the simplest possible syntax to make the regex work at all. – tjwrona1992 Sep 16 '15 at 16:11
  • @tjwrona1992 The simplest possible syntax is to use a temporary variable. – ThisSuitIsBlackNot Sep 16 '15 at 16:13
  • @ThisSuitIsBlackNot, what about a situation where I want a regex to match multiple attributes at once and would need to define many temporary variables. That just seems bulky and unneccessary. – tjwrona1992 Sep 16 '15 at 16:14
  • That makes no sense. You can only perform a regex match against one value. – ikegami Sep 16 '15 at 16:16
  • @tjwrona1992 you would need to build a pattern like `/foo|bar|baz/` to match against several attributes at the same time. – simbabque Sep 22 '15 at 15:02
  • @simbabque, That isn't what I meant by multiple at once. I meant I have a regular expression that contains multiple attributes within it. like `/$foo$bar$baz/` all in one regex – tjwrona1992 Sep 22 '15 at 17:48
  • What would you match against? – ikegami Sep 22 '15 at 17:49
  • Anything where a bunch of atomic values from the attributes form a value, like the name of a product that's made up of size, color, brand and model. But I'd make a custom method that returns it, or a lazy attribute with clearer and triggers on the parts. Then save that in a variable and build regex from that. – simbabque Sep 22 '15 at 20:23
  • No, then you'd need a joining and an escaping mechanism, which means you'd have to canonize first, which makes a regex approach much longer than the alternative (parsing and comparing), seeing as canonizing requires parsing on the first place – ikegami Sep 22 '15 at 20:26