Slash Boxes
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Alias (5735)

  (email not shown publicly)

Journal of Alias (5735)

Tuesday May 29, 2007
11:52 AM

Ask - Class Tree + Driver API ?

[ #33365 ]

Class trees are great. If your problem is complex and suitably hierarchal they provide the conceptual skeleton you need to solve the problem completely. I use class trees a lot.

Driver APIs are also great. They let you wrap an abstracted simple API over the top of a concept that has highly diverse implementations and hide the implementation details away.

They also let you reduce design risk. Introducing a driver API lets you drop in an alternative implementation at a later date with little to no pain, and even have the new (better, brighter, cooler) implementation co-exist with the old one. So upgrades can be rolled out across a subset of current users. I use driver APIs a lot.

Of course, if you could take both things that are themselves great, and COMBINE the two into some sort of "Driver Class Tree API" it should be stupendously awesome!

Except it isn't. In fact, it sucks rather hard. And I'm not sure how to make it suck less.

For example, I have a non-CPAN class tree for modeling SQL queries. It's code-generated for each application with the appropriate code to generate the SQL for the database for that project and does some other magical stuff I can only do easily because I'm using code-gen.

Suffice to say I'm really happy with it, since it allows things like this magically across arbitrary table structures.

my $query = MyApp::Entity::Person->select;
$query->condition( 'age', '==', 30 );
$query->condition( '', '==', 'Fido' );
$query->orderby( '+name' );
my $adam = $query->first( $dbh );

Now, much like a number of other CPAN modules that originated in my code-gen system, I'd love to abstract it out to CPAN one day.

But this class tree (there's about 15 classes) only works for one database. The "agnostic" version that gets generated has stuff like "if ( postgres ) { ... }" all through it, which obviously doesn't scale.

But how do I make the class structure work when 13 of those 15 classes may need to be subclasses/modified by the driver?

How do you typically add "driverness" to a class tree without resorting to ugly things like:

sub condition {
    my $self = shift;
    my $condition_class = $self->driver_class('Condition');
    my $condition = $condition_class->new( @_ );
    push @{$self->{conditions}}, $condition;
    return 1;

Anyone got a better solution?

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
More | Login | Reply
Loading... please wait.
  • Hi Alias,

    I don't know if I understood completely what you are trying to accomplish, but I think I have developed a similar system, that is used to access different data sources (txt, ftp, http, sql, AS/400, ...) with a single interface. Sql class in particular has the behaviour you mention, in that it supports Postgres, Mysql, Oracle, DB2, Sybase, Informix, ...

    In my case, I have a "Driver" class with many subclasses like Pg, Oracle, Sybase, MSSQL, ... that are embedded in a $self->{driver} membe

  • I'm probably over-simplifying your problem, but I think that the solution adopted by DBIx::Class is a nice brain fit. Under Catalyst, I would say something like:

    my $query = $c->model('Entity::Person');
    $query = $query->search({age => ['==', 30]});
    $query = $query->search({mumble...  => ['==', 'Fido']});
    $query = $query->search(undef, {order_by => 'name ASC'});
    my $adam = $query->first;

    where I don't know how to write the "mumble" part off the top of my head, but it's probably acc

    • Unfortunately, yes this oversimplifies the problem.

      It solves the problem by getting rid of the class tree altogether and using Perl structures.

      Here's another example of the sort of stuff I'm doing.

      # We need a certain type of person
      my $query = MyApp::Entity::Person->Fetch;
      $query->condition('OptimizedFor', '==', 'fun');
                      MyApp::SQL::Clause->new('Lame', 'is null'),
      • Ack, my "less than" condition in the nested structure broke it :(

        But you get the point.