radiantmatrix has asked for the wisdom of the Perl Monks concerning the following question:
I am writing a wrapper class that wraps DBI and adds additional functionality. I'd like it to have all the methods of a database handle; since those aren't directly in the DBI module, inheritance seems like the wrong approach (though I'd be happy to learn otherwise).
So, I've been cutting my teeth on AUTOLOAD. Here's a super-simple chunk of code:
package TestModule; require DBI; use AutoLoader; use base 'Class::Base'; sub AUTOLOAD { my $op = $AUTOLOAD; $op =~ s/^.*:://; if ( DBI::db->can($op) ) { eval "sub $op { my \$self=shift; $self->{DBH}->$op(\@_); }"; } else { eval "sub $op { return shift->error('Cannot autoload $op'); }" +; } } sub new { # new ( $dsn[, $user[, $pass]] ) my $self = shift; return undef unless @_; my @args = @_; my $obj = {}; eval { $obj->{DBH} = DBI->connect(@args); }; if ($@) { return $self->error("Unable to connect to DB: $@"); } bless $obj, $self; }
However, the following fails (I connect to a real DB for this test, and the SQL is valid):
use Test::More tests => 9; # ... snipped setup of $obj = new & etc. my $sth = $obj->prepare('SELECT * FROM test_table'); can_ok($obj, 'prepare'); #succeeds! # this fails: isnt ($sth, undef, 'prepare creates statement handle');
This suggests that AUTOLOAD's actually creating a sub prepare with stuff in it, but that it isn't working. When I manually create a prepare sub (i.e. bypass AUTOLOAD) with the same content, it works fine.
At this point, I don't even understand where I'm going wrong. Any help with this (including simpler approaches, etc.) would be most welcome. What I want to avoid, though, is having to write a myriad little subs that wrap the equivalent database handle methods.
Also, even if AUTOLOAD isn't the correct solution for this problem, I'd still like to know how to wield it, so any tips in that direction will be welcome regardless.
Thanks in advance!
Update: yosefm weighed in quickly, and with the correct answer! My new AUTOLOAD sub:
sub AUTOLOAD { my $op = $AUTOLOAD; $op =~ s/^.*:://; if ( DBI::db->can($op) ) { eval "sub $op { return shift->{DBH}->$op(\@_); }"; $op->(@_); } else { eval "sub $op { return shift->error('Cannot autoload $op'); }" +; } }
The above had bugs, here's what works today:
# sets up autoload subs that 'inherit' DBI's DBH methods sub AUTOLOAD { my $op = $AUTOLOAD; my $self = shift; $op =~ s/^.*:://; if ( DBI::db->can($op) ) { # create a wrapper for a DBH method eval "sub $op { return shift->{DBH}->$op(\@_); }"; $op->($self, @_); } elsif ( $op =~ /^_/ ) { # return the appropriate attribute $op =~ s/^_//; exists $self->{$op} && return $self->{$op}; return $self->error("Can't autoload for attribute _$op"); } else { return $self->error("Cannot autoload $op"); } }
It helps to actually call the subroutine after creating it... big D'OH for me.
Updates:
|
|---|