Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Class::DBI Builder

by BigLug (Chaplain)
on May 27, 2005 at 05:09 UTC ( [id://460946]=sourcecode: print w/replies, xml ) Need Help??
Category: Database Interface
Author/Contact Info BigLug
Description: This code is a utility I whipped up to turn my MySQL database into a Class::DBI module.

Unlike other solutions like Class::DBI::AutoLoader and Class::DBI::Loader this is designed to run at development time rather than run time. Unlike Class::DBI::Schema2Code it doesn't require other modules that check your database at runtime.

However, like Class::DBI::Schema2Code, it does have expectations on your data structure, but as it's code rather than a module, you're expected to tinker.

We assume that your primary key field is called 'id' and that your foreign keys are in the form 'tablename_id'. We also assume that your primary key is the first field in your table. (yes I know there's ways to discover it ...)

Using these assumptions we set up both has_a and has_many relationships for each table.

As I'm a huge DateTime fan, I inflate all my date and time fields into datetime objects. The database I'm working with has a lot of legacy epoch date/time fields in it, so I also try to find them.

THIS IS NOT A COMPLETE SOLUTION
You'll note that the other thing we output is a die message telling you to check the code carefully! NEVER use this code without reading the resulting code. You have been warned.

I've also created my own plural/singular routines rather than use Lingua::EN::Inflect as in my case I have mixed plurals and singulars on column names (legacy schema!) when I call PL('cats') I want 'cats'.

I hope you find this code useful. I do.

The reason I've written it is because creating the files that run Class::DBI, I do the same thing over and over again. I'm sure I'm not the only one who has a 'standard' way of creating a database, that should easily be convertable.

Let me know if you use it, and feel free to offer suggestions!

usage:

perl generator.pl > Module.pm
#!/usr/bin/perl

use DBI;
use Data::Dumper;
#use Lingua::EN::Inflect qw/PL/;

my $basename = 'My::Data';
my $dsn  = 'dbi:mysql:database:127.0.0.1';
my $user = 'user';
my $pass = 'pass';

my $dbh  = DBI -> connect( $dsn, $user, $pass );

my $table_sth = $dbh->table_info('%','','');


print "package $basename;\n\n";
print "use base 'Class::DBI';\n\n";
print "use DateTime;\nuse DateTime::Format::ISO8601;\n\n";
print "$basename->connection('$dsn', '$user', '$pass');\n\n\n\n";


foreach $table ( @{$table_sth->fetchall_arrayref()} ) {
    my ($cat, $schema, $table_name, $type, $remarks) = @{$table};


    $table{$table_name}{camel_name} = "${basename}::".SI(CamelCaps($ta
+ble_name));
    $table{$table_name}{type}       = $type;
    $table{$table_name}{remarks}    = $remarks;

    my $column_sth = $dbh->column_info($cat, $schema, $table_name, '%'
+);

    foreach $column ( sort { $a->[16] <=> $b->[16] } @{$column_sth->fe
+tchall_arrayref } ) {
        my (undef, undef, undef, $col_name, $data_type, $data_type_nam
+e,
            $col_size, $buffer_len, $decimals, undef, $nullable, $rema
+rks,
            $default, $sql_data_type, $sql_data_subtype, $col_char_siz
+e, $order,
            undef, undef, undef,undef,undef,undef,undef,undef,undef,un
+def,undef,
            undef,undef,undef,undef,undef,undef,undef,undef,$max_card,
+$dtd_ident,undef
        ) = @{$column};

        push(@{$table{$table_name}{cols}}, {
            name => $col_name,
            remark => $remark || $dtd_ident,
            type => $dtd_ident,
        });
    }


}

foreach $table (keys %table) {

    print div();
    print "# $table{$table}{remarks}\n" if $table{$table}{remarks};
    print 'package '.$table{$table}{camel_name}.";\n";
    print div();
    print "use base '$basename';\n\n";
    print $table{$table}{camel_name}."->table( '$table' );\n";
    print $table{$table}{camel_name}."->columns(\n\tAll => qw/\n\t\t".
+ join("\n\t\t", map{$_->{name}}@{$table{$table}{cols}}) . "\n\t/)\n);
+\n";
    print "die('You forgot to check the definition for the '$table' ta
+ble. Or you forgot to remove this message!');\n";

    # Check all columns for foreign key looking fields. eg ${tablename
+}_id
    my $has_a = 0;
    foreach my $col ( @{$table{$table}{cols}} ) {
        if ($col->{name}=~/^(.+)_id$/) {
            my $rel_col = $1;
            my $class =
                ($table{$rel_col})     ? $table{$rel_col}{camel_name} 
+    :
                ($table{PL($rel_col)}) ? $table{PL($rel_col)}{camel_na
+me} : '';
            next unless $class;
            print $table{$table}{camel_name}."->has_a( ".$col->{name}.
+" => '$class' );\n";
        } elsif ($col->{type} =~ /int\(1[01]\)/ and $col->{name} =~ /(
+start|end|expir|received|delivered|quarantined)/) {
            print $table{$table}{camel_name}."->has_a(\n";
            print "\t$col->{name} => 'DateTime',\n";
            print "\tinflate => sub { DateTime->from_epoch( shift ) },
+\n";
            print "\tdeflate => 'epoch'\n";
            print ");\n";
        } elsif ($col->{type} =~ /(date|time)/) {
            print $table{$table}{camel_name}."->has_a(\n";
            print "\t$col->{name} => 'DateTime',\n";
            print "\tinflate => sub { DateTime::Format::ISO8601->parse
+_datetime( shift ) },\n";
            print qq|\tdeflate => "strftime('%H:%M:%S')"\n|;
            print ");\n";
        }
        $has_a++;
    }
    print "\n" if $has_a;

    # Check everywhere else for foreign keys to our table
    my $table_singular = SI($table);
    my $has_many = 0;
    foreach my $tbl ( grep {$_ ne $table} keys %table ) {
        foreach my $col ( @{$table{$tbl}{cols}}) {
            #print "Looking for ${table_singular}_id : $col->{name}\n"
+;
            next unless $col->{name} eq "${table_singular}_id";
            print $table{$table}{camel_name}."->has_many( ". CamelCaps
+(PL($tbl)) ." => '$table{$tbl}{camel_name}' );\n";
        }
    }
    print "\n" if $has_many;
    print "\n";

}




sub CamelCaps {
    my $string = shift;
    return join('', map { ucfirst lc $_ } split(/[^a-z]/i,$string));
}

sub div {
    return '#' . ('-' x 71) . "\n";
}

sub SI {
    # Simple singularifier
    my $plural = shift;
    $plural =~ s/ces$/x/i and return $plural;
    $plural =~ s/ies$/y/i and return $plural;
    $plural =~ s/s$//i and return $plural;
    return $plural
}

sub PL {
    # Simple pluralizer
    my $singular = shift;
    $singular =~ s/y$/ies/i and return $singular;
    $singular =~ s/x$/ces/i and return $singular;
    $singular .= 's' unless $singular =~ /s$/;
    return $singular
}
Replies are listed 'Best First'.
Re: Class::DBI Builder
by perrin (Chancellor) on May 27, 2005 at 21:43 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://460946]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (6)
As of 2024-04-20 00:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found