Merge 'trunk' into 'DBIx-Class-resultset'
Matt S Trout [Tue, 20 Dec 2005 01:59:46 +0000 (01:59 +0000)]
r4570@obrien (orig r392):  castaway | 2005-12-14 14:22:23 +0000
Patches from Andreas Hartmeier applied to PK::Auto

r4601@obrien (orig r396):  marcus | 2005-12-15 15:59:43 +0000
make more sane error message for missing table.
r4632@obrien (orig r397):  bluefeet | 2005-12-15 17:07:45 +0000
Adding first version of DBIx::Class::Validation.
r4633@obrien (orig r398):  ningu | 2005-12-15 22:59:01 +0000
- PK::Auto doc patch from dwc
r4641@obrien (orig r400):  dwc | 2005-12-18 17:59:04 +0000
Update examples to be more explicit about load_components order

r4642@obrien (orig r401):  bricas | 2005-12-19 20:19:14 +0000
added cookbook example: setting default values

13 files changed:
Build.PL
lib/DBIx/Class.pm
lib/DBIx/Class/CDBICompat/Retrieve.pm
lib/DBIx/Class/Core.pm
lib/DBIx/Class/PK.pm
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/ResultSetInstance.pm [new file with mode: 0644]
lib/DBIx/Class/Schema.pm
lib/DBIx/Class/Storage/DBI.pm
lib/DBIx/Class/Table.pm
lib/DBIx/Class/TableInstance.pm [new file with mode: 0644]
t/20setuperrors.t
t/cdbi-t/12-filter.t

index 6061e4d..da41963 100644 (file)
--- a/Build.PL
+++ b/Build.PL
@@ -17,6 +17,7 @@ my %arguments = (
            'Tie::IxHash'               => 0,
         'Module::Find'              => 0,
         'Storable'                  => 0,
+        'Class::Data::Accessor'     => 0.01,
         # Following for CDBICompat only
         'Class::Trigger'            => 0,
         'DBIx::ContextualFetch'     => 0,
index 9f02215..7fb6f60 100644 (file)
@@ -4,9 +4,11 @@ use strict;
 use warnings;
 
 use vars qw($VERSION);
-use base qw/DBIx::Class::Componentised Class::Data::Inheritable/;
+use base qw/DBIx::Class::Componentised Class::Data::Accessor/;
 
-$VERSION = '0.04001';
+sub mk_classdata { shift->mk_classaccessor(@_); }
+
+$VERSION = '0.04999_01';
 
 1;
 
index 6937fb3..31dd128 100644 (file)
@@ -5,7 +5,12 @@ use warnings FATAL => 'all';
 
 sub retrieve          { shift->find(@_)            }
 sub retrieve_all      { shift->search              }
-sub retrieve_from_sql { shift->search_literal(@_)  }
+
+sub retrieve_from_sql {
+  my ($class, $cond, @rest) = @_;
+  $cond =~ s/^\s*WHERE//i;
+  $class->search_literal($cond, @rest);
+}
 
 sub count_all         { shift->count               }
   # Contributed by Numa. No test for this though.
index 10a776f..88d3c4b 100644 (file)
@@ -11,7 +11,8 @@ __PACKAGE__->load_components(qw/
   Relationship
   PK
   Row
-  Table
+  TableInstance
+  ResultSetInstance
   Exception
   AccessorGroup
   Validation/);
index eb2540d..4d46421 100644 (file)
@@ -79,10 +79,10 @@ sub find {
   $class->throw( "Can't find unless all primary keys are specified" )
     unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc
                                   # column names etc. Not sure what to do yet
-  #return $class->search($query)->next;
-  my @cols = $class->_select_columns;
-  my @row = $class->storage->select_single($class->_table_name, \@cols, $query);
-  return (@row ? $class->_row_to_object(\@cols, \@row) : ());
+  return $class->search($query)->next;
+  #my @cols = $class->_select_columns;
+  #my @row = $class->storage->select_single($class->_table_name, \@cols, $query);
+  #return (@row ? $class->_row_to_object(\@cols, \@row) : ());
 }
 
 =head2 discard_changes
index 597eef5..ce038db 100644 (file)
@@ -33,13 +33,13 @@ any queries -- these are executed as needed by the other methods.
 =cut
 
 sub new {
-  my ($it_class, $db_class, $attrs) = @_;
+  my ($class, $source, $attrs) = @_;
   #use Data::Dumper; warn Dumper(@_);
-  $it_class = ref $it_class if ref $it_class;
+  $class = ref $class if ref $class;
   $attrs = { %{ $attrs || {} } };
   my %seen;
-  $attrs->{cols} ||= [ map { "me.$_" } $db_class->_select_columns ];
-  $attrs->{from} ||= [ { 'me' => $db_class->_table_name } ];
+  $attrs->{cols} ||= [ map { "me.$_" } $source->columns ];
+  $attrs->{from} ||= [ { 'me' => $source->name } ];
   if ($attrs->{join}) {
     foreach my $j (ref $attrs->{join} eq 'ARRAY'
               ? (@{$attrs->{join}}) : ($attrs->{join})) {
@@ -49,46 +49,115 @@ sub new {
         $seen{$j} = 1;
       }
     }
-    push(@{$attrs->{from}}, $db_class->_resolve_join($attrs->{join}, 'me'));
+    push(@{$attrs->{from}}, $source->result_class->_resolve_join($attrs->{join}, 'me'));
   }
   foreach my $pre (@{$attrs->{prefetch} || []}) {
-    push(@{$attrs->{from}}, $db_class->_resolve_join($pre, 'me'))
+    push(@{$attrs->{from}}, $source->result_class->_resolve_join($pre, 'me'))
       unless $seen{$pre};
     push(@{$attrs->{cols}},
       map { "$pre.$_" }
-      $db_class->_relationships->{$pre}->{class}->_select_columns);
+      $source->result_class->_relationships->{$pre}->{class}->table->columns);
   }
   my $new = {
-    class => $db_class,
-    cols => $attrs->{cols} || [ $db_class->_select_columns ],
+    source => $source,
+    result_class => $source->result_class,
+    cols => $attrs->{cols},
     cond => $attrs->{where},
-    from => $attrs->{from} || $db_class->_table_name,
+    from => $attrs->{from},
     count => undef,
     pager => undef,
     attrs => $attrs };
-  bless ($new, $it_class);
+  bless ($new, $class);
   $new->pager if ($attrs->{page});
   return $new;
 }
 
+=head2 search
+
+  my @obj    = $rs->search({ foo => 3 }); # "... WHERE foo = 3"              
+  my $new_rs = $rs->search({ foo => 3 });                                    
+                                                                                
+If you need to pass in additional attributes but no additional condition,
+call it as ->search(undef, \%attrs);
+                                                                                
+  my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
+
+=cut
+
+sub search {
+  my $self = shift;
+
+  #use Data::Dumper;warn Dumper(@_);
+
+  my $attrs = { %{$self->{attrs}} };
+  if (@_ > 1 && ref $_[$#_] eq 'HASH') {
+    $attrs = { %{ pop(@_) } };
+  }
+
+  my $where = ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_});
+  if (defined $where) {
+    $where = (defined $attrs->{where}
+                ? { '-and' => [ $where, $attrs->{where} ] }
+                : $where);
+    $attrs->{where} = $where;
+  }
+
+  my $rs = $self->new($self->{source}, $attrs);
+
+  return (wantarray ? $rs->all : $rs);
+}
+
+=head2 search_literal                                                              
+  my @obj    = $rs->search_literal($literal_where_cond, @bind);
+  my $new_rs = $rs->search_literal($literal_where_cond, @bind);
+
+Pass a literal chunk of SQL to be added to the conditional part of the
+resultset
+
+=cut
+                                                         
+sub search_literal {
+  my ($self, $cond, @vals) = @_;
+  my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
+  $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
+  return $self->search(\$cond, $attrs);
+}
+
 =head2 cursor
 
-Return a storage-driven cursor to the given resultset.
+Returns a storage-driven cursor to the given resultset.
 
 =cut
 
 sub cursor {
   my ($self) = @_;
-  my ($db_class, $attrs) = @{$self}{qw/class attrs/};
+  my ($source, $attrs) = @{$self}{qw/source attrs/};
   if ($attrs->{page}) {
     $attrs->{rows} = $self->pager->entries_per_page;
     $attrs->{offset} = $self->pager->skipped;
   }
   return $self->{cursor}
-    ||= $db_class->storage->select($self->{from}, $self->{cols},
+    ||= $source->storage->select($self->{from}, $self->{cols},
           $attrs->{where},$attrs);
 }
 
+=head2 search_like                                                               
+                                                                                
+Identical to search except defaults to 'LIKE' instead of '=' in condition       
+                                                                                
+=cut                                                                            
+
+sub search_like {
+  my $class    = shift;
+  my $attrs = { };
+  if (@_ > 1 && ref $_[$#_] eq 'HASH') {
+    $attrs = pop(@_);
+  }
+  my $query    = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
+  $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
+  return $class->search($query, { %$attrs });
+}
+
 =head2 slice($first, $last)
 
 Returns a subset of elements from the resultset.
@@ -98,10 +167,10 @@ Returns a subset of elements from the resultset.
 sub slice {
   my ($self, $min, $max) = @_;
   my $attrs = { %{ $self->{attrs} || {} } };
-  $self->{class}->throw("Can't slice without where") unless $attrs->{where};
+  $self->{source}->result_class->throw("Can't slice without where") unless $attrs->{where};
   $attrs->{offset} = $min;
   $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
-  my $slice = $self->new($self->{class}, $attrs);
+  my $slice = $self->new($self->{source}, $attrs);
   return (wantarray ? $slice->all : $slice);
 }
 
@@ -125,17 +194,17 @@ sub _construct_object {
   @cols = grep { /\(/ or ! /\./ } @cols;
   my $new;
   unless ($self->{attrs}{prefetch}) {
-    $new = $self->{class}->_row_to_object(\@cols, \@row);
+    $new = $self->{source}->result_class->_row_to_object(\@cols, \@row);
   } else {
     my @main = splice(@row, 0, scalar @cols);
-    $new = $self->{class}->_row_to_object(\@cols, \@main);
+    $new = $self->{source}->result_class->_row_to_object(\@cols, \@main);
     PRE: foreach my $pre (@{$self->{attrs}{prefetch}}) {
-      my $rel_obj = $self->{class}->_relationships->{$pre};
-      my $pre_class = $self->{class}->resolve_class($rel_obj->{class});
+      my $rel_obj = $self->{source}->result_class->_relationships->{$pre};
+      my $pre_class = $self->{source}->result_class->resolve_class($rel_obj->{class});
       my @pre_cols = $pre_class->_select_columns;
       my @vals = splice(@row, 0, scalar @pre_cols);
       my $fetched = $pre_class->_row_to_object(\@pre_cols, \@vals);
-      $self->{class}->throw("No accessor for prefetched $pre")
+      $self->{source}->result_class->throw("No accessor for prefetched $pre")
         unless defined $rel_obj->{attrs}{accessor};
       if ($rel_obj->{attrs}{accessor} eq 'single') {
         foreach my $pri ($rel_obj->{class}->primary_columns) {
@@ -148,7 +217,7 @@ sub _construct_object {
       } elsif ($rel_obj->{attrs}{accessor} eq 'filter') {
         $new->{_inflated_column}{$pre} = $fetched;
       } else {
-        $self->{class}->throw("Don't know how to store prefetched $pre");
+        $self->{source}->result_class->throw("Don't know how to store prefetched $pre");
       }
     }
   }
@@ -160,21 +229,22 @@ sub _construct_object {
 =head2 count
 
 Performs an SQL C<COUNT> with the same query as the resultset was built
-with to find the number of elements.
+with to find the number of elements. If passed arguments, does a search
+on the resultset and counts the results of that.
 
 =cut
 
 sub count {
-  my ($self) = @_;
-  my $db_class = $self->{class};
+  my $self = shift;
+  return $self->search(@_)->count if @_ && defined $_[0];
   my $attrs = { %{ $self->{attrs} } };
   unless ($self->{count}) {
     # offset and order by are not needed to count
     delete $attrs->{$_} for qw/offset order_by/;
         
     my @cols = 'COUNT(*)';
-    $self->{count} = $db_class->storage->select_single($self->{from}, \@cols,
-                                              $self->{cond}, $attrs);
+    $self->{count} = $self->{source}->storage->select_single(
+        $self->{from}, \@cols, $self->{cond}, $attrs);
   }
   return 0 unless $self->{count};
   return $self->{pager}->entries_on_this_page if ($self->{pager});
@@ -183,6 +253,14 @@ sub count {
     : $self->{count};
 }
 
+=head2 count_literal
+
+Calls search_literal with the passed arguments, then count.
+
+=cut
+
+sub count_literal { shift->search_literal(@_)->count; }
+
 =head2 all
 
 Returns all elements in the resultset. Called implictly if the resultset
@@ -260,7 +338,7 @@ sub page {
   my ($self, $page) = @_;
   my $attrs = $self->{attrs};
   $attrs->{page} = $page;
-  return $self->new($self->{class}, $attrs);
+  return $self->new($self->{source}, $attrs);
 }
 
 =head1 Attributes
diff --git a/lib/DBIx/Class/ResultSetInstance.pm b/lib/DBIx/Class/ResultSetInstance.pm
new file mode 100644 (file)
index 0000000..736ac71
--- /dev/null
@@ -0,0 +1,13 @@
+package DBIx::Class::ResultSetInstance;
+
+use base qw/DBIx::Class/;
+
+sub search         { shift->resultset_instance->search(@_);         }
+sub search_literal { shift->resultset_instance->search_literal(@_); }
+sub search_like    { shift->resultset_instance->search_like(@_);    }
+sub count          { shift->resultset_instance->count(@_);          }
+sub count_literal  { shift->resultset_instance->count_literal(@_);  }
+
+__PACKAGE__->mk_classdata('resultset_instance');
+
+1;
index 4c4942b..fa8e850 100644 (file)
@@ -167,7 +167,9 @@ sub compose_connection {
   while (my ($comp, $comp_class) = each %reg) {
     my $target_class = "${target}::${comp}";
     $class->inject_base($target_class, $comp_class, $conn_class);
-    $target_class->table($comp_class->table);
+    my $table = $comp_class->table->new({ %{$comp_class->table} });
+    $table->result_class($target_class);
+    $target_class->table($table);
     @map{$comp, $comp_class} = ($target_class, $target_class);
   }
   {
index 3c935d8..ee944ad 100644 (file)
@@ -182,7 +182,7 @@ sub _execute {
   my ($sql, @bind) = $self->sql_maker->$op($ident, @args);
   unshift(@bind, @$extra_bind) if $extra_bind;
   warn "$sql: @bind" if $self->debug;
-  my $sth = $self->sth($sql);
+  my $sth = $self->sth($sql,$op);
   @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args
   my $rv = $sth->execute(@bind);
   return (wantarray ? ($rv, $sth, @bind) : $rv);
@@ -233,7 +233,9 @@ sub select_single {
 }
 
 sub sth {
-  shift->dbh->prepare(@_);
+  my ($self, $sql, $op) = @_;
+  my $meth = (defined $op && $op ne 'select' ? 'prepare_cached' : 'prepare');
+  return $self->dbh->$meth($sql);
 }
 
 1;
index 21eaa23..01771ee 100644 (file)
@@ -4,25 +4,18 @@ use strict;
 use warnings;
 
 use DBIx::Class::ResultSet;
-use Data::Page;
 
 use Carp qw/croak/;
 
 use base qw/DBIx::Class/;
+__PACKAGE__->load_components(qw/AccessorGroup/);
 
-__PACKAGE__->mk_classdata('_columns' => {});
-
-__PACKAGE__->mk_classdata('_table_name');
-
-__PACKAGE__->mk_classdata('table_alias'); # FIXME: Doesn't actually do anything yet!
-
-__PACKAGE__->mk_classdata('_resultset_class' => 'DBIx::Class::ResultSet');
-
-sub iterator_class { shift->_resultset_class(@_) }
+__PACKAGE__->mk_group_accessors('simple' =>
+  qw/_columns name resultset_class result_class storage/);
 
 =head1 NAME 
 
-DBIx::Class::Table - Basic table methods
+DBIx::Class::Table - Table object
 
 =head1 SYNOPSIS
 
@@ -35,162 +28,51 @@ L<DBIx::Class> classes.
 
 =cut
 
-sub _register_columns {
-  my ($class, @cols) = @_;
-  my $names = { %{$class->_columns} };
-  $names->{$_} ||= {} for @cols;
-  $class->_columns($names); 
+sub new {
+  my ($class, $attrs) = @_;
+  $class = ref $class if ref $class;
+  my $new = bless($attrs || {}, $class);
+  $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
+  $new->{_columns} ||= {};
+  $new->{name} ||= "!!NAME NOT SET!!";
+  return $new;
 }
 
-sub _mk_column_accessors {
-  my ($class, @cols) = @_;
-  $class->mk_group_accessors('column' => @cols);
-}
-
-=head2 add_columns
-
-  __PACKAGE__->add_columns(qw/col1 col2 col3/);
-
-Adds columns to the current class and creates accessors for them.
-
-=cut
-
 sub add_columns {
-  my ($class, @cols) = @_;
-  $class->_register_columns(@cols);
-  $class->_mk_column_accessors(@cols);
-}
-
-=head2 search_literal
-
-  my @obj    = $class->search_literal($literal_where_cond, @bind);
-  my $cursor = $class->search_literal($literal_where_cond, @bind);
-
-=cut
-
-sub search_literal {
-  my ($class, $cond, @vals) = @_;
-  $cond =~ s/^\s*WHERE//i;
-  my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
-  $attrs->{bind} = \@vals;
-  return $class->search(\$cond, $attrs);
-}
-
-=head2 count_literal
-
-  my $count = $class->count_literal($literal_where_cond);
-
-=cut
-
-sub count_literal {
-  my $class = shift;
-  return $class->search_literal(@_)->count;
+  my ($self, @cols) = @_;
+  while (my $col = shift @cols) {
+    $self->add_column($col => (ref $cols[0] ? shift : {}));
+  }
 }
 
-=head2 count
-
-  my $count = $class->count({ foo => 3 });
-
-=cut
-
-sub count {
-  my $class = shift;
-  return $class->search(@_)->count;
+sub add_column {
+  my ($self, $col, $info) = @_;
+  $self->_columns->{$col} = $info || {};
 }
 
-=head2 search 
-
-  my @obj    = $class->search({ foo => 3 }); # "... WHERE foo = 3"
-  my $cursor = $class->search({ foo => 3 });
-
-To retrieve all rows, simply call C<search()> with no condition parameter,
+=head2 add_columns
 
-  my @all = $class->search(); # equivalent to search({})
+  $table->add_columns(qw/col1 col2 col3/);
 
-If you need to pass in additional attributes (see
-L<DBIx::Class::ResultSet/Attributes> for details) an empty hash indicates
-no condition,
+  $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...);
 
-  my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
+Adds columns to the table object. If supplied key => hashref pairs uses
+the hashref as the column_info for that column.
 
 =cut
 
-sub search {
-  my $class = shift;
-  my $attrs = { };
-  croak "Table not defined for ". ( ref $class || $class ) unless $class->table();
-  if (@_ > 1 && ref $_[$#_] eq 'HASH') {
-    $attrs = { %{ pop(@_) } };
-  }
-  $attrs->{where} = (@_ == 1 || ref $_[0] eq "HASH" ? shift: {@_});
-  
-  my $rs = $class->resultset($attrs);
-  
-  return (wantarray ? $rs->all : $rs);
-}
-
 sub resultset {
-  my $class = shift;
-
-  my $rs_class = $class->_resultset_class;
+  my $self = shift;
+  my $rs_class = $self->resultset_class;
   eval "use $rs_class;";
-  my $rs = $rs_class->new($class, @_);
-}
-
-=head2 search_like
-
-Identical to search except defaults to 'LIKE' instead of '=' in condition
-
-=cut
-
-sub search_like {
-  my $class    = shift;
-  my $attrs = { };
-  if (@_ > 1 && ref $_[$#_] eq 'HASH') {
-    $attrs = pop(@_);
-  }
-  my $query    = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
-  $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
-  return $class->search($query, { %$attrs });
-}
-
-sub _select_columns {
-  return keys %{$_[0]->_columns};
-}
-
-=head2 table
-
-  __PACKAGE__->table('tbl_name');
-  
-Gets or sets the table name.
-
-=cut
-
-sub table {
-  shift->_table_name(@_);
-}
-
-=head2 find_or_create
-
-  $class->find_or_create({ key => $val, ... });
-
-Searches for a record matching the search condition; if it doesn't find one,
-creates one and returns that instead.
-
-=cut
-
-sub find_or_create {
-  my $class    = shift;
-  my $hash     = ref $_[0] eq "HASH" ? shift: {@_};
-  my $exists = $class->find($hash);
-  return defined($exists) ? $exists : $class->create($hash);
+  return $rs_class->new($self);
 }
 
 =head2 has_column                                                                
                                                                                 
   if ($obj->has_column($col)) { ... }                                           
                                                                                 
-Returns 1 if the class has a column of this name, 0 otherwise.                  
+Returns 1 if the table has a column of this name, 0 otherwise.                  
                                                                                 
 =cut                                                                            
 
diff --git a/lib/DBIx/Class/TableInstance.pm b/lib/DBIx/Class/TableInstance.pm
new file mode 100644 (file)
index 0000000..81c3c39
--- /dev/null
@@ -0,0 +1,145 @@
+package DBIx::Class::TableInstance;
+
+use strict;
+use warnings;
+
+use base qw/DBIx::Class/;
+use DBIx::Class::Table;
+
+__PACKAGE__->mk_classdata('table_alias'); # FIXME: Doesn't actually do anything yet!
+
+__PACKAGE__->mk_classdata('_resultset_class' => 'DBIx::Class::ResultSet');
+
+sub iterator_class { shift->table->resultset_class(@_) }
+sub resultset_class { shift->table->resultset_class(@_) }
+sub _table_name { shift->table->name }
+
+=head1 NAME 
+
+DBIx::Class::TableInstance - provides a classdata table object and method proxies
+
+=head1 SYNOPSIS
+
+  __PACKAGE__->table('foo');
+  __PACKAGE__->add_columns(qw/id bar baz/);
+  __PACKAGE__->set_primary_key('id');
+
+=head1 METHODS
+
+=cut
+
+sub _mk_column_accessors {
+  my ($class, @cols) = @_;
+  $class->mk_group_accessors('column' => @cols);
+}
+
+=head2 add_columns
+
+  __PACKAGE__->add_columns(qw/col1 col2 col3/);
+
+Adds columns to the current class and creates accessors for them.
+
+=cut
+
+sub add_columns {
+  my ($class, @cols) = @_;
+  $class->table->add_columns(@cols);
+  $class->_mk_column_accessors(@cols);
+}
+
+sub resultset_instance {
+  my $class = shift;
+  $class->table->storage($class->storage);
+  $class->next::method($class->table->resultset);
+}
+
+sub _select_columns {
+  return shift->table->columns;
+}
+
+=head2 table
+
+  __PACKAGE__->table('tbl_name');
+  
+Gets or sets the table name.
+
+=cut
+
+sub table {
+  my ($class, $table) = @_;
+  die "$class->table called and no table instance set yet" unless $table;
+  unless (ref $table) {
+    $table = DBIx::Class::Table->new(
+      {
+        name => $table,
+        result_class => $class,
+        #storage => $class->storage,
+      });
+  }
+  $class->mk_classdata('table' => $table);
+}
+
+=head2 find_or_create
+
+  $class->find_or_create({ key => $val, ... });
+
+Searches for a record matching the search condition; if it doesn't find one,
+creates one and returns that instead.
+
+=cut
+
+sub find_or_create {
+  my $class    = shift;
+  my $hash     = ref $_[0] eq "HASH" ? shift: {@_};
+  my $exists = $class->find($hash);
+  return defined($exists) ? $exists : $class->create($hash);
+}
+
+=head2 has_column                                                                
+                                                                                
+  if ($obj->has_column($col)) { ... }                                           
+                                                                                
+Returns 1 if the class has a column of this name, 0 otherwise.                  
+                                                                                
+=cut                                                                            
+
+sub has_column {
+  my ($self, $column) = @_;
+  return $self->table->has_column($column);
+}
+
+=head2 column_info                                                               
+                                                                                
+  my $info = $obj->column_info($col);                                           
+                                                                                
+Returns the column metadata hashref for a column.
+                                                                                
+=cut                                                                            
+
+sub column_info {
+  my ($self, $column) = @_;
+  return $self->table->column_info($column);
+}
+
+=head2 columns                                                                   
+                                                                                
+  my @column_names = $obj->columns;                                             
+                                                                                
+=cut                                                                            
+
+sub columns {
+  return shift->table->columns(@_);
+}
+
+1;
+
+=head1 AUTHORS
+
+Matt S. Trout <mst@shadowcatsystems.co.uk>
+
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
+=cut
+
index f082e93..3d36bd4 100644 (file)
@@ -7,6 +7,7 @@ eval {
   use base 'DBIx::Class';
 
   __PACKAGE__->load_components qw/Core/;
+  __PACKAGE__->table('buggy_table');
   __PACKAGE__->columns qw/this doesnt work as expected/;
 };
 
index ecaff02..adab754 100644 (file)
@@ -158,6 +158,7 @@ sub slice { qw/fred barney/ }
 package main;
 
 Actor->iterator_class('Class::DBI::My::Iterator');
+Actor->resultset_instance(Actor->construct_resultset);
 
 {
        my @acts = $film->actors->slice(1, 2);