[Rt-commit] r3843 - in Jifty-DBI/trunk: . lib/Jifty/DBI t

jesse at bestpractical.com jesse at bestpractical.com
Sun Sep 11 18:01:44 EDT 2005


Author: jesse
Date: Sun Sep 11 18:01:43 2005
New Revision: 3843

Modified:
   Jifty-DBI/trunk/   (props changed)
   Jifty-DBI/trunk/lib/Jifty/DBI/Column.pm
   Jifty-DBI/trunk/lib/Jifty/DBI/Record.pm
   Jifty-DBI/trunk/t/01records.t
   Jifty-DBI/trunk/t/01searches.t
   Jifty-DBI/trunk/t/02records_object.t
   Jifty-DBI/trunk/t/utils.pl
Log:
 r15681 at hualien:  jesse | 2005-09-11 15:46:16 -0400
 * Checkpoint. tests pass


Modified: Jifty-DBI/trunk/lib/Jifty/DBI/Column.pm
==============================================================================
--- Jifty-DBI/trunk/lib/Jifty/DBI/Column.pm	(original)
+++ Jifty-DBI/trunk/lib/Jifty/DBI/Column.pm	Sun Sep 11 18:01:43 2005
@@ -44,4 +44,9 @@
     return 0;
 
 }
+
+# Aliases for compatibility with searchbuilder code
+*read = \&readable;
+*write = \&writable;
+
 1;

Modified: Jifty-DBI/trunk/lib/Jifty/DBI/Record.pm
==============================================================================
--- Jifty-DBI/trunk/lib/Jifty/DBI/Record.pm	(original)
+++ Jifty-DBI/trunk/lib/Jifty/DBI/Record.pm	Sun Sep 11 18:01:43 2005
@@ -5,8 +5,10 @@
 
 use vars qw($AUTOLOAD);
 use Class::ReturnValue;
+use Lingua::EN::Inflect;
+use Jifty::DBI::Column;
 
-our %COLUMNS;
+our $COLUMNS; # The global cache of all schema columns
 
 =head1 NAME
 
@@ -265,6 +267,9 @@
     my $class = ref($proto) || $proto;
     my $self = {};
     bless( $self, $class );
+
+    $self->_init_columns() unless keys %{$self->COLUMNS};
+
     $self->_init(@_);
 
     return $self;
@@ -340,7 +345,8 @@
         goto &$AUTOLOAD;
     }
 
-    if ( $action eq 'write' and $column->writeable ) {
+    if ( $action eq 'write' ) {
+        if ( $column->writable ) {
 
         if ( $column->refers_to_record_class ) {
             *{$AUTOLOAD} = sub {
@@ -349,17 +355,19 @@
 
                 $val = $val->id
                     if UNIVERSAL::isa( $val, 'Jifty::DBI::Record' );
-                return ( $self->_set( field => $column_name, value => $val ) );
+                return ( $self->_set( column => $column_name, value => $val ) );
             };
         }
         else {
             *{$AUTOLOAD} = sub {
-                return ( $_[0]->_set( field => $column_name, value => $_[1] ) );
+                return ( $_[0]->_set( column => $column_name, value => $_[1] ) );
             };
         }
         goto &$AUTOLOAD;
+    } else {
+            return (0, 'Immutable field');
+    }
     }
-
     elsif ( $action eq 'validate' ) {
         *{$AUTOLOAD}
             = sub { return ( $_[0]->_validate( $column_name, $_[1] ) ) };
@@ -389,17 +397,17 @@
 
     my ($column_name, $action);
 
-    if ( $$method =~ /.*::set_(\w+)/o ) {
+    if ( $method =~ /^.*::set_(\w+)$/o ) {
         $column_name = $1;
         $action = 'write';
     }
-    elsif ( $$method =~ /.*::validate_(\w+)/o ) {
+    elsif ( $method =~ /^.*::validate_(\w+)$/o ) {
         $column_name = $1;
         $action = 'validate';
 
 
     }
-    elsif ( $$method =~ /::(\w+)$/o) {
+    elsif ( $method =~ /^.*::(\w+)$/o) {
         $column_name = $1;
         $action = 'read';
 
@@ -409,7 +417,9 @@
 }
 =head2 _accessible COLUMN ATTRIBUTE
 
-Private method.
+Private method. 
+
+DEPRECATED
 
 Returns undef unless C<COLUMN> has a true value for C<ATTRIBUTE>.
 
@@ -423,7 +433,6 @@
     my $column_name = shift;
     my $attribute = lc( shift || '' );
 
-
     my $col = $self->column($column_name);
     return undef unless ($col and $col->can($attribute));
     return $col->$attribute();
@@ -449,46 +458,60 @@
     return $pkeys->[0];
 }
 
-=head2 _class_accessible 
-
-An older way to read fields attributes in a derived class.
-(The current preferred method is by overriding C<schema>; if you do
-this and don't override C<_class_accessible>, the module will generate
-an appropriate C<_class_accessible> based on your C<schema>.)
+=head2 _init_columns
 
+Turns your sub schema into a set of column objects
 =cut
 
-sub _class_accessible {
+sub _init_columns {
     my $self = shift;
 
-    foreach my $column_name ( $self->_primary_keys ) {
+    foreach my $column_name ( @{$self->_primary_keys} ) {
         my $column = $self->add_column($column_name);
-        $column->read(1);
+        $column->writable(0);
     }
 
     my $schema = $self->schema;
 
     for my $column_name ( keys %$schema ) {
         my $column = $self->add_column($column_name);
-        if ( $schema->{$column_name}{'TYPE'} ) {
-            $column->read(1);
-            $column->write(1);
+        # Default, everything readable and writable
+        $column->readable(1);
 
+        if ( $schema->{$column_name}{'read'} ) {
+            $column->readable( $schema->{$column_name}{'read'});
+        } else {
+            $column->readable(1);
+        }
+    
+        if ( $schema->{$column_name}{'write'} ) {
+            $column->writable( $schema->{$column_name}{'write'});
+        } elsif (not defined $column->writable) { # don't want to make pkeys writable
+            $column->writable(1);
         }
-        elsif ( my $refclass = $schema->{$column_name}{'REFERENCES'} ) {
+
+    
+        # Next time, all-lower hash keys
+        my $type = $schema->{$column_name}{'type'} || $schema->{$column_name}{'TYPE'};
+
+        if ($type) {
+            $column->type($type);
+
+        }
+
+        my $refclass = $schema->{$column_name}{'REFERENCES'} || $schema->{$column_name}{'references'};
+
+        if ($refclass) {
             if ( UNIVERSAL::isa( $refclass, 'Jifty::DBI::Record' ) ) {
                 if ( $column_name =~ /(.*)_id$/ ) {
-                    $column->read(1);
-                    $column->write(1);
 
                     my $virtual_column = $self->add_column($1);
                     $virtual_column->refers_to_record_class($refclass);
                     $virtual_column->alias_for_column($column_name);
+                    $virtual_column->readable( $schema->{$column_name}{'read'});
                 }
                 else {
                     $column->refers_to_record_class($refclass);
-                    $column->read(1);
-                    $column->write(1);
                 }
 
             }
@@ -524,7 +547,7 @@
 
 
     my $column = $self->column($column_name);
-    my $classname = $column->references_record_class();
+    my $classname = $column->refers_to_record_class();
 
 
     return unless defined $value;
@@ -569,8 +592,9 @@
     my $self = shift;
     my $name = shift;
     $name = lc $name;
-    $COLUMNS{$name} = Jifty::DBI::Column->new() unless exists $COLUMNS{$name};
-    $COLUMNS{$name}->name($name);
+    $self->COLUMNS->{$name} = Jifty::DBI::Column->new() unless exists $self->COLUMNS->{$name};
+    $self->COLUMNS->{$name}->name($name);
+    return $self->COLUMNS->{$name};
 }
 
 
@@ -582,39 +606,51 @@
     my $self = shift;
     my $name = shift;
     $name = lc $name;
-    return $COLUMNS{$name};
+    return $self->COLUMNS->{$name} ;
 
 }
 
 sub columns {
-    return values %COLUMNS;
+    my $self = shift;
+    return (values %{$self->COLUMNS});
 }
 
+sub COLUMNS {
+    my $self = shift;
+    my $class = ref($self) || $self;
+    my $symbol_name = $class."::_COLUMNS";
+     
+    no strict 'refs';
+    # Initialize the has of columns if we haven't been here yet for this class
+    *{$symbol_name} = {} unless (*{$symbol_name}{HASH});
+    return *{$symbol_name};
+
+}
+
+
 # sub {{{ readable_attributes
 
 =head2 readable_attributes
 
 Returns a list this table's readable columns
 
-XXX TODO actuall returns the column objects
 =cut
 
 sub readable_attributes {
     my $self     = shift;
-    return grep { $_->readable } @{$self->columns};
+    return sort map {$_->name }  grep { $_->readable } $self->columns;
 }
 
 =head2 writable_attributes
 
 Returns a list of this table's writable columns
 
-XXX TODO returns the column objects
 
 =cut
 
 sub writable_attributes {
     my $self = shift;
-    return grep { $_->writeable } @{$self->columns};
+    return sort map { $_->name } grep { $_->writable } $self->columns;
 }
 
 =head2 __value
@@ -631,7 +667,7 @@
     # If the requested column is actually an alias for another, resolve it.
    
     $field = $self->column($field)->alias_for_column() 
-        while $self->column($field)->alias_for_column();
+    while ($self->column($field) and $self->column($field)->alias_for_column());
 
     if ( !$self->{'fetched'}{$field} and my $id = $self->id() ) {
         my $pkey = $self->_primary_key();
@@ -696,7 +732,8 @@
     }
     my $ret = Class::ReturnValue->new();
 
-    unless ( $args{'column'} ) {
+    my $column = $self->column(lc $args{'column'});
+    unless ( $column) {
         $ret->as_array( 0, 'No column specified' );
         $ret->as_error(
             errno        => 5,
@@ -705,7 +742,6 @@
         );
         return ( $ret->return_value );
     }
-    my $column_name = lc $args{'column'};
     if ( !defined( $args{'value'} ) ) {
         $ret->as_array( 0, "No value passed to _set" );
         $ret->as_error(
@@ -715,8 +751,8 @@
         );
         return ( $ret->return_value );
     }
-    elsif ( ( defined $self->__value($column_name) )
-        and ( $args{'value'} eq $self->__value($column_name) ) )
+    elsif ( ( defined $self->__value($column->name) )
+        and ( $args{'value'} eq $self->__value($column->name) ) )
     {
         $ret->as_array( 0, "That is already the current value" );
         $ret->as_error(
@@ -730,15 +766,15 @@
     # First, we truncate the value, if we need to.
     
 
-    $args{'value'} = $self->truncate_value( $args{'column'}, $args{'value'} );
+    $args{'value'} = $self->truncate_value( $column->name, $args{'value'} );
 
-    my $method = "validate_" . $args{'column'};
+    my $method = "validate_" . $column->name;
     unless ( $self->$method( $args{'value'} ) ) {
-        $ret->as_array( 0, 'Illegal value for ' . $args{'column'} );
+        $ret->as_array( 0, 'Illegal value for ' . $column->name);
         $ret->as_error(
             errno        => 3,
             do_backtrace => 0,
-            message      => "Illegal value for " . $args{'column'}
+            message      => "Illegal value for " . $column->name
         );
         return ( $ret->return_value );
     }
@@ -749,11 +785,10 @@
     my $unmunged_value = $args{'value'};
 
     unless ( $self->_handle->knows_blobs ) {
-        my $column = $self->column($column_name);
 
         # Support for databases which don't deal with LOBs automatically
         if ( $column->type =~ /^(text|longtext|clob|blob|lob)$/i ) {
-            my $bhash = $self->_handle->blob_params( $column_name, $column->type );
+            my $bhash = $self->_handle->blob_params( $column->name, $column->type );
             $bhash->{'value'} = $args{'value'};
             $args{'value'} = $bhash;
         }
@@ -766,7 +801,7 @@
 
     );
     unless ($val) {
-        my $message = $args{'column'} . " could not be set to " . $args{'value'} . ".";
+        my $message = $column->name . " could not be set to " . $args{'value'} . ".";
         $ret->as_array( 0, $message );
         $ret->as_error(
             errno        => 4,
@@ -783,7 +818,7 @@
         $self->load_by_id( $self->id );
     }
     else {
-        $self->{'values'}->{"$column_name"} = $unmunged_value;
+        $self->{'values'}->{$column->name} = $unmunged_value;
     }
     $ret->as_array( 1, "The new value has been set." );
     return ( $ret->return_value );
@@ -871,10 +906,12 @@
     my $value = shift;
 
     # We don't need to truncate empty things.
-    return undef unless ( defined($value) );
+    return undef unless ( defined($value) ); 
 
     my $column = $self->column($column_name);
 
+    die "No column $column_name" unless ($column);
+
     my $truncate_to;
     if ( $column->length && !$column->is_numeric ) {
         $truncate_to = $column->length;
@@ -1109,7 +1146,7 @@
         unless ($column) {
             die "$column_name isn't a column we know about"
         }
-        if ( $column->readable and $column->refers_to_record ) {
+        if ( $column->readable and $column->refers_to_record_class ) {
             $attribs{$column_name} = $attribs{$column_name}->id
                 if UNIVERSAL::isa( $attribs{$column_name}, 'Jifty::DBI::Record' );
         }
@@ -1172,20 +1209,42 @@
     }
 }
 
+
+
+
+
 =head2 table
 
-Returns or sets the name of the current table
+This method returns this class's default table name. It uses
+Lingua::EN::Inflect to pluralize the class's name as we believe that
+class names for records should be in the singular and table names
+should be plural.
+
+If your class name is C<My::App::Rhino>, your table name will default
+to C<rhinos>. If your class name is C<My::App::RhinoOctopus>, your
+default table name will be C<rhino_octopuses>. Not perfect, but
+arguably correct.
 
 =cut
 
 sub table {
     my $self = shift;
-    if (@_) {
-        $self->{'table'} = shift;
+    use Carp; die Carp::longmess unless ref $self;
+
+    if (not $self->{__table_name} ) {
+	    my $class = ref($self);
+	    die "Couldn't turn ".$class." into a table name" unless ($class =~ /::(\w+)$/);
+            my $table = $1;
+            $table =~ s/(?<=[a-z])([A-Z]+)/"_" . lc($1)/eg;
+            $table =~ tr/A-Z/a-z/;
+            $table = Lingua::EN::Inflect::PL_N($table);
+	    $self->{__table_name} = $table;
     }
-    return ( $self->{'table'} );
+    return $self->{__table_name};
 }
 
+
+
 =head2 _handle
 
 Returns or sets the current Jifty::DBI::Handle object

Modified: Jifty-DBI/trunk/t/01records.t
==============================================================================
--- Jifty-DBI/trunk/t/01records.t	(original)
+++ Jifty-DBI/trunk/t/01records.t	Sun Sep 11 18:01:43 2005
@@ -8,7 +8,7 @@
 BEGIN { require "t/utils.pl" }
 our (@available_drivers);
 
-use constant TESTS_PER_DRIVER => 65;
+use constant TESTS_PER_DRIVER => 64;
 
 my $total = scalar(@available_drivers) * TESTS_PER_DRIVER;
 plan tests => $total;
@@ -32,9 +32,10 @@
 	my $rec = TestApp::Address->new($handle);
 	isa_ok($rec, 'Jifty::DBI::Record');
 
+
 # _accessible testings
 	is( $rec->_accessible('id' => 'read'), 1, 'id is accessible for read' );
-	is( $rec->_accessible('id' => 'write'), undef, 'id is not accessible for write' );
+	is( $rec->_accessible('id' => 'write'), 0, 'id is not accessible for write' );
 	is( $rec->_accessible('id'), undef, "any field is not accessible in undefined mode" );
 	is( $rec->_accessible('unexpected_field' => 'read'), undef, "field doesn't exist and can't be accessible for read" );
 	is_deeply( [sort($rec->readable_attributes)], [sort qw(employee_id id name phone)], 'readable attributes' );
@@ -67,10 +68,8 @@
 		local $SIG{__WARN__} = sub {return};
 		is( $rec->_value( 'some_unexpected_field' ), undef, "The record has no 'some_unexpected_field'");
 	}
-	($val, $msg) = $rec->set_some_unexpected_field( 'foo' );
-	ok(!$val, $msg);
-	is($msg, 'Nonexistant field?', "Field doesn't exist");
-	($val, $msg) = $rec->_set('some_unexpected_field', 'foo');
+	ok (!eval { $rec->set_some_unexpected_field( 'foo' )}, "Can't call nonexistent fields");
+	($val, $msg) = $rec->_set(column =>'some_unexpected_field', value =>'foo');
 	ok(!$val, "$msg");
 
 
@@ -109,20 +108,20 @@
 
 # no prefetch feature and _load_from_sql sub checks
 	$newrec = TestApp::Address->new($handle);
-	($val, $msg) = $newrec->_load_from_sql('SELECT id FROM address WHERE id = ?', $newid);
+	($val, $msg) = $newrec->_load_from_sql('SELECT id FROM addresses WHERE id = ?', $newid);
 	is($val, 1, 'found object');
 	is($newrec->name, '12345678901234', "autoloaded not prefetched field");
 	is($newrec->employee_id, '1234567890', "autoloaded not prefetched field");
 
 # _load_from_sql and missing PK
 	$newrec = TestApp::Address->new($handle);
-	($val, $msg) = $newrec->_load_from_sql('SELECT name FROM address WHERE name = ?', '12345678901234');
+	($val, $msg) = $newrec->_load_from_sql('SELECT name FROM addresses WHERE name = ?', '12345678901234');
 	is($val, 0, "didn't find object");
 	is($msg, "Missing a primary key?", "reason is missing PK");
 
 # _load_from_sql and not existant row
 	$newrec = TestApp::Address->new($handle);
-	($val, $msg) = $newrec->_load_from_sql('SELECT id FROM address WHERE id = ?', 0);
+	($val, $msg) = $newrec->_load_from_sql('SELECT id FROM addresses WHERE id = ?', 0);
 	is($val, 0, "didn't find object");
 	is($msg, "Couldn't find row", "reason is wrong id");
 
@@ -208,13 +207,6 @@
 
 use base qw/Jifty::DBI::Record/;
 
-sub _init {
-    my $self = shift;
-    my $handle = shift;
-    $self->table('address');
-    $self->_handle($handle);
-}
-
 sub validate_name
 {
 	my ($self, $value) = @_;
@@ -222,18 +214,14 @@
 	return 1;
 }
 
-sub _class_accessible {
+sub schema {
 
     {   
         
-        id =>
-        {read => 1, type => 'int(11)', default => ''}, 
-        name => 
-        {read => 1, write => 1, type => 'varchar(14)', default => ''},
-        phone => 
-        {read => 1, write => 1, type => 'varchar(18)', length => 18, default => ''},
-        employee_id => 
-        {read => 1, write => 1, type => 'int(8)', default => ''},
+        id => { TYPE => 'int(11)' },
+        name => { TYPE => 'varchar(14)', DEFAULT => ''},
+        phone => { TYPE => 'varchar(18)', length => 18, DEFAULT => ''},
+        employee_id => { TYPE => 'int(8)', DEFAULT => ''},
 
 }
 
@@ -241,7 +229,7 @@
 
 sub schema_mysql {
 <<EOF;
-CREATE TEMPORARY TABLE address (
+CREATE TEMPORARY TABLE addresses (
         id integer AUTO_INCREMENT,
         name varchar(36),
         phone varchar(18),
@@ -253,7 +241,7 @@
 
 sub schema_pg {
 <<EOF;
-CREATE TEMPORARY TABLE address (
+CREATE TEMPORARY TABLE addresses (
         id serial PRIMARY KEY,
         name varchar,
         phone varchar,
@@ -266,7 +254,7 @@
 sub schema_sqlite {
 
 <<EOF;
-CREATE TABLE address (
+CREATE TABLE addresses (
         id  integer primary key,
         name varchar(36),
         phone varchar(18),

Modified: Jifty-DBI/trunk/t/01searches.t
==============================================================================
--- Jifty-DBI/trunk/t/01searches.t	(original)
+++ Jifty-DBI/trunk/t/01searches.t	Sun Sep 11 18:01:43 2005
@@ -213,7 +213,7 @@
     $self->_handle($handle);
 }
 
-sub _class_accessible {
+sub schema {
     {   
         id =>
         {read => 1, type => 'int(11)' }, 

Modified: Jifty-DBI/trunk/t/02records_object.t
==============================================================================
--- Jifty-DBI/trunk/t/02records_object.t	(original)
+++ Jifty-DBI/trunk/t/02records_object.t	Sun Sep 11 18:01:43 2005
@@ -9,7 +9,7 @@
 BEGIN { require "t/utils.pl" }
 our (@available_drivers);
 
-use constant TESTS_PER_DRIVER => 11;
+use constant TESTS_PER_DRIVER => 9;
 
 my $total = scalar(@available_drivers) * TESTS_PER_DRIVER;
 plan tests => $total;
@@ -40,17 +40,12 @@
 	is($p_id, 1, "Loaded record $p_id");
 	$phone->load( $p_id );
 
-	my $obj = $phone->employee_obj($handle);
+	my $obj = $phone->employee($handle);
 	ok($obj, "Employee #$e_id has phone #$p_id");
 	isa_ok( $obj, 'TestApp::Employee');
 	is($obj->id, $e_id);
 	is($obj->name, 'RUZ');
 
-	# tests for no object mapping
-	my ($state, $msg) = $phone->value_obj($handle);
-	ok(!$state, "State is false");
-	is( $msg, 'No object mapping for field', 'Error message is correct');
-
 	cleanup_schema( 'TestApp', $handle );
 }} # SKIP, foreach blocks
 
@@ -110,14 +105,7 @@
 use vars qw/$VERSION/;
 $VERSION=0.01;
 
-sub _init {
-    my $self = shift;
-    my $handle = shift;
-    $self->table('employees');
-    $self->_handle($handle);
-}
-
-sub _class_accessible {
+sub schema {
     {   
         
         id =>
@@ -137,21 +125,14 @@
 
 use base qw/Jifty::DBI::Record/;
 
-sub _init {
-    my $self = shift;
-    my $handle = shift;
-    $self->table('phones');
-    $self->_handle($handle);
-}
-
-sub _class_accessible {
+sub schema {
     {   
         
         id =>
         {read => 1, type => 'int(11)'}, 
         employee => 
-        {read => 1, write => 1, type => 'int(11)', object => 'TestApp::Employee' },
-        value => 
+        {read => 1, write => 1, type => 'int(11)', references => 'TestApp::Employee' },
+        phone => 
         {read => 1, write => 1, type => 'varchar(18)'},
 
     }

Modified: Jifty-DBI/trunk/t/utils.pl
==============================================================================
--- Jifty-DBI/trunk/t/utils.pl	(original)
+++ Jifty-DBI/trunk/t/utils.pl	Sun Sep 11 18:01:43 2005
@@ -11,13 +11,13 @@
 =cut
 
 our @supported_drivers = qw(
+	SQLite
 	Informix
 	mysql
 	mysqlPP
 	ODBC
 	Oracle
 	Pg
-	SQLite
 	Sybase
 );
 


More information about the Rt-commit mailing list