Here are some drafy docs for subclassing (partly based on a patch
sent by Stephen Clouse).

Comments welcome.

I'll add some words about what you could do, and should not do, by
subclassing.  And about multiple levels of subclassing.

Tim.


=head2 Subclassing the DBI
 
DBI can be subclassed and extended just like any other object
oriented module.  Before we talk about how to do that, it's important
to be clear about how the DBI classes work together.
 
By default C<$dbh = DBI->E<gt>C<connect(...)> returns a $dbh blessed
into the C<DBI::db> class.  And the C<$dbh->E<gt>C<prepare> method
returns an $sth blessed into the C<DBI::st> class (actually it
simply changes the last for characters of the calling handle class
to be C<::st>).
 
The leading 'C<DBI>' is known as the 'root class' and the extra
'C<::db>' or 'C<::st>' are the 'handle type suffixes'. If you want
to subclass the DBI you'll need to put your overriding methods into
the appropriate classes.  For example, if you want to use a root class
of C<MySubDBI> and override the do(), prepare() and execute() methods,
then your do() and prepare() methods should be in the C<MySubDBI::db>
class and the execute() method should be in the C<MySubDBI::st> class.

To setup the inheritance hierarchy the @ISA variable in C<MySubDBI::db>
should contain C<DBI::db> and the @ISA variable in C<MySubDBI::st>
should contain C<DBI::st>.  The C<MySubDBI> root class itself isn't
currently used and, apart from setting @ISA to include C<DBI>, it
should be left empty.
 
So, having put your overriding methods into the right classes, and
setup the inheritance hierarchy, how do you get the DBI to use them?
You have two choices, either a static method call using the name
of your subclass:
 
  $dbh = MySubDBI->connect(...);
 
or specifying a C<RootClass> attribute:

  $dbh = DBI->connect(..., { RootClass => 'MySubDBI' });
 
The only difference between the two is that using an explicit
RootClass attribute will make the DBI automatically attempt to load
a module by that name (and not complain if such a module can't be
found). If both forms are used then the attribute takes precedence.
 
Here's a brief example of a DBI subclass.  A more thorough example
can be found in t/subclass.t in the DBI distribution.
 
  package MySubDBI;
 
  use strict;
 
  use DBI;
  use vars qw(@ISA);
  @ISA = qw(DBI);
 
  package MySubDBI::db;
  use vars qw(@ISA);
  @ISA = qw(DBI::db);
 
  sub prepare {
    my ($dbh, @args) = @_;
    my $sth = $dbh->SUPER::prepare(@args)
        or return;
    $sth->{private_mysubdbi_foo} = 'bar';
    return $sth;
  }
 
  package MySubDBI::st;
  use vars qw(@ISA);
  @ISA = qw(DBI::st);
 
  sub fetch {
    my ($sth, @args) = @_;
    my $row = $sth->SUPER::fetch(@args)
        or return;
    do_something_magical_with_row_data($row)
        or return $sth->set_error(1234, "The magic failed");
    return $row;
  }

You can stash private class data in a handle and carry it around
via C<$h->E<gt>C<{private_..._*}>.  See the entry under L</ATTRIBUTES
COMMON TO ALL HANDLES> for info and important caveats.
 
When calling a supermethod that returns a handle, be careful to
check the return value before trying to do other things with it in
your overridden method. This is especially important if you want
to set a hash attribute on the handle, as Perl's autovivification
will bite you by (in)conveniently creating an unblessed hashref,
which your method will then return with occasionally baffling results
later on.
 
It's best to check right after the supermethod call and return undef
immediately on error, just like DBI would and just like the example
above.

If your method needs to record an error it should call the set_error()
method with the error code and error string, as shown in the example
above. The error code and error string will be recorded in the
handle and available via C<$h->E<gt>C<err> and C<$DBI::errstr> etc.
The set_error() method always returns an undef or empty list as
approriate. Since your method should nearly always return an undef
or empty list as soon as an error is detected it's handy to simply
return what set_error() returns, as shown in the example above.
 
If the handle has C<RaiseError>, C<PrintError>, or C<HandleError>
etc. set then the set_error() method will honour them. This means
that if C<RaiseError> is set then set_error() won't return in the
normal way but will 'throw an exception' that can be caught with
an C<eval> block.

=cut

----- End forwarded message -----

Reply via email to