[Jifty-commit] r5880 - in Jifty-DBI/branches/tisql: lib/Jifty lib/Jifty/DBI lib/Jifty/DBI/Handle lib/Jifty/DBI/Record

Jifty commits jifty-commit at lists.jifty.org
Fri Sep 19 19:30:00 EDT 2008


Author: ruz
Date: Fri Sep 19 19:29:59 2008
New Revision: 5880

Modified:
   Jifty-DBI/branches/tisql/   (props changed)
   Jifty-DBI/branches/tisql/Makefile.PL
   Jifty-DBI/branches/tisql/lib/Jifty/DBI.pm
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/Collection.pm
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/Filter.pm
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/Handle/Pg.pm
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/Handle/mysql.pm
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/HasFilters.pm
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/Record.pm
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/Record/Cachable.pm
   Jifty-DBI/branches/tisql/lib/Jifty/DBI/Schema.pm

Log:
* sync from trunk
 r5629 at cubic-pc (orig r5610):  trs | 2008-07-29 23:10:43 +0400
 
 r5630 at cubic-pc (orig r5611):  trs | 2008-07-29 23:11:18 +0400
  r38125 at zot:  tom | 2008-07-29 15:09:04 -0400
  We accept IN or = as operators for array ref. values, so match against that (and do it case insensitively to boot)
 
 r5643 at cubic-pc (orig r5624):  trs | 2008-08-01 01:37:50 +0400
  r38353 at zot:  tom | 2008-07-31 17:37:07 -0400
  Cache on record table, not record class so subclasses also get cached correctly.  If the data in the underlying table changes, regardless of the class you're using, you want to load the new data.
 
 r5659 at cubic-pc (orig r5640):  trs | 2008-08-01 22:13:09 +0400
  r38418 at zot:  tom | 2008-08-01 14:12:38 -0400
  Allow validators to get extra arguments
 
 r5787 at cubic-pc (orig r5768):  alexmv | 2008-08-29 19:58:22 +0400
  * Storable with non-bytea is OK if we base64
 r5788 at cubic-pc (orig r5769):  falcone | 2008-08-30 12:18:10 +0400
  r39205 at 70-9-196-144:  falcone | 2008-08-29 23:54:18 -0400
  * additional docs for load_by_cols
  
  We may want to default operator to '=' in the case
  documented
 
 r5794 at cubic-pc (orig r5775):  cubic | 2008-09-01 21:38:48 +0400
 * add double naming schema for record references:
 ** name_by - name is object accessor, name_by - value, name_by in DB
 ** name    - name is object accessor, name_by - value, name    in DB
 * So it's more about column name in the DB
 * as well fixed situation with 'column X_not_id refers_to M by "not_id"'
 r5795 at cubic-pc (orig r5776):  cubic | 2008-09-02 00:10:54 +0400
 * column should be virtual
 r5891 at cubic-pc (orig r5856):  alexmv | 2008-09-18 02:25:29 +0400
  r37358 at kohr-ah:  chmrr | 2008-09-17 18:25:12 -0400
   * When we have group_by, the first column may not be enough to distinctify the rows.  But since they're grouped, they're all distinct by definition.
 
 r5900 at cubic-pc (orig r5857):  cubic | 2008-09-20 02:53:54 +0400
  r5876 at cubic-pc:  cubic | 2008-09-19 04:19:45 +0400
  * don't call accessor twice when we have values around
 
 r5901 at cubic-pc (orig r5858):  cubic | 2008-09-20 02:54:11 +0400
  r5877 at cubic-pc:  cubic | 2008-09-19 04:20:53 +0400
  * don't create new local copies we don't need around, just return
 
 r5902 at cubic-pc (orig r5859):  cubic | 2008-09-20 02:54:19 +0400
  r5878 at cubic-pc:  cubic | 2008-09-19 04:22:52 +0400
  * usually there is no output filters, don't waste time creating an array
    ref then array
 
 r5903 at cubic-pc (orig r5860):  cubic | 2008-09-20 02:54:27 +0400
  r5879 at cubic-pc:  cubic | 2008-09-19 04:24:36 +0400
  * play with self/class only when passed argument is not a reference
 
 r5904 at cubic-pc (orig r5861):  cubic | 2008-09-20 02:54:35 +0400
  r5880 at cubic-pc:  cubic | 2008-09-19 04:33:41 +0400
  * turn _handle _is_limited rows_per_page into accessors
 
 r5905 at cubic-pc (orig r5862):  cubic | 2008-09-20 02:54:53 +0400
  r5881 at cubic-pc:  cubic | 2008-09-19 04:37:27 +0400
  * in _do_search separate prefetch and no prefetch paths, the latter
    is much simpler and doesn't require additional steps
  * in prefetch path refactor first pass over results
 
 r5906 at cubic-pc (orig r5863):  cubic | 2008-09-20 02:55:02 +0400
  r5882 at cubic-pc:  cubic | 2008-09-19 04:41:44 +0400
  * move call where we only need it
 
 r5907 at cubic-pc (orig r5864):  cubic | 2008-09-20 02:55:10 +0400
  r5883 at cubic-pc:  cubic | 2008-09-19 04:43:22 +0400
  * FIX: $self->_new_collection_args should be passed to the constructor
    of a collection, not to the new_item method of a record
 
 r5908 at cubic-pc (orig r5865):  cubic | 2008-09-20 02:55:18 +0400
  r5884 at cubic-pc:  cubic | 2008-09-19 04:44:53 +0400
  * refactor _qualified_record_columns
 
 r5909 at cubic-pc (orig r5866):  cubic | 2008-09-20 02:55:34 +0400
  r5885 at cubic-pc:  cubic | 2008-09-19 04:46:45 +0400
  * call ->table for defaulting only when we actually want defaulting
 
 r5910 at cubic-pc (orig r5867):  cubic | 2008-09-20 02:55:46 +0400
  r5886 at cubic-pc:  cubic | 2008-09-19 04:49:19 +0400
  * delay some calls and don't rewrite results of previouse calls
 
 r5911 at cubic-pc (orig r5868):  cubic | 2008-09-20 02:56:00 +0400
  r5887 at cubic-pc:  cubic | 2008-09-19 04:50:37 +0400
  * minor refactoring _get_alias
 
 r5912 at cubic-pc (orig r5869):  cubic | 2008-09-20 02:56:29 +0400
  r5888 at cubic-pc:  cubic | 2008-09-19 04:53:11 +0400
  * clean fetched in load_from_hash
 
 r5913 at cubic-pc (orig r5870):  cubic | 2008-09-20 02:56:36 +0400
  r5889 at cubic-pc:  cubic | 2008-09-19 04:54:23 +0400
  * grep before looping
 
 r5914 at cubic-pc (orig r5871):  cubic | 2008-09-20 02:56:45 +0400
  r5890 at cubic-pc:  cubic | 2008-09-19 04:54:49 +0400
  * delete commented code
 
 r5915 at cubic-pc (orig r5872):  cubic | 2008-09-20 02:56:50 +0400
  r5892 at cubic-pc:  cubic | 2008-09-19 07:48:05 +0400
  * pass arguments to the super method
 
 r5916 at cubic-pc (orig r5873):  cubic | 2008-09-20 02:56:56 +0400
  r5893 at cubic-pc:  cubic | 2008-09-19 22:52:18 +0400
  * update record_class' docs
  * don't chomp Collection or s if prev character is ':',
    die instead, it can happen for annon collections based
    either on JDBI::Collection or J::Collection
 
 r5917 at cubic-pc (orig r5874):  cubic | 2008-09-20 02:57:01 +0400
  r5894 at cubic-pc:  cubic | 2008-09-19 23:01:45 +0400
  * cache load attempts in new_item method
  * update documentation
 
 r5918 at cubic-pc (orig r5875):  cubic | 2008-09-20 02:57:33 +0400
  r5895 at cubic-pc:  cubic | 2008-09-20 02:36:41 +0400
  * no need to setup pager, its constructor does that for us
  * actually it's really questionable that we need to setup pager
    when there is no paging by default
 
 r5919 at cubic-pc (orig r5876):  cubic | 2008-09-20 02:57:45 +0400
  r5896 at cubic-pc:  cubic | 2008-09-20 02:42:29 +0400
  * don't optimize left joins on mysql 5.0 and newer, may be other
    DBs can drop this too
 
 r5920 at cubic-pc (orig r5877):  cubic | 2008-09-20 03:17:33 +0400
  r5897 at cubic-pc:  cubic | 2008-09-20 02:46:17 +0400
  * return back code I've deleted accidently
 
 r5921 at cubic-pc (orig r5878):  cubic | 2008-09-20 03:17:41 +0400
  r5898 at cubic-pc:  cubic | 2008-09-20 02:48:16 +0400
  * cache load attempts in local static cache
  * move all code related inside a block
 
 r5922 at cubic-pc (orig r5879):  cubic | 2008-09-20 03:17:50 +0400
  r5899 at cubic-pc:  cubic | 2008-09-20 02:51:44 +0400
  * we know several accessors so there is no point to check if we can call them
 


Modified: Jifty-DBI/branches/tisql/Makefile.PL
==============================================================================
--- Jifty-DBI/branches/tisql/Makefile.PL	(original)
+++ Jifty-DBI/branches/tisql/Makefile.PL	Fri Sep 19 19:29:59 2008
@@ -11,7 +11,7 @@
 requires('Clone');
 requires('DBI');
 requires('DBIx::DBSchema' => '0.34');
-requires('Data::Page');
+requires('Data::Page' => '2.0');
 requires('DateTime' => 0.34);
 requires('DateTime::Format::ISO8601');
 requires('DateTime::Format::Strptime');

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI.pm	Fri Sep 19 19:29:59 2008
@@ -2,7 +2,7 @@
 use warnings;
 use strict;
 
-$Jifty::DBI::VERSION = '0.52';
+$Jifty::DBI::VERSION = '0.53';
 
 =head1 NAME
 

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Collection.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Collection.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Collection.pm	Fri Sep 19 19:29:59 2008
@@ -68,7 +68,7 @@
 use Clone;
 use Carp qw/croak/;
 use base qw/Class::Accessor::Fast/;
-__PACKAGE__->mk_accessors(qw/pager prefetch_related derived/);
+__PACKAGE__->mk_accessors(qw/pager prefetch_related derived _handle _is_limited rows_per_page/);
 
 =head1 METHODS
 
@@ -123,11 +123,7 @@
 
 sub _init_pager {
     my $self = shift;
-    $self->pager( Data::Page->new );
-
-    $self->pager->total_entries(0);
-    $self->pager->entries_per_page(10);
-    $self->pager->current_page(1);
+    return $self->pager( Data::Page->new(0, 10, 1) );
 }
 
 =head2 clean_slate
@@ -152,7 +148,6 @@
     $self->{'order'}            = "";
     $self->{'alias_count'}      = 0;
     $self->{'first_row'}        = 0;
-    $self->{'show_rows'}        = 0;
 
     delete $self->{$_} for qw(
         items
@@ -164,6 +159,7 @@
         criteria_count
     );
 
+    $self->rows_per_page(0);
     $self->implicit_clauses(%args);
     $self->_is_limited(0);
 }
@@ -184,14 +180,6 @@
 
 =cut
 
-sub _handle {
-    my $self = shift;
-    if (@_) {
-        $self->{'DBIxhandle'} = shift;
-    }
-    return ( $self->{'DBIxhandle'} );
-}
-
 =head2 _do_search
 
 This internal private method actually executes the search on the
@@ -213,25 +201,39 @@
     my @names = @{ $records->{NAME_lc} };
     my $data  = {};
 
-    my @tables = (
-        "main", map { $_->{alias} } values %{ $self->prefetch_related || {} }
-    );
+    my @tables = map { $_->{alias} } values %{ $self->prefetch_related || {} };
+
+    unless ( @tables ) {
+        while ( my $row = $records->fetchrow_hashref() ) {
+            $row->{ substr($_, 5) } = delete $row->{ $_ }
+                foreach grep rindex($_, "main_", 0) == 0, keys %$row;
+            my $item = $self->new_item;
+            $item->load_from_hash($row);
+            $self->add_record($item);
+        }
+        if ( $records->err ) {
+            $self->{'must_redo_search'} = 0;
+        }
+
+        return $self->_record_count;
+    }
 
     my @order;
+    my $i = 1;
     while ( my $base_row = $records->fetchrow_hashref() ) {
         my $main_pkey = $base_row->{ $names[0] };
+        $main_pkey = 'unique-'.$i++ if $self->{group_by};
         push @order, $main_pkey
             unless ( $order[0] && $order[-1] eq $main_pkey );
 
         # let's chop the row into subrows;
-        foreach my $table (@tables) {
-            for ( keys %$base_row ) {
-                if (/^$table\_(.*)$/) {
-                    $data->{$main_pkey}->{$table}
-                        ->{ ( $base_row->{ $table . '_id' } || $main_pkey ) }
-                        ->{$1} = $base_row->{$_};
-                }
+        foreach my $table ('main', @tables) {
+            my %tmp = ();
+            for my $k( grep rindex($_, $table ."_", 0) == 0, keys %$base_row ) {
+                $tmp{ substr($k, length($table)+1) } = $base_row->{ $k };
             }
+            $data->{$main_pkey}{$table}{ $base_row->{ $table . '_id' } || $main_pkey }
+                = \%tmp if keys %tmp;
         }
     }
 
@@ -460,15 +462,6 @@
 
 =cut
 
-sub _is_limited {
-    my $self = shift;
-    if (@_) {
-        $self->{'is_limited'} = shift;
-    } else {
-        return ( $self->{'is_limited'} );
-    }
-}
-
 =head2 build_select_query
 
 Builds a query string for a "SELECT rows from Tables" statement for
@@ -516,11 +509,10 @@
     my $self = shift;
 
     my @cols = ();
-    my $item = $self->new_item;
     if ( $self->{columns} and @{ $self->{columns} } ) {
         push @cols, @{ $self->{columns} };
     } else {
-        push @cols, $self->_qualified_record_columns( 'main' => $item );
+        push @cols, $self->_qualified_record_columns( 'main' => $self->new_item );
     }
     my %prefetch_related = %{ $self->prefetch_related || {} };
     foreach my $alias ( keys %prefetch_related ) {
@@ -528,7 +520,7 @@
 
         my $reference;
         if ( $class->isa('Jifty::DBI::Collection') ) {
-            $reference = $class->new->new_item( $self->_new_collection_args );
+            $reference = $class->new( $self->_new_collection_args )->new_item;
         } elsif ( $class->isa('Jifty::DBI::Record') ) {
             $reference = $class->new( $self->_new_record_args );
         }
@@ -560,15 +552,8 @@
     my $self  = shift;
     my $alias = shift;
     my $item  = shift;
-    grep {$_} map {
-        my $col = $_;
-        if ( $col->virtual ) {
-            undef;
-        } else {
-            $col = $col->name;
-            $alias . "." . $col . " as " . $alias . "_" . $col;
-        }
-    } $item->columns;
+    return map $alias ."." . $_ ." as ". $alias ."_". $_,
+        map $_->name, grep !$_->virtual, $item->columns;
 }
 
 =head2 prefetch PARAMHASH
@@ -997,9 +982,16 @@
 =head2 new_item
 
 Should return a new object of the correct type for the current collection.
+L</record_class> method is used to determine class of the object.
+
+Each record class at least once is loaded using require. This method is
+called each time a record fetched so load atemts are cached to avoid
+penalties. If you're sure that all record classes are loaded before
+first use then you can override this method.
 
 =cut
 
+{ my %cache = ();
 sub new_item {
     my $self  = shift;
     my $class = $self->record_class();
@@ -1007,15 +999,17 @@
     die "Jifty::DBI::Collection needs to be subclassed; override new_item\n"
         unless $class;
 
-    warn "$self $class\n" if $class =~ /::$/;
-    $class->require();
+    unless ( exists $cache{$class} ) {
+        $class->require;
+        $cache{$class} = undef;
+    }
     return $class->new( $self->_new_record_args );
-}
+} }
 
 =head2 record_class
 
 Returns the record class which this is a collection of; override this
-to subclass.  Or, pass it the name of a class an an argument after
+to subclass.  Or, pass it the name of a class as an argument after
 creating a C<Jifty::DBI::Collection> object to create an 'anonymous'
 collection class.
 
@@ -1023,9 +1017,9 @@
 the name of the record class for this collection.
 
 It uses a simple heuristic to determine the record class name -- It
-chops "Collection" off its own name. If you want to name your records
-and collections differently, go right ahead, but don't say we didn't
-warn you.
+chops "Collection" or "s" off its own name. If you want to name your
+records and collections differently, go right ahead, but don't say we
+didn't warn you.
 
 =cut
 
@@ -1037,7 +1031,7 @@
             if ref $self->{record_class};
     } elsif ( not ref $self or not $self->{record_class} ) {
         my $class = ref($self) || $self;
-        $class =~ s/(Collection|s)$//
+        $class =~ s/(?<!:)(Collection|s)$//
             || die "Can't guess record class from $class";
         return $class unless ref $self;
         $self->{record_class} = $class;
@@ -1180,10 +1174,10 @@
 sub limit {
     my $self = shift;
     my %args = (
-        table            => $self->table,
+        table            => undef,
+        alias            => undef,
         column           => undef,
         value            => undef,
-        alias            => undef,
         quote_value      => 1,
         entry_aggregator => 'or',
         case_sensitive   => undef,
@@ -1210,7 +1204,7 @@
     unless ( defined $args{'alias'} ) {
 
         #if the table we're looking at is the same as the main table
-        if ( $args{'table'} eq $self->table ) {
+        if ( !defined $args{'table'} || $args{'table'} eq $self->table ) {
 
             # TODO this code assumes no self joins on that table.
             # if someone can name a case where we'd want to do that,
@@ -1315,17 +1309,21 @@
 
     # If it's a new value or we're overwriting this sort of restriction,
 
-    my $case_sensitive = $column_obj ? $column_obj->case_sensitive : 0;
-    $case_sensitive = $args{'case_sensitive'}
-        if defined $args{'case_sensitive'};
-    if (   $self->_handle->case_sensitive
-        && defined $args{'value'}
-        && $args{'quote_value'}
-        && !$case_sensitive )
-    {
+    if ( defined $args{'value'} && $args{'quote_value'} ) {
+        my $case_sensitive = 0;
+        if ( defined $args{'case_sensitive'} ) {
+            $case_sensitive = $args{'case_sensitive'};
+        }
+        elsif ( $column_obj ) {
+            $case_sensitive = $column_obj->case_sensitive;
+        }
+        # don't worry about case for numeric columns_in_db
+        # only be case insensitive when we KNOW it's a text
+        if ( $column_obj && !$case_sensitive && !$column_obj->is_string ) {
+            $case_sensitive = 1;
+        }
 
-# don't worry about case for numeric columns_in_db - only be case insensitive when we KNOW it's a blob
-        if ( defined $column_obj ? $column_obj->is_string : 0 ) {
+        if ( !$case_sensitive && $self->_handle->case_sensitive ) {
             ( $qualified_column, $args{'operator'}, $args{'value'} )
                 = $self->_handle->_make_clause_case_insensitive(
                 $qualified_column, $args{'operator'}, $args{'value'} );
@@ -1804,11 +1802,7 @@
     my $self  = shift;
     my $table = shift;
 
-    $self->{'alias_count'}++;
-    my $alias = $table . "_" . $self->{'alias_count'};
-
-    return ($alias);
-
+    return $table . "_" . ++$self->{'alias_count'};
 }
 
 =head2 join
@@ -1899,13 +1893,6 @@
 
 =cut
 
-sub rows_per_page {
-    my $self = shift;
-    $self->{'show_rows'} = shift if (@_);
-
-    return ( $self->{'show_rows'} );
-}
-
 =head2 first_row
 
 Get or set the first row of the result set the database should return.
@@ -2137,7 +2124,8 @@
 
 =head2 columns LIST
 
-Specify that we want to load only the columns in LIST, which is a 
+Specify that we want to load only the columns in LIST, which should be
+a list of column names.
 
 =cut
 

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Filter.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Filter.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Filter.pm	Fri Sep 19 19:29:59 2008
@@ -86,16 +86,15 @@
     );
     my $self = {};
     bless $self, $class;
+    
+    $self->$_( delete $args{$_} )
+        foreach qw(record column value_ref handle);
 
-    for ( keys %args ) {
-        if ( $self->can($_) ) {
-            $self->$_( $args{$_} );
-        }
-
+    for ( grep $self->can($_), keys %args ) {
+        $self->$_( $args{$_} );
     }
 
     return ($self);
-
 }
 
 =head2 encode

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Handle/Pg.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Handle/Pg.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Handle/Pg.pm	Fri Sep 19 19:29:59 2008
@@ -175,7 +175,7 @@
     if ( $self->_case_insensitivity_valid( $column, $operator, $value ) ) {
         if ( $operator =~ /(?:LIKE|=|IN)/i ) {
             $column = "LOWER($column)";
-            if ( $operator eq 'IN' and ref($value) eq 'ARRAY' ) {
+            if ( $operator =~ /^(IN|=)$/i and ref($value) eq 'ARRAY' ) {
                 $value = [ map {"LOWER($_)"} @$value ];
             } else {
                 $value = "LOWER($value)";

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Handle/mysql.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Handle/mysql.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Handle/mysql.pm	Fri Sep 19 19:29:59 2008
@@ -58,7 +58,7 @@
 
 sub database_version {
     my $self = shift;
-    my $v    = $self->SUPER::database_version();
+    my $v    = $self->SUPER::database_version(@_);
 
     $v =~ s/\-.*$//;
     return ($v);
@@ -75,6 +75,12 @@
     return (undef);
 }
 
+sub _optimize_joins {
+    my $self = shift;
+    return $self->SUPER::_optimize_joins if $self->database_version =~ /^[34]/;
+    return;
+}
+
 1;
 
 __END__

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/HasFilters.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/HasFilters.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/HasFilters.pm	Fri Sep 19 19:29:59 2008
@@ -43,7 +43,8 @@
     my $self = shift;
     if (@_) {    # setting
         my @values = map { UNIVERSAL::isa( $_, 'ARRAY' ) ? @$_ : $_ } @_;
-        $self->_input_filters_accessor( [@values] );
+        $self->_input_filters_accessor( \@values );
+        return @values;
     }
 
     return @{ $self->_input_filters_accessor || [] };
@@ -63,14 +64,14 @@
     my $self = shift;
     if (@_) {    # setting
         my @values = map { UNIVERSAL::isa( $_, 'ARRAY' ) ? @$_ : $_ } @_;
-        $self->_output_filters_accessor( [@values] );
+        $self->_output_filters_accessor( \@values );
+        return @values;
     }
 
-    my @values = @{ $self->_output_filters_accessor || [] };
-    return @values if @values;
+    my $values = $self->_output_filters_accessor;
+    return @$values if $values && @$values;
 
-    @values = reverse $self->input_filters;
-    return @values;
+    return reverse $self->input_filters;
 }
 
 =head2 filters FILTERS

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Record.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Record.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Record.pm	Fri Sep 19 19:29:59 2008
@@ -258,6 +258,8 @@
     # Check for the correct column type when the Storable filter is in use
     if ( grep { $_ eq 'Jifty::DBI::Filter::Storable' }
         ( $column->input_filters, $column->output_filters )
+         and not grep { $_ eq 'Jifty::DBI::Filter::base64' }
+        ( $column->input_filters, $column->output_filters )
             and !$column->is_binary )
     {
         die "Column '$column_name' in @{[$column->record_class]} "
@@ -1027,7 +1029,15 @@
 The hash's keys are the columns to look at.
 
 The hash's values are either: scalar values to look for OR hash
-references which contain 'operator' and 'value'
+references which contain 'operator', 'value', 'case_sensitive' 
+or 'function'
+
+To load something case sensitively on a case insensitive database,
+you can do:
+
+  $record->load_by_cols( column => { operator => '=',
+                                     value => 'Foo',
+                                     case_sensitive => 1 } );
 
 =cut
 
@@ -1141,28 +1151,21 @@
 =cut
 
 sub load_from_hash {
-    my $class   = shift;
+    my $self    = shift;
     my $hashref = shift;
-    my ($self);
 
-    if ( ref($class) ) {
-        ( $self, $class ) = ( $class, undef );
-    } else {
-        $self = $class->new(
-            handle => ( delete $hashref->{'_handle'} || undef ) );
+    unless ( ref $self ) {
+        $self = $self->new( handle => delete $hashref->{'_handle'} );
     }
 
-    $self->{values} = {};
+    $self->{'values'} = {};
+    $self->{'fetched'} = {};
 
-    #foreach my $f ( keys %$hashref ) { $self->{'fetched'}{  $f } = 1; }
-    foreach my $col ( map { $_->name } $self->columns ) {
-        next unless exists $hashref->{ lc($col) };
+    foreach my $col ( grep exists $hashref->{ lc $_ }, map $_->name, $self->columns ) {
         $self->{'fetched'}{$col} = 1;
-        $self->{'values'}->{$col} = $hashref->{ lc($col) };
-
+        $self->{'values'}{$col} = $hashref->{ lc $col };
     }
 
-    #$self->{'values'}  = $hashref;
     $self->{'decoded'} = {};
     return $self->id();
 }
@@ -1555,6 +1558,7 @@
     return (shift)->_apply_filters( direction => 'output', @_ );
 }
 
+{ my %cache = ();
 sub _apply_filters {
     my $self = shift;
     my %args = (
@@ -1567,14 +1571,20 @@
     my @filters = $self->_filters(%args);
     my $action = $args{'direction'} eq 'output' ? 'decode' : 'encode';
     foreach my $filter_class (@filters) {
-        local $UNIVERSAL::require::ERROR;
-        $filter_class->require()
-            unless $INC{ join( '/', split( /::/, $filter_class ) ) . ".pm" };
-
-        if ($UNIVERSAL::require::ERROR) {
-            warn $UNIVERSAL::require::ERROR;
+        unless ( exists $cache{ $filter_class } ) {
+            local $UNIVERSAL::require::ERROR;
+            $filter_class->require;
+            if ($UNIVERSAL::require::ERROR) {
+                warn $UNIVERSAL::require::ERROR;
+                $cache{ $filter_class } = 0;
+                next;
+            }
+            $cache{ $filter_class } = 1;
+        }
+        elsif ( !$cache{ $filter_class } ) {
             next;
         }
+
         my $filter = $filter_class->new(
             record    => $self,
             column    => $args{'column'},
@@ -1585,7 +1595,7 @@
         # XXX TODO error proof this
         $filter->$action();
     }
-}
+} }
 
 =head2 is_distinct COLUMN_NAME, VALUE
 
@@ -1666,7 +1676,7 @@
     }
 }
 
-=head2 run_validation_for_column column => 'COLUMN', value => 'VALUE'
+=head2 run_validation_for_column column => 'COLUMN', value => 'VALUE' [extra => \@ARGS]
 
 Runs all validators for the specified column.
 
@@ -1677,13 +1687,14 @@
     my %args = (
         column => undef,
         value  => undef,
+        extra  => [],
         @_
     );
     my $key  = $args{'column'};
     my $attr = $args{'value'};
 
     my ( $ret, $results )
-        = $self->_run_callback( name => "validate_" . $key, args => $attr );
+        = $self->_run_callback( name => "validate_" . $key, args => $attr, extra => $args{'extra'} );
 
     if ( defined $ret ) {
         return ( 1, 'Validation ok' );
@@ -1718,6 +1729,7 @@
         name => undef,
         args => undef,
         short_circuit => 1,
+        extra => [],
         @_
     );
 
@@ -1725,11 +1737,11 @@
     my $method = $args{'name'};
     my @results;
     if ( my $func = $self->can($method) ) {
-        @results = $func->( $self, $args{args} );
+        @results = $func->( $self, $args{args}, @{$args{'extra'}} );
         return ( wantarray ? ( undef, [ [@results] ] ) : undef )
             if $args{short_circuit} and not $results[0];
     }
-    $ret = $self->call_trigger( $args{'name'} => $args{args} );
+    $ret = $self->call_trigger( $args{'name'} => $args{args}, @{$args{'extra'}} );
     return (
         wantarray
         ? ( $ret, [ [@results], @{ $self->last_trigger_results } ] )

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Record/Cachable.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Record/Cachable.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Record/Cachable.pm	Fri Sep 19 19:29:59 2008
@@ -56,7 +56,7 @@
     my $self  = shift;
     my $cache = $self->_handle->dsn
         . "-KEYS--"
-        . ( $self->{'_class'} ||= ref($self) );
+        . ( $self->{'_class'} || $self->table );
     $self->_setup_cache($cache) unless exists( $_CACHES{$cache} );
     return ( $_CACHES{$cache} );
 
@@ -72,14 +72,14 @@
     my $self  = shift;
     my $cache = $self->_handle->dsn
         . "-KEYS--"
-        . ( $self->{'_class'} ||= ref($self) );
+        . ( $self->{'_class'} || $self->table );
     $self->_setup_cache($cache);
 }
 
 sub _record_cache {
     my $self = shift;
     my $cache
-        = $self->_handle->dsn . "--" . ( $self->{'_class'} ||= ref($self) );
+        = $self->_handle->dsn . "--" . ( $self->{'_class'} || $self->table );
     $self->_setup_cache($cache) unless exists( $_CACHES{$cache} );
     return ( $_CACHES{$cache} );
 

Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Schema.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Schema.pm	(original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Schema.pm	Fri Sep 19 19:29:59 2008
@@ -349,14 +349,21 @@
 #            $refclass->require();
             die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
         }
-        # References assume a refernce to an integer ID unless told otherwise
-        $column->type('integer') unless ( $column->type );
-
         # A one-to-one or one-to-many relationship is requested
         if ( UNIVERSAL::isa( $refclass, 'Jifty::DBI::Record' ) ) {
 
+            my $by = $column->by;
+            unless ( $column->type ) {
+                # XXX, TODO: we must set column type to the same value
+                # as column we refer to.
+                #
+                # my $referenced_column = $refclass->column( $by );
+                # $column->type( $referenced_column->type );
+                $column->type('integer');
+            }
+
             # Handle *_id reference columns specially
-            if ( $name =~ /(.*)_id$/ ) {
+            if ( $name =~ /(.*)_\Q$by\E$/ ) {
                 my $aliased_as = $1;
                 my $virtual_column = $from->add_column($aliased_as);
 
@@ -373,8 +380,24 @@
 
                 # Create the helper methods for the virtual column too
                 $from->_init_methods_for_column($virtual_column);
-            }
+            } else {
+                my $aliased_as = $name .'_'. $by;
+                my $virtual_column = $from->add_column($aliased_as);
+
+                # Clone ourselves into the virtual column
+                %$virtual_column = %$column;
+
+                # This column is now just the ID, the virtual holds the ref
+                $virtual_column->refers_to(undef); $virtual_column->by(undef);
 
+                # Note the original column
+                $virtual_column->aliased_as($aliased_as);
+                $virtual_column->alias_for_column($name);
+                $virtual_column->virtual(1);
+
+                # Create the helper methods for the virtual column too
+                $from->_init_methods_for_column($virtual_column);
+            }
         } elsif ( UNIVERSAL::isa( $refclass, 'Jifty::DBI::Collection' ) ) {
             $column->virtual('1');
         } else {
@@ -437,23 +460,84 @@
 =head2 references
 
 Indicates that the column references an object or a collection of objects in another
-class.  You may refer to either a class that inherits from Jifty::Record by a primary
-key in that class or to a class that inherits from Jifty::Collection.
+class.  You may refer to either a class that inherits from L<Jifty::Record> by a primary
+key in that class or to a class that inherits from L<Jifty::Collection>.
+
+=head3 referencing a record
 
 Correct usage is C<references Application::Model::OtherClass by 'column_name'>, where
-Application::Model::OtherClass is a valid Jifty model and C<'column_name'> is
-a column containing unique values in OtherClass.  You can omit C<by 'column_name'> and
-the column name 'id' will be used.
-
-If you are referring to a Jifty::Collection then you must specify C<by 'column_name'>.
-
-When accessing the value in the column the actual object referenced will be returned for
-refernces to Jifty::Records and a reference to a Jifty::Collection will be returned for
-columns referring to Jifty::Collections.
-
-For columns referring to Jifty::Records you can access the actual value of the column
-instead of the object reference by appending '_id' to the column name.  As a result,
-you may not end any column name which uses 'references' using '_id'.
+Application::Model::OtherClass is a valid Jifty model, subclass of L<Jifty::Record>,
+and C<'column_name'> is a distinct column of OtherClass. You can omit C<by 'column_name'>
+and the column name 'id' will be used.
+
+At this moment you must specify type of the column your self to match type of the column
+you refer to.
+
+You can name a column as combination of 'name' and 'by', for example:
+
+    column user_name => references App::Model::User by 'name', type is 'varchar(64)';
+
+Then user, user_name and respective setters will be generated. user method will return
+object, user_name will return actual value. Note that if you're using some magic on load
+for user records then to get real name of loaded record you should use C<< $record->user->name >>
+instead.
+
+In the above case name of the column in the DB will be 'user_name'. If you don't like
+suffixes like '_id', '_name' and other in the DB then you can name column without suffix,
+for example:
+
+    column user => references App::Model::User by 'name', type is 'varchar(64)';
+
+In this case name of the column in the DB will be 'user', accessors will be the same
+as in above example.
+
+=head3 referencing a collection
+
+Correct usage is C<references Application::Model::OtherCollection by 'column_name'>, where
+Application::Model::OtherCollection is a valid Jifty model, subclass of L<Jifty::Collection>,
+and C<'column_name'> is a column of records in OtherCollection. In this case  C<by 'column_name'>
+is not optional.
+
+Columns that refers to a collection are virtual and can be changed. So such columns in a model
+doesn't create real columns in the DB, but instead it's way to name collection of records that
+refer to this records.
+
+=head3 example
+
+Simple model with users and multiple phone records per user:
+
+    package TestApp::Model::User;
+    use Jifty::DBI::Schema;
+    use Jifty::DBI::Record schema {
+        column name  => type is 'varchar(18)';
+        ...
+        column phones => references TestApp::Model::PhoneCollection by 'user';
+    };
+
+    package TestApp::Model::Phone;
+    use Jifty::DBI::Schema;
+    use Jifty::DBI::Record schema {
+        column user  => references TestApp::Model::User by 'id',
+            is mandatory;
+        column type  => ...;
+        column value => ...;
+    };
+
+From a user record you get his phones and do something:
+
+    my $phones = $user->phones;
+    while ( my $phone = $phones->next ) {
+        ...
+    }
+
+From a phone record you can get its owner or change it:
+
+    my $user_object = $phone->user;
+    my $user_id = $phone->user_id;
+
+    $phone->set_user( $new_owner_object );
+    $phone->set_user( 123 );    # using id
+    $phone->set_user_id( 123 ); # the same, but only using id
 
 =head2 refers_to
 


More information about the Jifty-commit mailing list