moving reusable (useful?) code into schema classes
Allen Day [Fri, 29 Aug 2003 05:38:56 +0000 (05:38 +0000)]
lib/SQL/Translator/Producer/Turnkey.pm
lib/SQL/Translator/Schema/Table.pm

index db3d697..8e07f75 100644 (file)
@@ -1,9 +1,30 @@
+package Turnkey::Package;
+
+use strict;
+use Class::MakeMethods::Template::Hash (
+  new => [ 'new' ],
+  hash => [ qw( many ) ],
+  hash_of_arrays => [ qw( many_via) ],
+  scalar => [ qw( base name order primary_key primary_key_accessor table) ],
+);
+
+
+#  get_set => [ qw(order base name table primary_key primary_key_accessor) ],
+#  new_with_init => 'new',
+#;
+
+sub init {
+}
+
+1;
+
 package SQL::Translator::Producer::Turnkey;
 
 # -------------------------------------------------------------------
-# $Id: Turnkey.pm,v 1.1 2003-08-28 08:51:09 boconnor Exp $
+# $Id: Turnkey.pm,v 1.2 2003-08-29 05:38:56 allenday Exp $
 # -------------------------------------------------------------------
 # Copyright (C) 2003 Allen Day <allenday@ucla.edu>,
+#                    Brian O'Connor <boconnor@ucla.edu>,
 #                    Ying Zhang <zyolive@yahoo.com>
 #
 # This program is free software; you can redistribute it and/or
@@ -23,7 +44,7 @@ package SQL::Translator::Producer::Turnkey;
 
 use strict;
 use vars qw[ $VERSION $DEBUG ];
-$VERSION = sprintf "%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;
 $DEBUG   = 1 unless defined $DEBUG;
 
 use SQL::Translator::Schema::Constants;
@@ -63,125 +84,98 @@ sub produce {
     #
     my %linkable;
     my %linktable;
-    foreach my $table ( $schema->get_tables ) {
-        my $is_link = 1;
-        foreach my $field ( $table->get_fields ) {
-            unless ( $field->is_primary_key or $field->is_foreign_key ) {
-                $is_link = 0; 
-                last;
-            }
-        }
+       my %packages;
+       my $order;
 
-        next unless $is_link;
-      
-        foreach my $left ( $table->get_fields ) {
-            next unless $left->is_foreign_key;
-            my $lfk           = $left->foreign_key_reference or next;
-            my $lr_table      = $schema->get_table( $lfk->reference_table )
-                                 or next;
-            my $lr_field_name = ($lfk->reference_fields)[0];
-            my $lr_field      = $lr_table->get_field($lr_field_name);
-            next unless $lr_field->is_primary_key;
-
-            foreach my $right ( $table->get_fields ) {
-                next if $left->name eq $right->name;
-        
-                my $rfk      = $right->foreign_key_reference or next;
-                my $rr_table = $schema->get_table( $rfk->reference_table )
-                               or next;
-                my $rr_field_name = ($rfk->reference_fields)[0];
-                my $rr_field      = $rr_table->get_field($rr_field_name);
-                next unless $rr_field->is_primary_key;
-        
-                $linkable{ $lr_table->name }{ $rr_table->name } = $table;
-                $linkable{ $rr_table->name }{ $lr_table->name } = $table;
-                $linktable{ $table->name } = $table;
-            }
-        }
-    }
 
-    #
-    # Iterate over all tables
-    #
-    my ( %packages, $order );
-    for my $table ( $schema->get_tables ) {
-        my $table_name = $table->name or next;
-
-        my $table_pkg_name = $t->format_package_name($table_name);
-        $packages{ $table_pkg_name } = {
-            order     => ++$order,
-            pkg_name  => $table_pkg_name,
-            base      => $main_pkg_name,
-            table     => $table_name,
-        };
+       foreach my $table ($schema->get_tables){
+         die __PACKAGE__." table ".$table->name." doesn't have a primary key!" unless $table->primary_key;
+         die __PACKAGE__." table ".$table->name." can't have a composite primary key!" if ($table->primary_key->fields)[1];
 
-        #
-        # Primary key may have a differenct accessor method name
-        #
-        if ( my $constraint = $table->primary_key ) {
-            my $field          = ($constraint->fields)[0];
-                       $packages{ $table_pkg_name }{'columns_primary'} = $field;
 
-            if ( my $pk_xform = $t->format_pk_name ) {
-                my $pk_name = $pk_xform->( $table_pkg_name, $field );
+         my $package = Turnkey::Package->new();
+         $packages{ $package->name } = $package;
 
-                $packages{ $table_pkg_name }{'pk_accessor'} = 
-                    "#\n# Primary key accessor\n#\n".
-                    "sub $pk_name {\n    shift->$field\n}\n\n";
-                               
-            }
-        }
+         $package->order( ++$order );
+         $package->name( $t->format_package_name($table->name) );
+         $package->base( $main_pkg_name );
+         $package->table( $table );
+         $package->primary_key( ($table->primary_key->fields)[0] );
+         # Primary key may have a differenct accessor method name
+         $package->primary_key_accessor(
+                                                                        defined($t->format_pk_name) ? $t->format_pk_name->( $package->name, $package->primary_key )
+                                                                                                    : undef
+                                                                       );
+       }
 
-        my $is_data = 0;
-        foreach my $field ( $table->get_fields ) {
-                 if ( !$field->is_foreign_key and !$field->is_primary_key ) {
-                       push @{ $packages{ $table_pkg_name }{'columns_essential'} }, $field->name;
-                       $is_data++;
-                 } elsif ( !$field->is_primary_key ) {
-                       push @{ $packages{ $table_pkg_name }{'columns_others'} }, $field->name;
+       foreach my $maylink ( $schema->get_tables ){
+         foreach my $left ($schema->get_tables){
+               foreach my $right ($schema->get_tables){
+
+                 next if $left->name eq $right->name;
+
+                 if( $maylink->can_link($left,$right) ){
+
+                       $lpackage = $packages{$left->name};
+                       $rpackage = $packages{$right->name};
+
+                       $lpackage->many_via($rpackage => $maylink);
+                       $rpackage->many_via($lpackage => $maylink);
+#                      $linktable{ $maylink->name } = $maylink;
                  }
+
                }
+         }
+       }
+
+    #
+    # Iterate over all tables
+    #
+    for my $table_from ( $schema->get_tables ) {
+         my %linked;
 
-               my %linked;
-               if ( $is_data ) {
-                 foreach my $link ( keys %{ $linkable{ $table_name } } ) {
-                       my $linkmethodname;
+
+         next unless $table_from->is_data;
+
+         if($table_from->is_data){
+               foreach my $link ( keys %{ $linkable{ $table_from->name } } ) {
+                 my $linkmethodname;
 
                        if ( my $fk_xform = $t->format_fk_name ) {
                          # ADD CALLBACK FOR PLURALIZATION MANGLING HERE
-                         $linkmethodname = $fk_xform->($linkable{$table_name}{$link}->name,
+                         $linkmethodname = $fk_xform->($linkable{$table->name}{$link}->name,
                                                                                        ($schema->get_table($link)->primary_key->fields)[0]).'s';
                        } else {
                          # ADD CALLBACK FOR PLURALIZATION MANGLING HERE
-                         $linkmethodname = $linkable{$table_name}{$link}->name.'_'.
+                         $linkmethodname = $linkable{$table->name}{$link}->name.'_'.
                                ($schema->get_table($link)->primary_key->fields)[0].'s';
                        }
 
                        my @rk_fields = ();
                        my @lk_fields = ();
-                       foreach my $field ($linkable{$table_name}{$link}->get_fields) {
+                       foreach my $field ($linkable{$table->name}{$link}->get_fields) {
                          next unless $field->is_foreign_key;
 
                          next unless(
-                                                 $field->foreign_key_reference->reference_table eq $table_name
+                                                 $field->foreign_key_reference->reference_table eq $table->name
                                                  ||
                                                  $field->foreign_key_reference->reference_table eq $link
                                                 );
                          push @lk_fields, ($field->foreign_key_reference->reference_fields)[0]
                                if $field->foreign_key_reference->reference_table eq $link;
                          push @rk_fields, $field->name
-                               if $field->foreign_key_reference->reference_table eq $table_name;
+                               if $field->foreign_key_reference->reference_table eq $table->name;
                        }
 
                        #if one possible traversal via link table
                        if (scalar(@rk_fields) == 1 and scalar(@lk_fields) == 1) {
                          foreach my $rk_field (@rk_fields) {
                                #push @{ $packages{ $table_pkg_name }{'has_many'}{ $link } },
-                               push @{ $packages{ $table_pkg_name }{'has_many'}{$link}{'link_one_one'} },
+                               push @{ $packages{ $package->name }{'has_many'}{$link}{'link_one_one'} },
                                  "sub ".$linkmethodname." { my \$self = shift; ".
                                        "return map \$_->".
                                          ($schema->get_table($link)->primary_key->fields)[0].
-                                               ", \$self->".$linkable{$table_name}{$link}->name.
+                                               ", \$self->".$linkable{$table->name}{$link}->name.
                                                  "_".$rk_field." }\n\n";
                                #push @{ $packages{ $table_pkg_name }{'has_many'}{ $link }{'one_one'} },
                                #  {link_method_name => $linkmethodname, primary_key_field => ($schema->get_table($link)->primary_key->fields)[0],
@@ -196,10 +190,10 @@ sub produce {
                          foreach my $rk_field (@rk_fields) {
                                # ADD CALLBACK FOR PLURALIZATION MANGLING HERE
                                #push @{ $packages{ $table_pkg_name }{'has_many'}{ $link } },
-                               push @{ $packages{ $table_pkg_name }{'has_many'}{ $link }{'link_many_one'} },
-                                 "sub " . $linkable{$table_name}{$link}->name .
+                               push @{ $packages{ $package->name }{'has_many'}{ $link }{'link_many_one'} },
+                                 "sub " . $linkable{$table->name}{$link}->name .
                                        "s { my \$self = shift; return \$self->" .
-                                         $linkable{$table_name}{$link}->name . "_" .
+                                         $linkable{$table->name}{$link}->name . "_" .
                                                $rk_field . "(\@_) }\n\n";
                                #push @{ $packages{ $table_pkg_name }{'has_many'}{ $link }{'many_one'} },
                                #  {
@@ -215,10 +209,10 @@ sub produce {
                          foreach my $rk_field (@rk_fields) {
                                # ADD CALLBACK FOR PLURALIZATION MANGLING HERE
                                #push @{ $packages{ $table_pkg_name }{'has_many'}{ $link } },
-                               push @{ $packages{ $table_pkg_name }{'has_many'}{ $link }{'link_many_many'} },
-                                 "sub " . $linkable{$table_name}{$link}->name . "_" . $rk_field .
+                               push @{ $packages{ $package->name }{'has_many'}{ $link }{'link_many_many'} },
+                                 "sub " . $linkable{$table->name}{$link}->name . "_" . $rk_field .
                                        "s { my \$self = shift; return \$self->" .
-                                         $linkable{$table_name}{$link}->name . "_" .
+                                         $linkable{$table->name}{$link}->name . "_" .
                                                $rk_field . "(\@_) }\n\n";
                                #push @{ $packages{ $table_pkg_name }{'has_many'}{ $link }{'many_many'} },
                                #  {
@@ -243,11 +237,9 @@ sub produce {
                 my $ref_pkg    = $t->format_package_name($ref_table);
                 my $ref_field  = ($fk->reference_fields)[0];
 
-                push @{ $packages{ $table_pkg_name }{'has_a'} },
-                    "$table_pkg_name->has_a(\n".
-                    "    $field_name => '$ref_pkg'\n);\n\n".
-                    "sub $fk_method {\n".
-                    "    return shift->$field_name\n}\n\n"
+                push @{ $packages{ $package->name }{'has_a'} },
+                    $package->name."->has_a( $field_name => '$ref_pkg');\n".
+                    "sub $fk_method { return shift->$field_name }\n\n"
                 ;
                                
                                
@@ -260,12 +252,12 @@ sub produce {
                                #if there weren't M-M relationships via the has_many
                                #being set up here, create nice pluralized method alias
                                #rather for user as alt. to ugly tablename_fieldname name
-                               if(! $packages{ $ref_pkg }{ 'has_many' }{ $table_name } ){
+                               if(! $packages{ $ref_pkg }{ 'has_many' }{ $table->name } ){
                                  # ADD CALLBACK FOR PLURALIZATION MANGLING HERE
                                  #push @{ $packages{ $ref_pkg }{'has_many'}{ $table_name } },
                                #       "sub $table_name\s {\n    return shift->$table_name\_$field_name\n}\n\n";
-                                 push @{ $packages{ $ref_pkg }{'has_many'}{ $table_name }{'fk_pluralized'} },
-                                       { table_name => $table_name, field_name => $field_name };
+                                 push @{ $packages{ $ref_pkg }{'has_many'}{ $table->name }{'fk_pluralized'} },
+                                       { table_name => $table->name, field_name => $field_name };
 
                                #else ugly
                                } else {
@@ -275,7 +267,7 @@ sub produce {
                                #  "$ref_pkg->has_many(\n    '${table_name}_${field_name}', ".
                                #  "'$table_pkg_name' => '$field_name'\n);\n\n";
                                push @{ $packages{ $ref_pkg }{'has_many'}{ $table_name }{pluralized} },
-                                 { ref_pkg => $ref_pkg, table_pkg_name => $table_pkg_name, table_name => $table_name, field_name => $field_name };
+                                 { ref_pkg => $ref_pkg, table_pkg_name => $package->name, table_name => $table->name, field_name => $field_name };
             }
                }
        }
@@ -464,37 +456,38 @@ my $turnkey_xml_tt2 = <<EOF;
 <!-- Atom Classes -->
 [% FOREACH package = linkable %]
   <atom class="Durian::Atom::[% package.key FILTER ucfirst %]"  name="[% package.key FILTER ucfirst %]" xlink:label="[% package.key FILTER ucfirst %]Atom"/>
-[% END %]
+[%- END -%]
 
 <!-- Atom Bindings -->
 <atomatombindings>
 [% FOREACH focus_atom = linkable %]
   [% FOREACH link_atom = focus_atom.value %]
   <atomatombinding xlink:from="#[% focus_atom.key FILTER ucfirst %]Atom" xlink:to="#[% link_atom.key FILTER ucfirst %]Atom" xlink:label="[% focus_atom.key FILTER ucfirst %]Atom2[% link_atom.key FILTER ucfirst %]Atom"/>
-  [% END %]
-[% END %]
+  [%- END -%]
+[%- END -%]
 </atomatombindings>
 
 <atomcontainerbindings>
-       [% FOREACH focus_atom = linkable %]
-       <atomcontainerbindingslayout xlink:label="Durian::Model::[% focus_atom.key FILTER ucfirst %]">
-         [% FOREACH link_atom = focus_atom.value %]
-         <atomcontainerbinding xlink:from="#MidLeftContainer" xlink:label="MidLeftContainer2[% link_atom.key FILTER ucfirst %]Atom"  xlink:to="#[% link_atom.key FILTER ucfirst %]Atom"/>
-         [% END %]
-         <atomcontainerbinding xlink:from="#MainContainer"    xlink:label="MainContainer2[% focus_atom.key FILTER ucfirst %]Atom"    xlink:to="#[% focus_atom.key FILTER ucfirst %]Atom"/>
-    </atomcontainerbindingslayout>
-       [% END %]
-   </atomcontainerbindings>
-
-  <uribindings>
-    <uribinding uri="/" class="Durian::Util::Frontpage"/>
-  </uribindings>
+[% FOREACH focus_atom = linkable %]
+  <atomcontainerbindingslayout xlink:label="Durian::Model::[% focus_atom.key FILTER ucfirst %]">
+  [% FOREACH link_atom = focus_atom.value %]
+    <atomcontainerbinding xlink:from="#MidLeftContainer" xlink:label="MidLeftContainer2[% link_atom.key FILTER ucfirst %]Atom"  xlink:to="#[% link_atom.key FILTER ucfirst %]Atom"/>
+  [%- END -%]
+  <atomcontainerbinding xlink:from="#MainContainer"    xlink:label="MainContainer2[% focus_atom.key FILTER ucfirst %]Atom"    xlink:to="#[% focus_atom.key FILTER ucfirst %]Atom"/>
+  </atomcontainerbindingslayout>
+  [%- END -%]
+</atomcontainerbindings>
+
+<uribindings>
+  <uribinding uri="/" class="Durian::Util::Frontpage"/>
+</uribindings>
+
+<classbindings>
+[% FOREACH focus_atom = linkable %]
+   <classbinding class="Durian::Model::[% focus_atom.key FILTER ucfirst %]" plugin="#[% focus_atom.key FILTER ucfirst %]Atom" rank="0"/>
+[%- END -%]
 
-  <classbindings>
-       [% FOREACH focus_atom = linkable %]
-     <classbinding class="Durian::Model::[% focus_atom.key FILTER ucfirst %]" plugin="#[% focus_atom.key FILTER ucfirst %]Atom" rank="0"/>
-       [% END %]
-  </classbindings>
+</classbindings>
 
 </Durian>
 EOF
@@ -571,10 +564,12 @@ sub translateForm
   my $args = $t->producer_args;
   my $tt2     = $args->{'template'};
   my $tt2Ref;
-  if ($tt2 eq 'atom') { $tt2Ref = \$turnkey_atom_tt2; }
-  if ($tt2 eq 'dbi') { $tt2Ref = \$turnkey_dbi_tt2; }
-  if ($tt2 eq 'xml') { $tt2Ref = \$turnkey_xml_tt2; }
-  if ($tt2 eq 'template') { $tt2Ref = \$turnkey_template_tt2; }
+
+     if ($tt2 eq 'atom')     { $tt2Ref = \$turnkey_atom_tt2;     }
+  elsif ($tt2 eq 'classdbi') { $tt2Ref = \$turnkey_dbi_tt2;      }
+  elsif ($tt2 eq 'xml')      { $tt2Ref = \$turnkey_xml_tt2;      }
+  elsif ($tt2 eq 'template') { $tt2Ref = \$turnkey_template_tt2; }
+  else                       { die __PACKAGE__." didn't recognize your template option: $tt2" }
 
   my $vars = {
                                packages  => $output->{packages},
@@ -613,7 +608,7 @@ SQL::Translator::Producer::ClassDBI - create Class::DBI classes from schema
 Use this producer as you would any other from SQL::Translator.  See
 L<SQL::Translator> for details.
 
-This package utilizes SQL::Translator's formatting methods
+This package utilizes SQL::Translator\'s formatting methods
 format_package_name(), format_pk_name(), format_fk_name(), and
 format_table_name() as it creates classes, one per table in the schema
 provided.  An additional base class is also created for database connectivity
@@ -624,4 +619,4 @@ configuration.  See L<Class::DBI> for details on how this works.
 Allen Day E<lt>allenday@ucla.eduE<gt>
 Ying Zhang E<lt>zyolive@yahoo.comE<gt>,
 Ken Y. Clark E<lt>kclark@cpan.orgE<gt>,
-Brian O'Connor E<lt>brian.oconnor@excite.comE<gt>.
+Brian O\'Connor E<lt>brian.oconnor@excite.comE<gt>.
index 878debe..0476992 100644 (file)
@@ -1,7 +1,7 @@
 package SQL::Translator::Schema::Table;
 
 # ----------------------------------------------------------------------
-# $Id: Table.pm,v 1.14 2003-08-21 20:27:04 kycl4rk Exp $
+# $Id: Table.pm,v 1.15 2003-08-29 05:38:56 allenday Exp $
 # ----------------------------------------------------------------------
 # Copyright (C) 2003 Ken Y. Clark <kclark@cpan.org>
 #
@@ -50,7 +50,7 @@ use SQL::Translator::Schema::Index;
 use base 'Class::Base';
 use vars qw( $VERSION $FIELD_ORDER );
 
-$VERSION = sprintf "%d.%02d", q$Revision: 1.14 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.15 $ =~ /(\d+)\.(\d+)/;
 
 # ----------------------------------------------------------------------
 sub init {
@@ -246,7 +246,7 @@ existing field, you will get an error and the field will not be created.
     my $field_name = $field->name or return $self->error('No name');
 
     if ( exists $self->{'fields'}{ $field_name } ) { 
-        return $self->error(qq[Can't create field: "$field_name" exists]);
+        return $self->error(qq[Can\'t create field: "$field_name" exists]);
     }
     else {
         $self->{'fields'}{ $field_name } = $field;
@@ -418,6 +418,72 @@ Determine whether the view is valid or not.
     return 1;
 }
 
+sub is_data {
+  my $self = shift;
+  return $self->{'is_data'} if defined $self->{'is_data'};
+
+  $self->{'is_data'} = 0;
+
+  foreach my $field ($self->get_fields){
+       if(!$field->is_primary_key or !$field->is_foreign_key){
+         $self->{'is_data'} = 1;
+         return $self->{'is_data'}
+       }
+  }
+
+  return $self->{'is_data'};
+}
+
+sub can_link {
+
+=pod
+
+=head2 can_link
+
+Determine whether the table can link two arg tables via many-to-many.
+
+  my $ok = $table->can_link($table1,$table2);
+
+=cut
+
+  my($self,$table1,$table2) = @_;
+
+  #get tables in abc order
+  ($table1,$table2) = sort {$a->name cmp $b->name} ($table1,$table2);
+
+  return $self->{'can_link'}{$table1->name}{$table2->name} if defined $self->{'can_link'}{$table1->name}{$table2->name};
+
+  if($self->is_data == 1){
+       $self->{'can_link'}{$table1->name}{$table2->name} = 0;
+       return $self->{'can_link'}{$table1->name}{$table2->name};
+  }
+
+  my %fk = ();
+
+  foreach my $field ($self->get_fields){
+       #if the table has non-key fields, it can't be a link
+       if(!$field->is_primary_key or !$field->is_foreign_key){
+         $self->{'can_link'}{$table1->name}{$table2->name} = 0;
+         return $self->{'can_link'}{$table1->name}{$table2->name};
+
+       #otherwise, count up how many fields refer to each FK table.field
+       } elsif($field->is_foreign_key){
+         $fk{$field->foreign_key_reference->reference_table->name}++;
+       }
+  }
+
+  if($fk{ $table1->name } == 1
+        and
+        $fk{ $table2->name } == 1
+       ){
+       $self->{'can_link'}{$table1->name}{$table2->name} = 1;
+  } else {
+       $self->{'can_link'}{$table1->name}{$table2->name} = 0;
+  }
+
+  return $self->{'can_link'}{$table1->name}{$table2->name};
+}
+
 # ----------------------------------------------------------------------
 sub name {
 
@@ -425,7 +491,7 @@ sub name {
 
 =head2 name
 
-Get or set the table's name.
+Get or set the table\'s name.
 
 If provided an argument, checks the schema object for a table of 
 that name and disallows the change if one exists.
@@ -438,7 +504,7 @@ that name and disallows the change if one exists.
 
     if ( my $arg = shift ) {
         if ( my $schema = $self->schema ) {
-            return $self->error( qq[Can't use table name "$arg": table exists] )
+            return $self->error( qq[Can\'t use table name "$arg": table exists] )
                 if $schema->get_table( $arg );
         }
         $self->{'name'} = $arg;
@@ -454,7 +520,7 @@ sub schema {
 
 =head2 schema
 
-Get or set the table's schema object.
+Get or set the table\'s schema object.
 
   my $schema = $table->schema;
 
@@ -477,7 +543,7 @@ sub primary_key {
 
 =head2 options
 
-Gets or sets the table's primary key(s).  Takes one or more field
+Gets or sets the table\'s primary key(s).  Takes one or more field
 names (as a string, list or array[ref]) as an argument.  If the field
 names are present, it will create a new PK if none exists, or it will
 add to the fields of an existing PK (and will unique the field names).
@@ -543,7 +609,7 @@ sub options {
 
 =head2 options
 
-Get or set the table's options (e.g., table types for MySQL).  Returns
+Get or set the table\'s options (e.g., table types for MySQL).  Returns
 an array or array reference.
 
   my @options = $table->options;
@@ -570,7 +636,7 @@ sub order {
 
 =head2 order
 
-Get or set the table's order.
+Get or set the table\'s order.
 
   my $order = $table->order(3);