Best way to use "isa" method? - perl

What is the "best" way to use "isa()" reliably? In other words, so it works correctly on any value, not just an object.
By "best", I mean lack of un-handled corner cases as well as lack of potential performance issues, so this is not a subjective question.
This question mentions two approaches that seem reliable (please note that the old style UNIVERSAL::isa() should not be used, with reasons well documented in the answers to that Q):
eval { $x->isa("Class") }
#and check $# in case $x was not an object, in case $x was not an object
use Scalar::Util 'blessed';
blessed $x && $x ->isa($class);
The first one uses eval, the second uses B:: (at least for non-XS flavor of Scalar::Util).
The first does not seem to work correctly if $x is a scalar containing a class name, as illustrated below, so I'm leaning towards #2 (using blessed) unless somoene indicates a good reason not to.
$ perl5.8 -e '{use IO::Handle;$x="IO::Handle";
eval {$is = $x->isa("IO::Handle")}; print "$is:$#\n";}'
1:
Are there any objective reasons to pick one of these two approaches (or a 3rd one i'm not aware of) such as performance, not handling some special case, etc...?

The Scalar::Util implementation is categorically better. It avoids the overhead of the eval {} which always results in the setting of an additional variable.
perl -we'$#=q[foo]; eval {}; print $#'
The Scalar::Util implementation is easier to read (it doesn't die for a reason that is unknown to the code). If the eval fails too, I believe what happens is you have walk backwards in the tree to the state prior to the eval -- this is how resetting state is achieved. This comes with additional overhead on failure.
Benchmarks
Not an object at all
Rate eval su
eval 256410/s -- -88%
su 2222222/s 767% --
Object passing isa check
Rate su eval
su 1030928/s -- -16%
eval 1234568/s 20% --
Object failing isa check
Rate su eval
su 826446/s -- -9%
eval 909091/s 10% --
Test code:
use strict;
use warnings;
use Benchmark;
use Scalar::Util;
package Foo;
Benchmark::cmpthese(
1_000_000
, {
eval => sub{ eval{ $a->isa(__PACKAGE__) } }
, su => sub { Scalar::Util::blessed $a && $a->isa(__PACKAGE__) }
}
);
package Bar;
$a = bless {};
Benchmark::cmpthese(
1_000_000
, {
eval => sub{ eval{ $a->isa(__PACKAGE__)} }
, su => sub { Scalar::Util::blessed $a && $a->isa(__PACKAGE__) }
}
);
package Baz;
$a = bless {};
Benchmark::cmpthese(
1_000_000
, {
eval => sub{ eval{ $a->isa('duck')} }
, su => sub { Scalar::Util::blessed $a && $a->isa( 'duck' ) }
}
);
I used This is perl, v5.10.1 (*) built for i486-linux-gnu-thread-multi, and Scalar::Util, 1.21

You can wrap the safety checks in a scalar and then use the scalar as a method to keep things clean:
use Scalar::Util 'blessed';
my $isa = sub {blessed $_[0] and $_[0]->isa($_[1])};
my $obj;
if ($obj->$isa('object')) { ... } # returns false instead of throwing an error
$obj = {};
if ($obj->$isa('object')) { ... } # returns false as well
bless $obj => 'object';
if ($obj->$isa('object')) { say "we got an object" }
Note that $obj->$isa(...) is just a different spelling of $isa->($obj, ...) so no method call actually takes place (which is why it avoids throwing any errors).
And here is some code that will allow you to call isa on anything and then inspect the result (inspired by Axeman's answer):
{package ISA::Helper;
use Scalar::Util;
sub new {
my ($class, $obj, $type) = #_;
my $blessed = Scalar::Util::blessed $obj;
bless {
type => $type,
obj => $obj,
blessed => $blessed,
isa => $blessed && $obj->isa($type)
} => $class
}
sub blessed {$_[0]{blessed}}
sub type {$_[0]{isa}}
sub ref {ref $_[0]{obj}}
sub defined {defined $_[0]{obj}}
use overload fallback => 1,
bool => sub {$_[0]{isa}};
sub explain {
my $self = shift;
$self->type ? "object is a $$self{type}" :
$self->blessed ? "object is a $$self{blessed} not a $$self{type}" :
$self->ref ? "object is a reference, but is not blessed" :
$self->defined ? "object is defined, but not a reference"
: "object is not defined"
}
}
my $isa = sub {ISA::Helper->new(#_)};
By placing the code reference in a scalar, it can be called on anything without error:
my #items = (
undef,
5,
'five',
\'ref',
bless( {} => 'Other::Pkg'),
bless( {} => 'My::Obj'),
);
for (#items) {
if (my $ok = $_->$isa('My::Obj')) {
print 'ok: ', $ok->explain, "\n";
} else {
print 'error: ', $ok->explain, "\n";
}
}
print undef->$isa('anything?')->explain, "\n";
my $obj = bless {} => 'Obj';
print $obj->$isa('Obj'), "\n";
my $ref = {};
if (my $reason = $ref->$isa('Object')) {
say "all is well"
} else {
given ($reason) {
when (not $_->defined) {say "not defined"}
when (not $_->ref) {say "not a reference"}
when (not $_->blessed) {say "not a blessed reference"}
when (not $_->type) {say "not correct type"}
}
}
this prints:
error: object is not defined
error: object is defined, but not a reference
error: object is defined, but not a reference
error: object is a reference, but is not blessed
error: object is a Other::Pkg not a My::Obj
ok: object is a My::Obj
object is not defined
1
not a blessed reference
If anyone thinks this is actually useful, let me know, and I will put it up on CPAN.

Here's an update for 2020. Perl v5.32 has the isa operator, also known as the class infix operator. It handles the case where the left-hand argument is not an object it returns false instead of blowing up:
use v5.32;
if( $something isa 'Animal' ) { ... }

This might sound a little bit harsh to Perl, but neither one of these is ideal. Both cover up the fact that objects are a tack on to Perl. The blessed idiom is wordy and contains more than a couple simple pieces.
blessed( $object ) && object->isa( 'Class' )
I would prefer something more like this:
object_isa( $object, 'Class' )
There is no logical operation to get wrong, and most of the unfit uses will be weeded out by the compiler. (Quotes not closed, no comma, parens not closed, calling object_isa instead...)
It would take undefined scalars, simple scalars (unless they are a classname that is a Class), unblessed references, and blessed references that do not extend 'Class' and tell you that no, they are not Class objects. Unless we want to go the route of autobox-ing everything, we're going to need a function that tells us simply.
Perhaps there might be a third parameter for $how_close, but there could also be something like this:
if ( my $ranking = object_isa( $object, 'Class' )) {
...
}
else {
given ( $ranking ) {
when ( NOT_TYPE ) { ... }
when ( NOT_BLESSED ) { ... }
when ( NOT_REF ) { ... }
when ( NOT_DEFINED ) { ... }
}
}
About the only way I can see that we could return this many unique falses is if $ranking was blessed into a class that overloaded the boolean operator to return false unless the function returned the one value indicating an ISA relationship.
However, it could have a few members: EXACTLY, INHERITS, IMPLEMENTS, AGGREGATES or even MOCKS
I get tired of typing this too:
$object->can( 'DOES' ) && $object->DOES( 'role' )
because I try to implement the future-facing DOES in lesser perls (on the idea that people might frown on my polluting UNIVERSAL) on them.

Related

Seek Perl idiom to check that $self is a class or object

In Perl, I just got bitten by something that looked like the bug below:
package Foo;
sub method {
my $self = shift;
my #args = #_;
...
}
where I called it as a subroutine, not a method:
Foo::method( "arg1", "arg2" );
rather than calling it as a method - in this case, it was a "class method":
Foo->method( "arg1", "arg2" );
Calling Foo::method("arg1","arg2") resulted in "arg1" getting dropped.
Similar considerations can arise with an "object method":
my $object = Foo->new();
$obj->method( "arg1", "arg2" );
Is there a friendly, concise, Perl idiom for checking that the first argument, conventionally called $self, is in fact an object in the class (package), and/or the class/package name?
The best I have come up with is:
package Foo;
sub method {
my $self = ($_[0]->isa(__PACKAGE__) ? shift #_ : die "...error message...";
my #args = #_;
...
}
which is not much more concise than
package Foo;
sub method {
my $self = shift;
die "...error message..." if $self->isa(__PACKAGE__);
my #args = #_;
...
}
or
package Foo;
use Carp::Assert;
sub method {
my $self = shift;
assert($self->isa(__PACKAGE__));
my #args = #_;
...
}
Notes:
I know about Perl signatures, but dislike using experimental features.
I know about use attributes and :method. Is that the best way to go? Similar concerns about "evolving" features.
I know about Moose - but I don't think that Moose enforces this. (Did I miss anything.)
The problem with Perl is that there are so many ways to do something.
The best answer is to not mix functions and methods in a single package. "Hybrid modules", as they're known, are problematic. Everything which you might want to make a function should instead be a class method call.
There should be little need to fully qualify a function call in day-to-day programming.
The most concise way is to use Moops which is the new way to use Moose with syntax-sugar.
use Moops;
class Foo {
method something() {
print("something called\n");
}
}
Foo->new->something();
Foo::something();
# something called
# Invocant $self is required at /Users/schwern/tmp/test.plx line 10.
Moops is marked as unstable, but that's the interface, not the signatures themselves. Signatures have been around and usable in production for a long time, longer than they've been built in. More worrying is there hasn't been a release in over a year, however the author writes good stuff. Your call.
Otherwise, like with anything else, write a function.
use Carp;
use Scalar::Util qw(blessed);
sub check_invocant {
my $thing = shift;
my $caller = caller;
if( !defined $thing ) {
croak "The invocant is not defined";
}
elsif( !ref $thing ) {
croak "The invocant is not a reference";
}
elsif( !blessed $thing ) {
croak "The invocant is not an object";
}
elsif( !$thing->isa($caller) ) {
croak "The invocant is not a subclass of $caller";
}
return $thing;
}
Since this returns the invocant and handles the exception for you it can be used very concisely.
package Foo;
sub method {
my $self = ::check_invocant(shift);
...
}
I'll add to what Schwern has written to say that you could also take a look at Safe::Isa, which lets you safely call isa on something which you cannot be sure is an object.
I'm going to try to follow the advice of #Schwern and "not mix functions and methods in a single package". That said, here's an example using the fun method approach from Function::Parameters. The example is of course contrived and a bit awkward, but it illustrates the idea.
Function::Parameters requires a compiler version of at least perl5.14. It's still perl (and XS) so it will not magically make your code "strongly typed". But, with attributes and type constraints via Type::Tiny, you can separate your methods and functions by more than name only. Even just using different names for different types of subroutines - fun and method by default - can be really helpful.
Using the ':strict' keyword and/or default function/method "types" (fun => { ... } and method => { ... } below, as well as others such as method_lax) obviates the need for passing values to settings when the module is imported, so the code below can be made shorter.
use v5.22;
package My::Package {
use DDP;
use attributes 'get';
use Function::Parameters {
fun => { strict => 1, } ,
method => { strict => 1,
invocant => 1,
shift => '$class',
attributes => ':method',} ,
} ;
fun func_test ( # ) {
warn "must be called as a function"
if $_[0] eq __PACKAGE__ && get(__SUB__) ne "method";
print "args = ", np #_ ;
}
method meth_test ( # ) {
warn "must be called as a method"
unless $class eq __PACKAGE__ && get(__SUB__) eq "method";
say "\$class = $class" if length $class ;
say "args = ", np #_ ;
}
}
say "\nCalling meth_test as method:";
My::Package->meth_test( ["foo", "bar"] );
say "\nCalling meth_test as function:";
My::Package::meth_test( ["foo", "bar"] );
say "\nCalling func_test as a function:";
My::Package::func_test( qw/baz fuz/ );
say "\nCalling func_test as a method:";
My::Package->func_test( qw/baz fuz/ );
Output:
Calling meth_test as method:
$class = My::Package
args = [
[0] [
[0] "foo",
[1] "bar"
]
]
Calling meth_test as function:
must be called as a method at FunctionParameters-PackageCheck-SO.pl line 24.
$class = ARRAY(0x801cfa330)
args = []
Calling func_test as a function:
args = [
[0] "baz",
[1] "fuz"
]
Calling func_test as a method:
must be called as a function at FunctionParameters-PackageCheck-SO.pl line 17.
args = [
[0] "My::Package",
[1] "baz",
[2] "fuz"
]

Check if a subroutine is being used as an lvalue or an rvalue in Perl

I'm writing some code where I am using a subroutine as both an lvalue and an rvalue to read and write database values. The problem is, I want it to react differently based on whether it is being used as an lvalue or an rvalue.
I want the subroutine to write to the database when it is used as an lvalue, and read from the database when it is used as an rvalue.
Example:
# Write some data
$database->record_name($subscript) = $value;
# Read some data
my $value = $database->record_name($subscript);
The only way I can think of the make this work is to find a way for the subroutine to recognize whether it is being used as an lvalue or an rvalue and react differently for each case.
Is there a way to do this?
Deciding how to behave on whether it was called as an lvalue or not is a bad idea since foo(record_name(...)) would call it as an lvalue.
Instead, you should decide how to behave on whether it is used as an lvalue or not.
You can do that by returning a magical value.
use Variable::Magic qw( cast wizard );
my $wiz = wizard(
data => sub { shift; \#_ },
get => sub { my ($ref, $args) = #_; $$ref = get_record_name(#$args); },
set => sub { my ($ref, $args) = #_; set_record_name(#$args, $$ref); },
);
sub record_name :lvalue {
cast(my $rv, $wiz, #_);
return $rv;
}
A little test:
use Data::Dumper;
sub get_record_name { print("get: #_\n"); return "val"; }
sub set_record_name { print("set: #_\n"); }
my $x = record_name("abc", "def"); # Called as rvalue
record_name("abc", "def") = "xyz"; # Called as lvalue. Used as lvalue.
my $y_ref = \record_name("abc", "def"); # Called as lvalue.
my $y = $$y_ref; # Used as rvalue.
$$y_ref = "xyz"; # Used as lvalue.
Output:
get: abc def
set: abc def xyz
get: abc def
set: abc def xyz
After seeing this, you've surely learned that you should abandon the idea of using an lvalue sub. It's possible to hide all that complexity (such as by using sentinel), but the complexity remains. The fanciness is not worth all the complexity. Use separate setters and getters or use an accessor whose role is based on the number of parameters passed to it ($s=acc(); vs acc($s)) instead.
For this situation you might like to try my Sentinel module.
It provides a function you can use in the accessor, to turn it into a more get/set style approach. E.g. you could
use Sentinel qw( sentinel );
sub get_record_name { ... }
sub set_record_name { ... }
sub record_name
{
sentinel get => \&get_record_name,
set => \&set_record_name,
obj => shift;
}
At this point, the following pairs of lines of code are equivalent
$name = $record->record_name;
$name = $record->get_record_name;
$record->record_name = $new_name;
$record->set_record_name( $new_name );
Of course, if you're not needing to provide the specific get_ and set_ prefixed versions of the methods as well, you could inline them as closures.
See the module docs also for further ideas.
In my opinion, lvalue subroutines in Perl were a dumb idea. Just support ->record_name($subscript, $value) as a setter and ->record_name($subscript) as a getter.
That said, you can use the Want module, like this
use Want;
sub record_name:lvalue {
if ( want('LVALUE') ) {
...
}
else {
...
}
}
though that will also treat this as an LVALUE:
foo( $database->record_name($subscript) );
If you want only assignment statements to be treated specially, use want('ASSIGN') instead.

idioms for named parameters in object constructors in Perl

In Perl, if I want to use named parameters in an object constructor, my code seems a bit clumsy if I wish to have some validation.
sub new {
my $class = shift;
my $self = {};
my %args = #_;
foreach my $argname (keys %args) {
if ($argname eq 'FOO') { $self->{$argname} = $args{$argname}; }
elsif ($argname eq 'BAR') { $self->{$argname} = $args{$argname}; }
elsif ($argname eq 'BAZ') { $self->{$argname} = $args{$argname}; }
…
else { die "illegal argument $argname\n"; }
}
bless $self;
return $self;
}
Firstly it seems a bit clumsy to have a temporary hash (%args). Secondly the whole if chain seems verbose and tedious.
The latter can be simplified to
if ('-FOO-BAR-BAZ-'=~m/-$argname-/) { $self->{$argname} = $args{$argname} }
else { die "..."; }
but I imagine this can be improved.
If I need to check values, the if … elsif chain is still necessary?
I've searched a little but cannot find a better idiom. Is there one (other than using a Perl OO framework of some sort)
I found myself constantly writing unnecessary code which checked the given parameters. But then I discovered Params::Validate. It is easy to use and if the validation fails it provides very clear and user-friendly error messages. Covering all possible combinations of parameters and their error messages is a tedious task. I prefer this way instead:
use Params::Validate qw/:all/;
sub new {
my $pkg = shift;
validate(
#_, {
foo => { type => SCALAR | ARRAYREF },
bar => { type => SCALAR, optional => 1},
baz => { type => ARRAYREF, default => ['value'] },
quux => { isa => 'CGI' }
}
);
return bless { #_ }, $pkg;
}
And later this code
MyApp::Something->new(
foo => 123,
bbr => 'typo',
quux => CGI->new()
);
becomes:
The following parameter was passed in the call to MyApp::Something::new but was not listed in the validation options: bbr
at test.pl line 14.
MyApp::Something::new(undef, 'foo', 123, 'bbr', 'typo', 'quux', 'CGI=HASH(0x7fd4fa1857e0)') called at test.pl line 27
You can use smart matching
my #validkeys = qw(FOO BAR BAZ);
if ($argname ~~ #validkeys) { # smart matching
$self->{$argname} = $args{$argname};
} else { die ... }
If you don't like the obscurity of the smart match operator you can swing together a regex
my $rx = '^' . join("|", #validkeys) . '$';
if ($argname =~ /$rx/) { ...
for validation you can define a hash of all legal argument and then just test, if the keys are in it or not
for example:
my %legal = ('FOO' => 1, 'BAR' => 1, 'BAZ' => 1);
my %args = #_;
foreach my $argname (keys %args) {
if(exists $legal{$argname}) { $self->{$argname} = $args{$argname}; }
else { die "illegal argument $argname\n"; }
}
about the clumsyness: well that's the to do it in perl
it can use hashes efficiently and the hash literals are readable
Warning! Untested code.
Check for valid keys.
die "invalid args" if grep { ! /^FOO|BAR|BAZ$/ } keys %args;
Store %args.
$self->{$_} = $args{$_} foreach(keys %args);
For completeness I'm adding this answer (to my own question) describing what I'm actually going to do, which is based on elements from several answers.
sub new {
my $package = shift;
# 1. validate argument names
my %args = #_;
my $valid = '^FOO|BAR|BAZ$';
for (keys %args) { die "invalid arg $_\n" unless /$valid/; }
# 2. construct instance from arguments
return bless { #_ };
}
I've accepted Sebastian's answer although I'm not using Params::Validate yet.
Notes:
I'm deploying to a server that has Perl 5.8 (really) but not Params::Validate. I have reasons for not yet pushing for the upgrades to 5.10.x etc.
For my specific circumstance the above strikes a good balance between brevity and readability. I can later add more validation without too much refactoring.
This compensates for one of the advantages of a getter/setter or accessor style methods for setting parameters (compiler catches typos in parameter name as that is the method name) whilst being more concise.
For other people the above will not apply, so I have accepted Sebastian's answer which I feel is the best one in general (YMMV).

Make the Moose constructor ignore undef arguments

A hashtable is the typical initializer for your Perl objects. Now your input is unreliable in that you don't know whether for any given key there'll be a defined value, nor whether the key is there at all. Now you want to feed such unreliable input into Moose objects, and while absent keys are perfectly okay you do want to get rid of the undefined values so you don't end up with an object full of undefined attributes.
You could certainly take great care when instantiating objects and filter out the undefined values. But let's say you want to install that filter in your constructor because then it is in one place. You want the constructor to ignore undefined values, but not to die on encountering them.
For accessor methods, you can use around around to prevent the attribute to be set to undef. But those method modifiers aren't called for the constructor, only for accessors. Is there a similar facility in Moose to achieve the same effect for the c'tor, i.e. to preclude any undef attributes from being accepted?
Note that the Moose Any type will create the hash key in the object if the attribute is undef. I don't want that because I want %$self not to contain any undef values.
Here's some testing I did:
package Gurke;
use Moose;
use Data::Dumper;
has color => is => 'rw', isa => 'Str', default => 'green';
has length => is => 'rw', isa => 'Num';
has appeal => is => 'rw', isa => 'Any';
around color => sub {
# print STDERR Dumper \#_;
my $orig = shift;
my $self = shift;
return $self->$orig unless #_;
return unless defined $_[0];
return $self->$orig( #_ );
};
package main;
use Test::More;
use Test::Exception;
my $gu = Gurke->new;
isa_ok $gu, 'Gurke';
diag explain $gu;
ok ! exists $gu->{length}, 'attribute not passed, so not set';
diag q(attempt to set color to undef - we don't want it to succeed);
ok ! defined $gu->color( undef ), 'returns undef';
is $gu->color, 'green', 'value unchanged';
diag q(passing undef in the constructor will make it die);
dies_ok { Gurke->new( color => undef ) }
'around does not work for the constructor!';
lives_ok { $gu = Gurke->new( appeal => undef ) } 'anything goes';
diag explain $gu;
diag q(... but creates the undef hash key, which is not what I want);
done_testing;
This is exactly what MooseX::UndefTolerant does. If you make your class immutable, it will be much faster than writing your own BUILDARGS method, as the code is inlined into the generated constructor.
Just provide your own BUILDARGS subroutine.
package Gurke;
...
around 'BUILDARGS' => sub{
my($orig,$self,#params) = #_;
my $params;
if( #params == 1 ){
($params) = #params;
}else{
$params = { #params };
}
for my $key ( keys %$params ){
delete $params->{$key} unless defined $params->{$key};
}
$self->$orig($params);
};
I realize that it is somewhat a duplicated effort, but you can hook ctor with BUILDARGS:
around BUILDARGS => sub {
my $orig = shift;
my $class = shift;
my %params = ref $_[0] ? %{$_[0]} : #_;
return $class->$orig(
map { $_ => $params{$_} }
grep { defined $params{$_} }
keys %params
);
};
Edit: Edited to support even the reference passed to ctor.
While the example given clarifies that the question is inspired by a desire to handle undef attributes passed to a constructor, the question itself additionally implies the case of passing only undef to the constructor, which is something I've encountered and wanted to solve.
E.g., Class->new(undef).
I like bvr's BUILDARGS answer. It can be extended to handle the case of passing an undef value instead of a hashref as the lone argument to a constructor:
around BUILDARGS => sub {
my $orig = shift;
my $class = shift;
my %params = defined $_[0] ? ref $_[0] ? %{$_[0]} : #_ : ();
return $class->$orig(
map { $_ => $params{$_} }
grep { defined $params{$_} }
keys %params
);
};
MooseX::UndefTolerant does not appear to support this case.

Using a variable as a method name in Perl

I have a perl script (simplified) like so:
my $dh = Stats::Datahandler->new(); ### homebrew module
my %url_map = (
'/(article|blog)/' => \$dh->articleDataHandler,
'/video/' => \$dh->nullDataHandler,
);
Essentially, I'm going to loop through %url_map, and if the current URL matches a key, I want to call the function pointed to by the value of that key:
foreach my $key (keys %url_map) {
if ($url =~ m{$key}) {
$url_map{$key}($url, $visits, $idsite);
$mapped = 1;
last;
}
}
But I'm getting the message:
Can't use string ("/article/") as a subroutine ref while "strict refs" in use at ./test.pl line 236.
Line 236 happens to be the line $url_map{$key}($url, $visits, $idsite);.
I've done similar things in the past, but I'm usually doing it without parameters to the function, and without using a module.
Since this is being answered here despite being a dup, I may as well post the right answer:
What you need to do is store a code reference as the values in your hash. To get a code reference to a method, you can use the UNIVERSAL::can method of all objects. However, this is not enough as the method needs to be passed an invocant. So it is clearest to skip ->can and just write it this way:
my %url_map = (
'/(article|blog)/' => sub {$dh->articleDataHandler(#_)},
'/video/' => sub {$dh->nullDataHandler(#_)},
);
This technique will store code references in the hash that when called with arguments, will in turn call the appropriate methods with those arguments.
This answer omits an important consideration, and that is making sure that caller works correctly in the methods. If you need this, please see the question I linked to above:
How to take code reference to constructor?
You're overthinking the problem. Figure out the string between the two forward slashes, then look up the method name (not reference) in a hash. You can use a scalar variable as a method name in Perl; the value becomes the method you actually call:
%url_map = (
'foo' => 'foo_method',
);
my( $type ) = $url =~ m|\A/(.*?)/|;
my $method = $url_map{$type} or die '...';
$dh->$method( #args );
Try to get rid of any loops where most of the iterations are useless to you. :)
my previous answer, which I don't like even though it's closer to the problem
You can get a reference to a method on a particular object with can (unless you've implemented it yourself to do otherwise):
my $dh = Stats::Datahandler->new(); ### homebrew module
my %url_map = (
'/(article|blog)/' => $dh->can( 'articleDataHandler' ),
'/video/' => $dh->can( 'nullDataHandler' ),
);
The way you have calls the method and takes a reference to the result. That's not what you want for deferred action.
Now, once you have that, you call it as a normal subroutine dereference, not a method call. It already knows its object:
BEGIN {
package Foo;
sub new { bless {}, $_[0] }
sub cat { print "cat is $_[0]!\n"; }
sub dog { print "dog is $_[0]!\n"; }
}
my $foo = Foo->new;
my %hash = (
'cat' => $foo->can( 'cat' ),
'dog' => $foo->can( 'dog' ),
);
my #tries = qw( cat dog catbird dogberg dogberry );
foreach my $try ( #tries ) {
print "Trying $try\n";
foreach my $key ( keys %hash ) {
print "\tTrying $key\n";
if ($try =~ m{$key}) {
$hash{$key}->($try);
last;
}
}
}
The best way to handle this is to wrap your method calls in an anonymous subroutine, which you can invoke later. You can also use the qr operator to store proper regexes to avoid the awkwardness of interpolating patterns into things. For example,
my #url_map = (
{ regex => qr{/(article|blog)/},
method => sub { $dh->articleDataHandler }
},
{ regex => qr{/video/},
method => sub { $dh->nullDataHandler }
}
);
Then run through it like this:
foreach my $map( #url_map ) {
if ( $url =~ $map->{regex} ) {
$map->{method}->();
$mapped = 1;
last;
}
}
This approach uses an array of hashes rather than a flat hash, so each regex can be associated with an anonymous sub ref that contains the code to execute. The ->() syntax dereferences the sub ref and invokes it. You can also pass parameters to the sub ref and they'll be visible in #_ within the sub's block. You can use this to invoke the method with parameters if you want.