[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