Another strike against AUTOLOAD
When you're using Redis, you can issue the select index command to use a different database (think "namespace"). By default Redis provides you with 16 different databases numbered 0 to 15 (you can configure it for more). All keys and values will be separate from any other keys and values in other databases. So how do you know which database you're in? As it turns out, Redis doesn't offer a command for directly querying that information.
In our production code I needed to know which database that Redis was connected to. As it turns out, this is a bit tricky. You can get this information from Redis if you have:
- named your Redis connection
- issued the client-list command
- found the connection with the correct name
and written a parser for this:
addr=127.0.0.1:36188 fd=5 name= age=9 idle=0 flags=N db=2 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
Oops. We forgot to name our connection, so we can't uniquely identify this one.
Instead, of doing things the "right" way, I decided that YAGNI applied. In a subclass of Redis, I was trying to write the following code because it seemed simpler:
has 'database' => (
is => 'rw',
isa => 'Int',
default => 0,
);
after 'select' => sub {
my ( $self, $database ) = @_;
$self->database($database);
};
That failed. Then I remembered that the excellent Redis module by Pedro Melo uses AUTOLOAD
for the Redis command. I can't apply a method modifier to a method that does not yet exist.
I honestly can't fault him for this. It means that when a new version of Redis is released, the code just magically works.
So I tried this:
sub select {
my ( $self, $database ) = @_;
$self->next::method($database);
$self->database($database);
}
Nope. Same issue. I finally relied on SUPER::select
. That worked fine, though it limits my choices due to the well-known SUPER bug (SUPER::$method
calls the parent of the class the code is compiled into, not the parent of the invocant).
The following shows the issue:
use Modern::Perl;
{
package Parent;
sub AUTOLOAD {
our $AUTOLOAD;
my $command = $AUTOLOAD;
$command =~ s/.*://;
return "command was $command";
}
}
{
package Child;
our @ISA = 'Parent';
sub new { bless {} => shift }
sub foo { return shift->SUPER::foo }
sub bar { return shift->next::method }
}
my $child = Child->new;
say $child->foo;
say $child->bar;
And the output:
command was foo
No next::method 'bar' found for Child at ...
This was pretty easy for me to find and fix, but I can easily imagine for many who are new to Perl, this would have been completely mystifying.
You might be able to do this more easily if you switched to Redis::Client (shameless plug.) No AUTOLOAD, and easier to extend.
I don't see this as a strike against AUTOLOAD but a consequence of using a dynamic language. You could have the same "method doesn't exist yet" problem without AUTOLOAD. The problem I see is that Smalltalk's omniscience has invaded Moose, but that design doesn't really work when you can't know everything. It's why my Perl class browser project ultimately failed.
I don't dispute that this is mystifying to new Perl users, but I don't see that as a problem. Power and pro-level tools tend to mystify new users, but when they become experienced users, they have a lot of, well, power.
And, it's not even that hard to get help. Google and Stackoverflow solve all sorts of problems that mystify me. Indeed, my entire career dealing with code has been learning to deal with mystification. :)
Where does the next::method thing come from? I've never seen that before.
Actually, what you found was a bug in Redis, congrats! :)
Redis.pm must keep track of the current database to deal properly with reconnect. I should have the time to patch this in soon, but if you really need it, you can send me a pull request, I'll cut a release immediately.
Bye,
Nathan, see perldoc mro.