Merge 'trunk' into 'cdbicompat_integration'
Michael G Schwern [Thu, 21 Feb 2008 08:03:47 +0000 (08:03 +0000)]
r54378@windhund (orig r3954):  matthewt | 2008-01-18 05:03:08 -0800
added strict and warnings to HashRefInflator, fixed inflation for empty has_many rels
r54379@windhund (orig r3955):  matthewt | 2008-01-20 04:28:55 -0800
made search_rs smarter about when to preserve the cache to fix mm prefetch usage
r54389@windhund (orig r3965):  semifor | 2008-01-22 07:13:11 -0800
Added build_datetime_parser method for MSSQL over ODBC.
r54394@windhund (orig r3970):  castaway | 2008-01-24 05:19:52 -0800
Oops, fix joining manual to be correct

r54395@windhund (orig r3971):  castaway | 2008-01-24 15:22:49 -0800
Version 0.08009

r54396@windhund (orig r3972):  castaway | 2008-01-24 15:36:59 -0800
0.08009 released

r54399@windhund (orig r3975):  tomboh | 2008-01-25 09:20:38 -0800
Fix a typo and a couple of links.

r54448@windhund (orig r4024):  oyse | 2008-02-05 00:42:32 -0800
Added Ã˜ystein Torget to the list of contributers
r54474@windhund (orig r4050):  ash | 2008-02-10 09:31:27 -0800
Add txn_scope_guard method/object

67 files changed:
lib/DBIx/Class/CDBICompat.pm
lib/DBIx/Class/CDBICompat/AbstractSearch.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/AccessorMapping.pm
lib/DBIx/Class/CDBICompat/ColumnCase.pm
lib/DBIx/Class/CDBICompat/ColumnGroups.pm
lib/DBIx/Class/CDBICompat/ColumnsAsHash.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/Copy.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/GetSet.pm
lib/DBIx/Class/CDBICompat/HasA.pm [deleted file]
lib/DBIx/Class/CDBICompat/HasMany.pm [deleted file]
lib/DBIx/Class/CDBICompat/ImaDBI.pm
lib/DBIx/Class/CDBICompat/Iterator.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/LazyLoading.pm
lib/DBIx/Class/CDBICompat/LiveObjectIndex.pm
lib/DBIx/Class/CDBICompat/MightHave.pm [deleted file]
lib/DBIx/Class/CDBICompat/Relationship.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/Relationships.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/Retrieve.pm
lib/DBIx/Class/CDBICompat/TempColumns.pm
lib/DBIx/Class/CDBICompat/Triggers.pm
lib/DBIx/Class/DB.pm
lib/DBIx/Class/Relationship/Accessor.pm
lib/DBIx/Class/Relationship/Base.pm
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/ResultSourceHandle.pm
lib/DBIx/Class/ResultSourceProxy/Table.pm
lib/DBIx/Class/Row.pm
lib/DBIx/Class/Serialize/Storable.pm
lib/DBIx/Class/Storage/DBI.pm
lib/SQL/Translator/Parser/DBIx/Class.pm
t/03podcoverage.t
t/18inserterror.t
t/75limit.t
t/84serialize.t
t/cdbi-abstract/search_where.t [new file with mode: 0644]
t/cdbi-t/01-columns.t
t/cdbi-t/02-Film.t
t/cdbi-t/04-lazy.t
t/cdbi-t/06-hasa.t
t/cdbi-t/08-inheritcols.t [new file with mode: 0644]
t/cdbi-t/14-might_have.t
t/cdbi-t/15-accessor.t
t/cdbi-t/19-set_sql.t
t/cdbi-t/21-iterator.t
t/cdbi-t/22-deflate_order.t [new file with mode: 0644]
t/cdbi-t/23-cascade.t [new file with mode: 0644]
t/cdbi-t/24-meta_info.t [new file with mode: 0644]
t/cdbi-t/26-mutator.t [new file with mode: 0644]
t/cdbi-t/columns_as_hashes.t [new file with mode: 0644]
t/cdbi-t/columns_dont_override_custom_accessors.t [new file with mode: 0644]
t/cdbi-t/construct.t [new file with mode: 0644]
t/cdbi-t/copy.t [new file with mode: 0644]
t/cdbi-t/discard_changes_in_DESTROY.t [new file with mode: 0644]
t/cdbi-t/hasa_without_loading.t [new file with mode: 0644]
t/cdbi-t/max_min_value_of.t [new file with mode: 0644]
t/cdbi-t/multi_column_set.t [new file with mode: 0644]
t/cdbi-t/object_cache.t [new file with mode: 0644]
t/cdbi-t/retrieve_from_sql_with_limit.t [new file with mode: 0644]
t/cdbi-t/set_to_undef.t [new file with mode: 0644]
t/cdbi-t/set_vs_DateTime.t [new file with mode: 0644]
t/deleting_many_to_many.t [new file with mode: 0644]
t/relationship_after_update.t [new file with mode: 0644]
t/relationship_doesnt_exist.t [new file with mode: 0644]
t/resultset_overload.t [new file with mode: 0644]
t/testlib/MyBase.pm
t/testlib/MyFoo.pm
t/testlib/PgBase.pm

index 874c4c7..ee797a2 100644 (file)
@@ -15,7 +15,6 @@ __PACKAGE__->load_own_components(qw/
   Constraints
   Triggers
   ReadOnly
-  GetSet
   LiveObjectIndex
   AttributeAPI
   Stringify
@@ -23,16 +22,20 @@ __PACKAGE__->load_own_components(qw/
   Constructor
   AccessorMapping
   ColumnCase
-  HasA
-  HasMany
-  MightHave
+  Relationships
+  Copy
   LazyLoading
   AutoUpdate
   TempColumns
+  GetSet
   Retrieve
   Pager
   ColumnGroups
-  ImaDBI/);
+  ColumnsAsHash
+  AbstractSearch
+  ImaDBI
+  Iterator
+/);
 
             #DBIx::Class::ObjIndexStubs
 1;
@@ -43,17 +46,47 @@ DBIx::Class::CDBICompat - Class::DBI Compatibility layer.
 
 =head1 SYNOPSIS
 
-  use base qw/DBIx::Class/;
-  __PACKAGE__->load_components(qw/CDBICompat Core DB/);
+  package My::CDBI;
+  use base qw/DBIx::Class::CDBICompat/;
+
+  ...continue as Class::DBI...
 
 =head1 DESCRIPTION
 
 DBIx::Class features a fully featured compatibility layer with L<Class::DBI>
-to ease transition for existing CDBI users. In fact, this class is just a
-receipe containing all the features emulated. If you like, you can choose
-which features to emulate by building your own class and loading it like
-this:
+and some common plugins to ease transition for existing CDBI users. 
+
+This is not a wrapper or subclass of DBIx::Class but rather a series of plugins.  The result being that even though you're using the Class::DBI emulation layer you are still getting DBIx::Class objects.  You can use all DBIx::Class features and methods via CDBICompat.  This allows you to take advantage of DBIx::Class features without having to rewrite your CDBI code.
+
+
+=head2 Plugins
+
+CDBICompat is good enough that many CDBI plugins will work with CDBICompat, but many of the plugin features are better done with DBIx::Class methods.
+
+=head3 Class::DBI::AbstractSearch
+
+C<search_where()> is fully emulated using DBIC's search.  Aside from emulation there's no reason to use C<search_where()>.
+
+=head3 Class::DBI::Plugin::NoCache
+
+C<nocache> is fully emulated.
+
+=head3 Class::DBI::Sweet
+
+The features of CDBI::Sweet are better done using DBIC methods which are almost exactly the same.  It even uses L<Data::Page>.
 
+=head3 Class::DBI::Plugin::DeepAbstractSearch
+
+This is better done using DBIC's native search facilities.  The major difference is that DBIC will not infer the join for you, you have to tell it the join tables.
+
+
+=head2 Choosing Features
+
+In fact, this class is just a receipe containing all the features emulated.
+If you like, you can choose which features to emulate by building your 
+own class and loading it like this:
+
+  package My::DB;
   __PACKAGE__->load_own_components(qw/CDBICompat/);
 
 this will automatically load the features included in My::DB::CDBICompat,
@@ -68,58 +101,59 @@ provided it looks something like this:
     CDBICompat::MightHave
   /);
 
-=head1 COMPONENTS
-
-=over 4
-
-=item AccessorMapping
-
-=item AttributeAPI
 
-=item AutoUpdate
+=head1 LIMITATIONS
 
-Allows you to turn on automatic updates for column values.
+=head2 Unimplemented
 
-=item ColumnCase
+The following methods and classes are not emulated, maybe in the future.
 
-=item ColumnGroups
+=over 4
 
-=item Constraints
+=item Class::DBI::Query
 
-=item Constructor
+Deprecated in Class::DBI.
 
-=item DestroyWarning
+=item Class::DBI::Column
 
-=item GetSet
+Not documented in Class::DBI.  CDBICompat's columns() returns a plain string, not an object.
 
-=item HasA
+=item data_type()
 
-=item HasMany
+Undocumented CDBI method.
 
-=item ImaDBI
+=back
 
-=item LazyLoading
+=head2 Limited Support
 
-=item LiveObjectIndex
+The following elements of Class::DBI have limited support.
 
-The live object index tries to ensure there is only one version of a object
-in the perl interpreter.
+=over 4
 
-=item MightHave
+=item Class::DBI::Relationship
 
-=item ObjIndexStubs
+The semi-documented Class::DBI::Relationship objects returned by C<meta_info($type, $col)> are mostly emulated except for their C<args> method.
 
-=item ReadOnly
+=item Relationships
 
-=item Retrieve
+Relationships between tables (has_a, has_many...) must be delcared after all tables in the relationship have been declared.  Thus the usual CDBI idiom of declaring columns and relationships for each class together will not work.  They must instead be done like so:
 
-=item Stringify
+    package Foo;
+    use base qw(Class::DBI);
+    
+    Foo->table("foo");
+    Foo->columns( All => qw(this that bar) );
 
-=item TempColumns
+    package Bar;
+    use base qw(Class::DBI);
+    
+    Bar->table("bar");
+    Bar->columns( All => qw(up down) );
 
-=item Triggers
+    # Now that Foo and Bar are declared it is safe to declare a
+    # relationship between them
+    Foo->has_a( bar => "Bar" );
 
-=item PassThrough
 
 =back
 
diff --git a/lib/DBIx/Class/CDBICompat/AbstractSearch.pm b/lib/DBIx/Class/CDBICompat/AbstractSearch.pm
new file mode 100644 (file)
index 0000000..948dcd9
--- /dev/null
@@ -0,0 +1,37 @@
+package # hide form PAUSE
+    DBIx::Class::CDBICompat::AbstractSearch;
+
+use strict;
+use warnings;
+
+=head1 NAME
+
+DBIx::Class::CDBICompat::AbstractSearch
+
+=head1 SYNOPSIS
+
+See DBIx::Class::CDBICompat for directions for use.
+
+=head1 DESCRIPTION
+
+Emulates L<Class::DBI::AbstractSearch>.
+
+=cut
+
+# The keys are mostly the same.
+my %cdbi2dbix = (
+    limit               => 'rows',
+);
+
+sub search_where {
+    my $class = shift;
+    my $where = (ref $_[0]) ? $_[0] : { @_ };
+    my $attr  = (ref $_[0]) ? $_[1] : {};
+
+    # Translate the keys
+    $attr->{$cdbi2dbix{$_}} = delete $attr->{$_} for keys %cdbi2dbix;
+
+    return $class->resultset_instance->search($where, $attr);
+}
+
+1;
index c012586..1792a13 100644 (file)
@@ -6,18 +6,17 @@ use warnings;
 
 sub mk_group_accessors {
   my ($class, $group, @cols) = @_;
-  unless ($class->can('accessor_name') || $class->can('mutator_name')) {
+  unless ($class->_can_accessor_name_for || $class->_can_mutator_name_for) {
     return $class->next::method($group => @cols);
   }
   foreach my $col (@cols) {
-    my $ro_meth = ($class->can('accessor_name')
-                    ? $class->accessor_name($col)
-                    : $col);
-    my $wo_meth = ($class->can('mutator_name')
-                    ? $class->mutator_name($col)
-                    : $col);
-    #warn "$col $ro_meth $wo_meth";
-    if ($ro_meth eq $wo_meth) {
+    my $ro_meth = $class->_try_accessor_name_for($col);
+    my $wo_meth = $class->_try_mutator_name_for($col);
+
+    # warn "class: $class / col: $col / ro: $ro_meth / wo: $wo_meth\n";
+    if ($ro_meth eq $wo_meth or     # they're the same
+        $wo_meth eq $col)           # or only the accessor is custom
+    {
       $class->next::method($group => [ $ro_meth => $col ]);
     } else {
       $class->mk_group_ro_accessors($group => [ $ro_meth => $col ]);
@@ -26,16 +25,46 @@ sub mk_group_accessors {
   }
 }
 
+# CDBI 3.0.7 decided to change "accessor_name" and "mutator_name" to
+# "accessor_name_for" and "mutator_name_for".  This is recent enough
+# that we should support both.  CDBI does.
+sub _can_accessor_name_for {
+    my $class = shift;
+    return $class->can("accessor_name") || $class->can("accessor_name_for");
+}
+
+sub _can_mutator_name_for {
+    my $class = shift;
+    return $class->can("mutator_name") || $class->can("mutator_name_for");
+}
+
+sub _try_accessor_name_for {
+    my($class, $column) = @_;
+
+    my $method = $class->_can_accessor_name_for;
+    return $column unless $method;
+    return $class->$method($column);
+}
+
+sub _try_mutator_name_for {
+    my($class, $column) = @_;
+
+    my $method = $class->_can_mutator_name_for;
+    return $column unless $method;
+    return $class->$method($column);
+}
+
+
 sub new {
   my ($class, $attrs, @rest) = @_;
   $class->throw_exception( "create needs a hashref" ) unless ref $attrs eq 'HASH';
   foreach my $col ($class->columns) {
-    if ($class->can('accessor_name')) {
-      my $acc = $class->accessor_name($col);
+    if ($class->_can_accessor_name_for) {
+      my $acc = $class->_try_accessor_name_for($col);
       $attrs->{$col} = delete $attrs->{$acc} if exists $attrs->{$acc};
     }
-    if ($class->can('mutator_name')) {
-      my $mut = $class->mutator_name($col);
+    if ($class->_can_mutator_name_for) {
+      my $mut = $class->_try_mutator_name_for($col);
       $attrs->{$col} = delete $attrs->{$mut} if exists $attrs->{$mut};
     }
   }
index 9be24ff..0f847db 100644 (file)
@@ -79,18 +79,70 @@ sub _build_query {
   return \%new_query;
 }
 
+
+# CDBI will never overwrite an accessor, but it only uses one
+# accessor for all column types.  DBIC uses many different
+# accessor types so, for example, if you declare a column()
+# and then a has_a() for that same column it must overwrite.
+#
+# To make this work CDBICompat has decide if an accessor
+# method was put there by itself and only then overwrite.
+{
+  my %our_accessors;
+
+  sub _has_custom_accessor {
+    my($class, $name) = @_;
+    
+    no strict 'refs';
+    my $existing_accessor = *{$class .'::'. $name}{CODE};
+    return $existing_accessor && !$our_accessors{$existing_accessor};
+  }
+
+  sub _deploy_accessor {
+    my($class, $name, $accessor) = @_;
+
+    return if $class->_has_custom_accessor($name);
+
+    for my $name ($name, lc $name) {
+      no strict 'refs';
+      no warnings 'redefine';
+      *{$class .'::'. $name} = $accessor;
+    }
+    
+    $our_accessors{$accessor}++;
+
+    return 1;
+  }
+}
+
 sub _mk_group_accessors {
   my ($class, $type, $group, @fields) = @_;
-  #warn join(', ', map { ref $_ ? (@$_) : ($_) } @fields);
-  my @extra;
-  foreach (@fields) {
-    my ($acc, $field) = ref $_ ? @$_ : ($_, $_);
-    #warn "$acc ".lc($acc)." $field";
-    next if defined &{"${class}::${acc}"};
-    push(@extra, [ lc $acc => $field ]);
+
+  # So we don't have to do lots of lookups inside the loop.
+  my $maker = $class->can($type) unless ref $type;
+
+  # warn "$class $type $group\n";
+  foreach my $field (@fields) {
+    if( $field eq 'DESTROY' ) {
+        carp("Having a data accessor named DESTROY in ".
+             "'$class' is unwise.");
+    }
+
+    my $name = $field;
+
+    ($name, $field) = @$field if ref $field;
+
+    my $accessor = $class->$maker($group, $field);
+    my $alias = "_${name}_accessor";
+
+    # warn "  $field $alias\n";
+    {
+      no strict 'refs';
+      
+      $class->_deploy_accessor($name,  $accessor);
+      $class->_deploy_accessor($alias, $accessor);
+    }
   }
-  return $class->next::method($type, $group,
-                                                     @fields, @extra);
 }
 
 sub new {
index 98e6508..829f589 100644 (file)
@@ -12,6 +12,8 @@ sub columns {
   my $proto = shift;
   my $class = ref $proto || $proto;
   my $group = shift || "All";
+  $class->_init_result_source_instance();
+
   $class->_add_column_group($group => @_) if @_;
   return $class->all_columns    if $group eq "All";
   return $class->primary_column if $group eq "Primary";
diff --git a/lib/DBIx/Class/CDBICompat/ColumnsAsHash.pm b/lib/DBIx/Class/CDBICompat/ColumnsAsHash.pm
new file mode 100644 (file)
index 0000000..b5f1168
--- /dev/null
@@ -0,0 +1,105 @@
+package
+    DBIx::Class::CDBICompat::ColumnsAsHash;
+
+use strict;
+use warnings;
+
+
+=head1 NAME
+
+DBIx::Class::CDBICompat::ColumnsAsHash
+
+=head1 SYNOPSIS
+
+See DBIx::Class::CDBICompat for directions for use.
+
+=head1 DESCRIPTION
+
+Emulates the I<undocumnted> behavior of Class::DBI where the object can be accessed as a hash of columns.  This is often used as a performance hack.
+
+    my $column = $row->{column};
+
+=head2 Differences from Class::DBI
+
+If C<DBIC_CDBICOMPAT_HASH_WARN> is true it will warn when a column is accessed as a hash key.
+
+=cut
+
+sub new {
+    my $class = shift;
+
+    my $new = $class->next::method(@_);
+
+    $new->_make_columns_as_hash;
+
+    return $new;
+}
+
+sub inflate_result {
+    my $class = shift;
+
+    my $new = $class->next::method(@_);
+    
+    $new->_make_columns_as_hash;
+    
+    return $new;
+}
+
+
+sub _make_columns_as_hash {
+    my $self = shift;
+    
+    for my $col ($self->columns) {
+        if( exists $self->{$col} ) {
+            warn "Skipping mapping $col to a hash key because it exists";
+        }
+
+        tie $self->{$col}, 'DBIx::Class::CDBICompat::Tied::ColumnValue',
+            $self, $col;
+    }
+}
+
+
+package DBIx::Class::CDBICompat::Tied::ColumnValue;
+
+use Carp;
+use Scalar::Util qw(weaken isweak);
+
+
+sub TIESCALAR {
+    my($class, $obj, $col) = @_;
+    my $self = [$obj, $col];
+    weaken $self->[0];
+
+    return bless $self, $_[0];
+}
+
+sub FETCH {
+    my $self = shift;
+    my($obj, $col) = @$self;
+
+    my $class = ref $obj;
+    my $id    = $obj->id;
+    carp "Column '$col' of '$class/$id' was fetched as a hash"
+        if $ENV{DBIC_CDBICOMPAT_HASH_WARN};
+
+    return $obj->column_info($col)->{_inflate_info}
+                ? $obj->get_inflated_column($col)
+                : $obj->get_column($col);
+}
+
+sub STORE {
+    my $self = shift;
+    my($obj, $col) = @$self;
+
+    my $class = ref $obj;
+    my $id    = $obj->id;
+    carp "Column '$col' of '$class/$id' was stored as a hash"
+        if $ENV{DBIC_CDBICOMPAT_HASH_WARN};
+
+    return $obj->column_info($col)->{_inflate_info}
+                ? $obj->set_inflated_column($col => shift)
+                : $obj->set_column($col => shift);
+}
+
+1;
diff --git a/lib/DBIx/Class/CDBICompat/Copy.pm b/lib/DBIx/Class/CDBICompat/Copy.pm
new file mode 100644 (file)
index 0000000..414cbd6
--- /dev/null
@@ -0,0 +1,36 @@
+package # hide from PAUSE
+    DBIx::Class::CDBICompat::Copy;
+
+use strict;
+use warnings;
+
+use Carp;
+
+=head1 NAME
+
+DBIx::Class::CDBICompat::Copy
+
+=head1 SYNOPSIS
+
+See DBIx::Class::CDBICompat for directions for use.
+
+=head1 DESCRIPTION
+
+Emulates C<<Class::DBI->copy($new_id)>>.
+
+=cut
+
+
+# CDBI's copy will take an id in addition to a hash ref.
+sub copy {
+    my($self, $arg) = @_;
+    return $self->next::method($arg) if ref $arg;
+    
+    my @primary_columns = $self->primary_columns;
+    croak("Need hash-ref to edit copied column values")
+        if @primary_columns > 1;
+
+    return $self->next::method({ $primary_columns[0] => $arg });
+}
+
+1;
index 6b98e79..dd621f2 100644 (file)
@@ -16,7 +16,17 @@ sub get {
 }
 
 sub set {
-  return shift->set_column(@_);
+  my($self, %data) = @_;
+
+  # set_columns() is going to do a string comparison before setting.
+  # This breaks on DateTime objects (whose comparison is arguably broken)
+  # so we stringify anything first.
+  for my $key (keys %data) {
+    next unless ref $data{$key};
+    $data{$key} = "$data{$key}";
+  }
+
+  return shift->set_columns(\%data);
 }
 
 1;
diff --git a/lib/DBIx/Class/CDBICompat/HasA.pm b/lib/DBIx/Class/CDBICompat/HasA.pm
deleted file mode 100644 (file)
index 647674f..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-package # hide from PAUSE
-    DBIx::Class::CDBICompat::HasA;
-
-use strict;
-use warnings;
-
-sub has_a {
-  my ($self, $col, $f_class, %args) = @_;
-  $self->throw_exception( "No such column ${col}" ) unless $self->has_column($col);
-  $self->ensure_class_loaded($f_class);
-  if ($args{'inflate'} || $args{'deflate'}) { # Non-database has_a
-    if (!ref $args{'inflate'}) {
-      my $meth = $args{'inflate'};
-      $args{'inflate'} = sub { $f_class->$meth(shift); };
-    }
-    if (!ref $args{'deflate'}) {
-      my $meth = $args{'deflate'};
-      $args{'deflate'} = sub { shift->$meth; };
-    }
-    $self->inflate_column($col, \%args);
-    return 1;
-  }
-
-  $self->belongs_to($col, $f_class);
-  return 1;
-}
-
-sub search {
-  my $self = shift;
-  my $attrs = {};
-  if (@_ > 1 && ref $_[$#_] eq 'HASH') {
-    $attrs = { %{ pop(@_) } };
-  }
-  my $where = (@_ ? ((@_ == 1) ? ((ref $_[0] eq "HASH") ? { %{+shift} } : shift)
-                               : {@_})
-                  : undef());
-  if (ref $where eq 'HASH') {
-    foreach my $key (keys %$where) { # has_a deflation hack
-      $where->{$key} = ''.$where->{$key}
-        if eval { $where->{$key}->isa('DBIx::Class') };
-    }
-  }
-  $self->next::method($where, $attrs);
-}
-
-1;
diff --git a/lib/DBIx/Class/CDBICompat/HasMany.pm b/lib/DBIx/Class/CDBICompat/HasMany.pm
deleted file mode 100644 (file)
index 6438e43..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-package # hide from PAUSE
-    DBIx::Class::CDBICompat::HasMany;
-
-use strict;
-use warnings;
-
-sub has_many {
-  my ($class, $rel, $f_class, $f_key, $args) = @_;
-
-  my @f_method;
-
-  if (ref $f_class eq 'ARRAY') {
-    ($f_class, @f_method) = @$f_class;
-  }
-
-  if (ref $f_key eq 'HASH' && !$args) { $args = $f_key; undef $f_key; };
-
-  $args ||= {};
-  if (delete $args->{no_cascade_delete}) {
-    $args->{cascade_delete} = 0;
-  }
-
-  if( !$f_key and !@f_method ) {
-      my $f_source = $f_class->result_source_instance;
-      ($f_key) = grep { $f_source->relationship_info($_)->{class} eq $class }
-                      $f_source->relationships;
-  }
-
-  $class->next::method($rel, $f_class, $f_key, $args);
-
-  if (@f_method) {
-    no strict 'refs';
-    no warnings 'redefine';
-    my $post_proc = sub { my $o = shift; $o = $o->$_ for @f_method; $o; };
-    *{"${class}::${rel}"} =
-      sub {
-        my $rs = shift->search_related($rel => @_);
-        $rs->{attrs}{record_filter} = $post_proc;
-        return (wantarray ? $rs->all : $rs);
-      };
-    return 1;
-  }
-
-}
-
-1;
index 880eb9d..0a143fa 100644 (file)
@@ -8,7 +8,7 @@ use DBIx::ContextualFetch;
 use base qw/DBIx::Class/;
 
 __PACKAGE__->mk_classdata('_transform_sql_handler_order'
-                            => [ qw/TABLE ESSENTIAL JOIN/ ] );
+                            => [ qw/TABLE ESSENTIAL JOIN IDENTIFIER/ ] );
 
 __PACKAGE__->mk_classdata('_transform_sql_handlers' =>
   {
@@ -24,8 +24,14 @@ __PACKAGE__->mk_classdata('_transform_sql_handlers' =>
     'ESSENTIAL' =>
       sub {
         my ($self, $class, $data) = @_;
-        return join(' ', $class->columns('Essential')) unless $data;
-        return join(' ', $self->{_classes}{$data}->columns('Essential'));
+        $class = $data ? $self->{_classes}{$data} : $class;
+        return join(', ', $class->columns('Essential'));
+      },
+    'IDENTIFIER' =>
+      sub {
+        my ($self, $class, $data) = @_;
+        $class = $data ? $self->{_classes}{$data} : $class;
+        return join ' AND ', map  "$_ = ?", $class->primary_columns;
       },
     'JOIN' =>
       sub {
diff --git a/lib/DBIx/Class/CDBICompat/Iterator.pm b/lib/DBIx/Class/CDBICompat/Iterator.pm
new file mode 100644 (file)
index 0000000..1b96835
--- /dev/null
@@ -0,0 +1,47 @@
+package DBIx::Class::CDBICompat::Iterator;
+
+use strict;
+use warnings;
+
+=head1 NAME
+
+DBIx::Class::CDBICompat::Iterator
+
+=head1 SYNOPSIS
+
+See DBIx::Class::CDBICompat for directions for use.
+
+=head1 DESCRIPTION
+
+Emulates the extra behaviors of the Class::DBI search iterator.
+
+=head2 Differences from DBIx::Class result set
+
+The CDBI iterator returns true if there were any results, false otherwise.  The DBIC result set always returns true.
+
+=cut
+
+
+sub _init_result_source_instance {
+  my $class = shift;
+  
+  my $table = $class->next::method(@_);
+  $table->resultset_class("DBIx::Class::CDBICompat::Iterator::ResultSet");
+
+  return $table;
+}
+
+
+
+package DBIx::Class::CDBICompat::Iterator::ResultSet;
+
+use strict;
+use warnings;
+
+use base qw(DBIx::Class::ResultSet);
+
+sub _bool {
+  return $_[0]->count;
+}
+
+1;
index b7d3633..e8ffbcc 100644 (file)
@@ -11,6 +11,47 @@ sub resultset_instance {
   return $rs;
 }
 
+
+# Emulate that CDBI throws out all changed columns and reloads them on 
+# request in case the database modifies the new value (say, via a trigger)
+sub update {
+    my $self = shift;
+    
+    my @dirty_columns = keys %{$self->{_dirty_columns}};
+    
+    my $ret = $self->next::method(@_);
+    $self->_clear_column_data(@dirty_columns);
+    
+    return $ret;
+}
+
+
+# And again for create
+sub create {
+    my $class = shift;
+    my($data) = @_;
+    
+    my @columns = keys %$data;
+    
+    my $obj = $class->next::method(@_);
+    return $obj unless defined $obj;
+    
+    my %primary_cols = map { $_ => 1 } $class->primary_columns;
+    my @data_cols = grep !$primary_cols{$_}, @columns;
+    $obj->_clear_column_data(@data_cols);
+
+    return $obj;
+}
+
+
+sub _clear_column_data {
+    my $self = shift;
+    
+    delete $self->{_column_data}{$_}     for @_;
+    delete $self->{_inflated_column}{$_} for @_;
+}
+
+
 sub get_column {
   my ($self, $col) = @_;
   if ((ref $self) && (!exists $self->{'_column_data'}{$col})
@@ -22,6 +63,28 @@ sub get_column {
   $self->next::method(@_[1..$#_]);
 }
 
+# CDBI does not explicitly declare auto increment columns, so
+# we just clear out our primary columns before copying.
+sub copy {
+  my($self, $changes) = @_;
+
+  for my $col ($self->primary_columns) {
+    $changes->{$col} = undef unless exists $changes->{$col};
+  }
+  
+  return $self->next::method($changes);
+}
+
+sub discard_changes {
+  my($self) = shift;
+
+  delete $self->{_column_data}{$_} for $self->is_changed;
+  delete $self->{_dirty_columns};
+  delete $self->{_relationship_data};
+
+  return $self;
+}
+
 sub _ident_cond {
   my ($class) = @_;
   return join(" AND ", map { "$_ = ?" } $class->primary_columns);
index fb8a77e..445282c 100644 (file)
@@ -12,6 +12,21 @@ __PACKAGE__->mk_classdata('purge_object_index_every' => 1000);
 __PACKAGE__->mk_classdata('live_object_index' => { });
 __PACKAGE__->mk_classdata('live_object_init_count' => { });
 
+# Caching is on by default, but a classic CDBI hack to turn it off is to
+# set this variable false.
+$Class::DBI::Weaken_Is_Available = 1
+    unless defined $Class::DBI::Weaken_Is_Available;
+__PACKAGE__->mk_classdata('__nocache' => 0);
+
+sub nocache {
+    my $class = shift;
+    
+    return $class->__nocache(@_) if @_;
+    
+    return 1 if $Class::DBI::Weaken_Is_Available == 0;
+    return $class->__nocache;
+}
+
 # Ripped from Class::DBI 0.999, all credit due to Tony Bowden for this code,
 # all blame due to me for whatever bugs I introduced porting it.
 
@@ -30,11 +45,15 @@ sub clear_object_index {
   delete @$live{ keys %$live };
 }
 
+
 # And now the fragments to tie it in to DBIx::Class::Table
 
 sub insert {
   my ($self, @rest) = @_;
   $self->next::method(@rest);
+  
+  return $self if $self->nocache;
+
     # Because the insert will die() if it can't insert into the db (or should)
     # we can be sure the object *was* inserted if we got this far. In which
     # case, given primary keys are unique and ID only returns a
@@ -55,6 +74,9 @@ sub insert {
 sub inflate_result {
   my ($class, @rest) = @_;
   my $new = $class->next::method(@rest);
+  
+  return $new if $new->nocache;
+  
   if (my $key = $new->ID) {
     #warn "Key $key";
     my $live = $class->live_object_index;
@@ -67,16 +89,4 @@ sub inflate_result {
   return $new;
 }
 
-sub discard_changes {
-  my ($self) = @_;
-  if (my $key = $self->ID) {
-    $self->remove_from_object_index;
-    my $ret = $self->next::method;
-    $self->live_object_index->{$key} = $self if $self->in_storage;
-    return $ret;
-  } else {
-    return $self->next::method;
-  }
-}
-
 1;
diff --git a/lib/DBIx/Class/CDBICompat/MightHave.pm b/lib/DBIx/Class/CDBICompat/MightHave.pm
deleted file mode 100644 (file)
index 519c6fe..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package # hide from PAUSE
-    DBIx::Class::CDBICompat::MightHave;
-
-use strict;
-use warnings;
-
-sub might_have {
-  my ($class, $rel, $f_class, @columns) = @_;
-  if (ref $columns[0] || !defined $columns[0]) {
-    return $class->next::method($rel, $f_class, @columns);
-  } else {
-    return $class->next::method($rel, $f_class, undef,
-                                     { proxy => \@columns });
-  }
-}
-
-1;
diff --git a/lib/DBIx/Class/CDBICompat/Relationship.pm b/lib/DBIx/Class/CDBICompat/Relationship.pm
new file mode 100644 (file)
index 0000000..a702bd2
--- /dev/null
@@ -0,0 +1,49 @@
+package
+    DBIx::Class::CDBICompat::Relationship;
+
+use strict;
+use warnings;
+
+
+=head1 NAME
+
+DBIx::Class::CDBICompat::Relationship
+
+=head1 DESCRIPTION
+
+Emulate the Class::DBI::Relationship object returned from C<meta_info()>.
+
+The C<args()> method does not return any useful result as it's not clear what it should contain nor if any of the information is applicable to DBIx::Class.
+
+=cut
+
+my %method2key = (
+    name            => 'type',
+    class           => 'self_class',
+    accessor        => 'accessor',
+    foreign_class   => 'class',
+);
+
+sub new {
+    my($class, $args) = @_;
+    
+    return bless $args, $class;
+}
+
+for my $method (keys %method2key) {
+    my $key = $method2key{$method};
+    my $code = sub {
+        $_[0]->{$key};
+    };
+    
+    no strict 'refs';
+    *{$method} = $code;
+}
+
+sub args {
+    warn "args() is unlikely to ever work";
+    return undef;
+}
+
+
+1;
diff --git a/lib/DBIx/Class/CDBICompat/Relationships.pm b/lib/DBIx/Class/CDBICompat/Relationships.pm
new file mode 100644 (file)
index 0000000..6893bc9
--- /dev/null
@@ -0,0 +1,175 @@
+package # hide from PAUSE
+    DBIx::Class::CDBICompat::Relationships;
+
+use strict;
+use warnings;
+
+use base qw/Class::Data::Inheritable/;
+
+use Clone;
+use DBIx::Class::CDBICompat::Relationship;
+
+__PACKAGE__->mk_classdata('__meta_info' => {});
+
+
+=head1 NAME
+
+DBIx::Class::CDBICompat::Relationships
+
+=head1 DESCRIPTION
+
+Emulate C<has_a>, C<has_many>, C<might_have> and C<meta_info>.
+
+=cut
+
+sub has_a {
+  my ($self, $col, $f_class, %args) = @_;
+  $self->throw_exception( "No such column ${col}" ) unless $self->has_column($col);
+  $self->ensure_class_loaded($f_class);
+  
+  my $rel;
+
+  if ($args{'inflate'} || $args{'deflate'}) { # Non-database has_a
+    if (!ref $args{'inflate'}) {
+      my $meth = $args{'inflate'};
+      $args{'inflate'} = sub { $f_class->$meth(shift); };
+    }
+    if (!ref $args{'deflate'}) {
+      my $meth = $args{'deflate'};
+      $args{'deflate'} = sub { shift->$meth; };
+    }
+    $self->inflate_column($col, \%args);
+    
+    $rel = {
+        class => $f_class
+    };
+  }
+  else {
+    $self->belongs_to($col, $f_class);
+    $rel = $self->result_source_instance->relationship_info($col);
+  }
+  
+  $self->_extend_meta(
+    has_a => $col,
+    $rel
+  );
+  
+  return 1;
+}
+
+
+sub has_many {
+  my ($class, $rel, $f_class, $f_key, $args) = @_;
+
+  my @f_method;
+
+  if (ref $f_class eq 'ARRAY') {
+    ($f_class, @f_method) = @$f_class;
+  }
+
+  if (ref $f_key eq 'HASH' && !$args) { $args = $f_key; undef $f_key; };
+
+  $args ||= {};
+  my $cascade = delete $args->{cascade} || '';
+  if (delete $args->{no_cascade_delete} || $cascade eq 'None') {
+    $args->{cascade_delete} = 0;
+  }
+  elsif( $cascade eq 'Delete' ) {
+    $args->{cascade_delete} = 1;
+  }
+  elsif( length $cascade ) {
+    warn "Unemulated cascade option '$cascade' in $class->has_many($rel => $f_class)";
+  }
+
+  if( !$f_key and !@f_method ) {
+      my $f_source = $f_class->result_source_instance;
+      ($f_key) = grep { $f_source->relationship_info($_)->{class} eq $class }
+                      $f_source->relationships;
+  }
+
+  $class->next::method($rel, $f_class, $f_key, $args);
+
+  $class->_extend_meta(
+    has_many => $rel,
+    $class->result_source_instance->relationship_info($rel)
+  );
+
+  if (@f_method) {
+    no strict 'refs';
+    no warnings 'redefine';
+    my $post_proc = sub { my $o = shift; $o = $o->$_ for @f_method; $o; };
+    *{"${class}::${rel}"} =
+      sub {
+        my $rs = shift->search_related($rel => @_);
+        $rs->{attrs}{record_filter} = $post_proc;
+        return (wantarray ? $rs->all : $rs);
+      };
+    return 1;
+  }
+
+}
+
+
+sub might_have {
+  my ($class, $rel, $f_class, @columns) = @_;
+  
+  my $ret;
+  if (ref $columns[0] || !defined $columns[0]) {
+    $ret = $class->next::method($rel, $f_class, @columns);
+  } else {
+    $ret = $class->next::method($rel, $f_class, undef,
+                                { proxy => \@columns });
+  }
+  
+  $class->_extend_meta(
+    might_have => $rel,
+    $class->result_source_instance->relationship_info($rel)
+  );
+  
+  return $ret;
+}
+
+
+sub _extend_meta {
+    my ($class, $type, $rel, $val) = @_;
+    my %hash = %{ Clone::clone($class->__meta_info || {}) };
+
+    $val->{self_class} = $class;
+    $val->{type}       = $type;
+    $val->{accessor}   = $rel;
+
+    $hash{$type}{$rel} = DBIx::Class::CDBICompat::Relationship->new($val);
+    $class->__meta_info(\%hash);
+}
+
+
+sub meta_info {
+    my ($class, $type, $rel) = @_;
+    my $meta = $class->__meta_info;
+    return $meta unless $type;
+
+    my $type_meta = $meta->{$type};
+    return $type_meta unless $rel;
+    return $type_meta->{$rel};
+}
+
+
+sub search {
+  my $self = shift;
+  my $attrs = {};
+  if (@_ > 1 && ref $_[$#_] eq 'HASH') {
+    $attrs = { %{ pop(@_) } };
+  }
+  my $where = (@_ ? ((@_ == 1) ? ((ref $_[0] eq "HASH") ? { %{+shift} } : shift)
+                               : {@_})
+                  : undef());
+  if (ref $where eq 'HASH') {
+    foreach my $key (keys %$where) { # has_a deflation hack
+      $where->{$key} = ''.$where->{$key}
+        if eval { $where->{$key}->isa('DBIx::Class') };
+    }
+  }
+  $self->next::method($where, $attrs);
+}
+
+1;
index 1186ae4..4c36887 100644 (file)
@@ -47,12 +47,35 @@ sub _build_query {
 
 sub retrieve_from_sql {
   my ($class, $cond, @rest) = @_;
+
   $cond =~ s/^\s*WHERE//i;
-  $class->search_literal($cond, @rest);
+
+  if( $cond =~ s/\bLIMIT (\d+)\s*$//i ) {
+      push @rest, { rows => $1 };
+  }
+
+  return $class->search_literal($cond, @rest);
+}
+
+sub construct {
+    my $class = shift;
+    my $obj = $class->resultset_instance->new_result(@_);
+    $obj->in_storage(1);
+    
+    return $obj;
 }
 
 sub retrieve_all      { shift->search              }
 sub count_all         { shift->count               }
-  # Contributed by Numa. No test for this though.
+
+sub maximum_value_of  {
+    my($class, $col) = @_;
+    return $class->resultset_instance->get_column($col)->max;
+}
+
+sub minimum_value_of  {
+    my($class, $col) = @_;
+    return $class->resultset_instance->get_column($col)->min;
+}
 
 1;
index 95be2a8..d025bb8 100644 (file)
@@ -22,17 +22,26 @@ sub _add_column_group {
 
 sub new {
   my ($class, $attrs, @rest) = @_;
-  my %temp;
-  foreach my $key (keys %$attrs) {
-    $temp{$key} = delete $attrs->{$key} if $class->_temp_columns->{$key};
-  }
+
+  my $temp = $class->_extract_temp_data($attrs);
+
   my $new = $class->next::method($attrs, @rest);
-  foreach my $key (keys %temp) {
-    $new->set_temp($key, $temp{$key});
-  }
+
+  $new->set_temp($_, $temp->{$_}) for keys %$temp;
+
   return $new;
 }
 
+sub _extract_temp_data {
+  my($self, $data) = @_;
+
+  my %temp;
+  foreach my $key (keys %$data) {
+    $temp{$key} = delete $data->{$key} if $self->_temp_columns->{$key};
+  }
+
+  return \%temp;
+}
 
 sub find_column {
   my ($class, $col, @rest) = @_;
@@ -40,6 +49,16 @@ sub find_column {
   return $class->next::method($col, @rest);
 }
 
+sub set {
+  my($self, %data) = @_;
+  
+  my $temp_data = $self->_extract_temp_data(\%data);
+  
+  $self->set_temp($_, $temp_data->{$_}) for keys %$temp_data;
+  
+  return $self->next::method(%data);
+}
+
 sub get_temp {
   my ($self, $column) = @_;
   $self->throw_exception( "Can't fetch data as class method" ) unless ref $self;
index 2c4ff30..3f6aef7 100644 (file)
@@ -7,6 +7,9 @@ use Class::Trigger;
 
 sub insert {
   my $self = shift;
+
+  return $self->create(@_) unless ref $self;
+
   $self->call_trigger('before_create');
   $self->next::method(@_);
   $self->call_trigger('after_create');
index e502278..93d7ea6 100644 (file)
@@ -161,7 +161,7 @@ sub result_source_instance {
   return $class->_result_source_instance(@_) if @_;
 
   my $source = $class->_result_source_instance;
-  return {} unless Scalar::Util::blessed($source);
+  return unless Scalar::Util::blessed($source);
 
   if ($source->result_class ne $class) {
     # Remove old source instance so we dont get deep recursion
@@ -172,7 +172,6 @@ sub result_source_instance {
     #$class->table($class);
     #$source = $class->_result_source_instance;
 
-    $DB::single = 1;
     $source = $source->new({ 
         %$source, 
         source_name  => $class,
index b20eb16..76183de 100644 (file)
@@ -36,7 +36,7 @@ sub add_relationship_accessor {
     $class->inflate_column($rel,
       { inflate => sub {
           my ($val, $self) = @_;
-          return $self->find_or_create_related($rel, {}, {});
+          return $self->find_or_new_related($rel, {}, {});
         },
         deflate => sub {
           my ($val, $self) = @_;
index 1fbcf52..09dfbbf 100644 (file)
@@ -288,7 +288,8 @@ L<DBIx::Class::Row/insert> on it.
 
 sub find_or_new_related {
   my $self = shift;
-  return $self->find_related(@_) || $self->new_related(@_);
+  my $obj = $self->find_related(@_);
+  return defined $obj ? $obj : $self->new_related(@_);
 }
 
 =head2 find_or_create_related
index c2b045c..775d032 100644 (file)
@@ -3,8 +3,8 @@ package DBIx::Class::ResultSet;
 use strict;
 use warnings;
 use overload
-        '0+'     => \&count,
-        'bool'   => sub { 1; },
+        '0+'     => "count",
+        'bool'   => "_bool",
         fallback => 1;
 use Carp::Clan qw/^DBIx::Class/;
 use Data::Page;
@@ -51,6 +51,10 @@ In the examples below, the following table classes are used:
   __PACKAGE__->belongs_to(artist => 'MyApp::Schema::Artist');
   1;
 
+=head1 OVERLOADING
+
+If a resultset is used as a number it returns the C<count()>.  However, if it is used as a boolean it is always true.  So if you want to check if a result set has any results use C<if $rs != 0>.  C<if $rs> will always be true.
+
 =head1 METHODS
 
 =head2 new
@@ -996,6 +1000,10 @@ sub _count { # Separated out so pager can get the full count
   return $count;
 }
 
+sub _bool {
+  return 1;
+}
+
 =head2 count_literal
 
 =over 4
index c1a5070..9354318 100644 (file)
@@ -3,6 +3,7 @@ package DBIx::Class::ResultSourceHandle;
 use strict;
 use warnings;
 use Storable;
+use Carp;
 
 use base qw/DBIx::Class/;
 
@@ -77,7 +78,8 @@ sub STORABLE_freeze {
 
     my $to_serialize = { %$self };
     
-    delete $to_serialize->{schema};
+    my $class = $self->schema->class($self->source_moniker);
+    $to_serialize->{schema} = $class;
     return (Storable::freeze($to_serialize));
 }
 
@@ -93,7 +95,17 @@ C<$schema->thaw($ice)> which handles this for you.
 sub STORABLE_thaw {
     my ($self, $cloning,$ice) = @_;
     %$self = %{ Storable::thaw($ice) };
-    $self->{schema} = $thaw_schema;
+
+    my $class = delete $self->{schema};
+    if( $thaw_schema ) {
+        $self->{schema} = $thaw_schema;
+    }
+    else {
+        my $rs = $class->result_source_instance;
+        $self->{schema} = $rs->schema if $rs;
+    }
+
+    carp "Unable to restore schema" unless $self->{schema};
 }
 
 =head1 AUTHOR
index 1b43c08..61b53fa 100644 (file)
@@ -12,6 +12,42 @@ __PACKAGE__->mk_classdata(table_class => 'DBIx::Class::ResultSource::Table');
 __PACKAGE__->mk_classdata('table_alias'); # FIXME: Doesn't actually do
                                           # anything yet!
 
+sub _init_result_source_instance {
+    my $class = shift;
+
+    $class->mk_classdata('result_source_instance')
+        unless $class->can('result_source_instance');
+
+    my $table = $class->result_source_instance;
+    my $class_has_table_instance = ($table and $table->result_class eq $class);
+    return $table if $class_has_table_instance;
+
+    if( $table ) {
+        $table = $class->table_class->new({
+            %$table,
+            result_class => $class,
+            source_name => undef,
+            schema => undef
+        });
+    }
+    else {
+        $table = $class->table_class->new({
+            name            => undef,
+            result_class    => $class,
+            source_name     => undef,
+        });
+    }
+
+    $class->result_source_instance($table);
+
+    if ($class->can('schema_instance')) {
+        $class =~ m/([^:]+)$/;
+        $class->schema_instance->register_class($class, $class);
+    }
+
+    return $table;
+}
+
 =head1 NAME
 
 DBIx::Class::ResultSourceProxy::Table - provides a classdata table
@@ -47,7 +83,7 @@ sub table {
   unless (ref $table) {
     $table = $class->table_class->new({
         $class->can('result_source_instance') ?
-          %{$class->result_source_instance} : (),
+          %{$class->result_source_instance||{}} : (),
         name => $table,
         result_class => $class,
         source_name => undef,
index 377e5e8..279e508 100644 (file)
@@ -50,7 +50,9 @@ sub new {
   my ($class, $attrs) = @_;
   $class = ref $class if ref $class;
 
-  my $new = { _column_data => {} };
+  my $new = {
+      _column_data          => {},
+  };
   bless $new, $class;
 
   if (my $handle = delete $attrs->{-source_handle}) {
@@ -455,7 +457,11 @@ sub set_column {
   my $old = $self->get_column($column);
   my $ret = $self->store_column(@_);
   $self->{_dirty_columns}{$column} = 1
-    if (defined $old ^ defined $ret) || (defined $old && $old ne $ret);
+    if (defined $old xor defined $ret) || (defined $old && $old ne $ret);
+
+  # XXX clear out the relation cache for this column
+  delete $self->{related_resultsets}{$column};
+
   return $ret;
 }
 
index 7ccd2b0..d904c0b 100644 (file)
@@ -4,14 +4,19 @@ use warnings;
 use Storable;
 
 sub STORABLE_freeze {
-    my ($self,$cloning) = @_;
+    my ($self, $cloning) = @_;
     my $to_serialize = { %$self };
+
     delete $to_serialize->{result_source};
+    delete $to_serialize->{related_resultsets};
+    delete $to_serialize->{_inflated_column};
+
     return (Storable::freeze($to_serialize));
 }
 
 sub STORABLE_thaw {
-    my ($self,$cloning,$serialized) = @_;
+    my ($self, $cloning, $serialized) = @_;
+
     %$self = %{ Storable::thaw($serialized) };
     $self->result_source($self->result_source_instance)
       if $self->can('result_source_instance');
index 874d3a4..74fadd9 100644 (file)
@@ -1122,6 +1122,9 @@ sub _select {
   } else {
     $self->throw_exception("rows attribute must be positive if present")
       if (defined($attrs->{rows}) && !($attrs->{rows} > 0));
+
+    # MySQL actually recommends this approach.  I cringe.
+    $attrs->{rows} = 2**48 if not defined $attrs->{rows} and defined $attrs->{offset};
     push @args, $attrs->{rows}, $attrs->{offset};
   }
 
index ac91446..6311f33 100644 (file)
@@ -45,8 +45,6 @@ sub parse {
 
 #    print Dumper($dbixschema->registered_classes);
 
-    #foreach my $tableclass ($dbixschema->registered_classes)
-
     my %seen_tables;
 
     my @monikers = $dbixschema->sources;
index 8f2c0d6..35add86 100644 (file)
@@ -32,12 +32,21 @@ my $exceptions = {
         ]
     },
     'DBIx::Class::CDBICompat::AccessorMapping'          => { skip => 1 },
+    'DBIx::Class::CDBICompat::AbstractSearch' => {
+        ignore => [qw(search_where)]
+    },
     'DBIx::Class::CDBICompat::AttributeAPI'             => { skip => 1 },
     'DBIx::Class::CDBICompat::AutoUpdate'               => { skip => 1 },
+    'DBIx::Class::CDBICompat::ColumnsAsHash' => {
+        ignore => [qw(inflate_result new update)]
+    },
     'DBIx::Class::CDBICompat::ColumnCase'               => { skip => 1 },
     'DBIx::Class::CDBICompat::ColumnGroups'             => { skip => 1 },
     'DBIx::Class::CDBICompat::Constraints'              => { skip => 1 },
     'DBIx::Class::CDBICompat::Constructor'              => { skip => 1 },
+    'DBIx::Class::CDBICompat::Copy' => {
+        ignore => [qw(copy)]
+    },
     'DBIx::Class::CDBICompat::DestroyWarning'           => { skip => 1 },
     'DBIx::Class::CDBICompat::GetSet'                   => { skip => 1 },
     'DBIx::Class::CDBICompat::HasA'                     => { skip => 1 },
@@ -49,6 +58,8 @@ my $exceptions = {
     'DBIx::Class::CDBICompat::ObjIndexStubs'            => { skip => 1 },
     'DBIx::Class::CDBICompat::Pager'                    => { skip => 1 },
     'DBIx::Class::CDBICompat::ReadOnly'                 => { skip => 1 },
+    'DBIx::Class::CDBICompat::Relationship'             => { skip => 1 },
+    'DBIx::Class::CDBICompat::Relationships'            => { skip => 1 },
     'DBIx::Class::CDBICompat::Retrieve'                 => { skip => 1 },
     'DBIx::Class::CDBICompat::Stringify'                => { skip => 1 },
     'DBIx::Class::CDBICompat::TempColumns'              => { skip => 1 },
index 043cec5..005209a 100644 (file)
@@ -7,19 +7,23 @@ BEGIN {
     eval "use DBD::SQLite";
     plan $@
         ? ( skip_all => 'needs DBD::SQLite for testing' )
-        : ( tests => 3 );
+        : ( tests => 4 );
 }
 
 use lib qw(t/lib);
 
 use_ok( 'DBICTest' );
-
 use_ok( 'DBICTest::Schema' );
+my $schema = DBICTest->init_schema;
 
 {
        my $warnings;
        local $SIG{__WARN__} = sub { $warnings .= $_[0] };
-       eval { DBICTest::CD->create({ title => 'vacation in antarctica' }) };
+       eval {
+         $schema->resultset('CD')
+                ->create({ title => 'vacation in antarctica' })
+       };
+       like $@, qr/NULL/;  # as opposed to some other error
        ok( $warnings !~ /uninitialized value/, "No warning from Storage" );
 }
 
index 0fc7e3a..881668d 100644 (file)
@@ -9,8 +9,8 @@ my $schema = DBICTest->init_schema();
 
 BEGIN {
     eval "use DBD::SQLite";
-    plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 9);
-}                                                                               
+    plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 10);
+}
 
 # test LIMIT
 my $it = $schema->resultset("CD")->search( {},
@@ -51,6 +51,15 @@ is( $it->next, undef, "software next past end of resultset ok" );
 );
 is( $cds[0]->title, "Spoonful of bees", "software offset ok" );
 
+
+@cds = $schema->resultset("CD")->search( {},
+    {
+      offset => 2,
+      order_by => 'year' }
+);
+is( $cds[0]->title, "Spoonful of bees", "offset with no limit" );
+
+
 # based on a failing criteria submitted by waswas
 # requires SQL::Abstract >= 1.20
 $it = $schema->resultset("CD")->search(
index c1b67dc..76e18c2 100644 (file)
@@ -4,29 +4,57 @@ use warnings;
 use Test::More;
 use lib qw(t/lib);
 use DBICTest;
-use Storable;
+use Storable qw(dclone freeze thaw);
 
 my $schema = DBICTest->init_schema();
 
-plan tests => 6;
-
-my $artist = $schema->resultset('Artist')->find(1);
-
-{
-  my $copy = $schema->dclone($artist);
-  is_deeply($copy, $artist, "dclone row object works");
-  eval { $copy->discard_changes };
-  ok( !$@, "discard_changes okay" );
-  is($copy->id, $artist->id, "IDs still match ");
-}
-
-{
-  my $ice = $schema->freeze($artist);
-  my $copy = $schema->thaw($ice);
-  is_deeply($copy, $artist, 'dclone row object works');
-
-  eval { $copy->discard_changes };
-  ok( !$@, "discard_changes okay" );
-  is($copy->id, $artist->id, "IDs still okay");
+my %stores = (
+    dclone_method           => sub { return $schema->dclone($_[0]) },
+    dclone_func             => sub { return dclone($_[0]) },
+    "freeze/thaw_method"    => sub {
+        my $ice = $schema->freeze($_[0]);
+        return $schema->thaw($ice);
+    },
+    "freeze/thaw_func"      => sub {
+        thaw(freeze($_[0]));
+    },
+);
+
+plan tests => (7 * keys %stores);
+
+for my $name (keys %stores) {
+    my $store = $stores{$name};
+
+    my $artist = $schema->resultset('Artist')->find(1);
+    
+    # Test that the procedural versions will work if there's a registered
+    # schema as with CDBICompat objects and that the methods work
+    # without.
+    if( $name =~ /func/ ) {
+        $artist->result_source_instance->schema($schema);
+        DBICTest::CD->result_source_instance->schema($schema);
+    }
+    else {
+        $artist->result_source_instance->schema(undef);
+        DBICTest::CD->result_source_instance->schema(undef);
+    }
+
+    my $copy = eval { $store->($artist) };
+    is_deeply($copy, $artist, "serialize row object works: $name");
+
+    # Test that an object with a related_resultset can be serialized.
+    my @cds = $artist->related_resultset("cds");
+
+    ok $artist->{related_resultsets}, 'has key: related_resultsets';
+
+    $copy = eval { $store->($artist) };
+    for my $key (keys %$artist) {
+        next if $key eq 'related_resultsets';
+        next if $key eq '_inflated_column';
+        is_deeply($copy->{$key}, $artist->{$key},
+                  qq[serialize with related_resultset "$key"]);
+    }
+  
+    ok eval { $copy->discard_changes; 1 } or diag $@;
+    is($copy->id, $artist->id, "IDs still match ");
 }
-
diff --git a/t/cdbi-abstract/search_where.t b/t/cdbi-abstract/search_where.t
new file mode 100644 (file)
index 0000000..3a89e3c
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+
+use Test::More;
+
+use strict;
+use warnings;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  if ($@) {
+    plan (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@");
+    next;
+  }
+  eval "use DBD::SQLite";
+  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 10);
+}
+
+INIT {
+       use lib 't/testlib';
+       use Film;
+}
+
+
+Film->create({ Title => $_, Rating => "PG" }) for ("Superman", "Super Fuzz");
+Film->create({ Title => "Batman", Rating => "PG13" });
+
+my $superman = Film->search_where( Title => "Superman" );
+is $superman->next->Title, "Superman", "search_where() as iterator";
+is $superman->next, undef;
+
+{
+    my @supers = Film->search_where({ title => { 'like' => 'Super%' } });
+    is_deeply [sort map $_->Title, @supers],
+              [sort ("Super Fuzz", "Superman")], 'like';
+}
+    
+
+my @all = Film->search_where({}, { order_by => "Title ASC" });
+is_deeply ["Batman", "Super Fuzz", "Superman"],
+          [map $_->Title, @all],
+          "order_by ASC";
+
+@all = Film->search_where({}, { order_by => "Title DESC" });
+is_deeply ["Superman", "Super Fuzz", "Batman"],
+          [map $_->Title, @all],
+          "order_by DESC";
+
+@all = Film->search_where({ Rating => "PG" }, { limit => 1, order_by => "Title ASC" });
+is_deeply ["Super Fuzz"],
+          [map $_->Title, @all],
+          "where, limit";
+
+@all = Film->search_where({}, { limit => 2, order_by => "Title ASC" });
+is_deeply ["Batman", "Super Fuzz"],
+          [map $_->Title, @all],
+          "limit";
+
+@all = Film->search_where({}, { offset => 1, order_by => "Title ASC" });
+is_deeply ["Super Fuzz", "Superman"],
+          [map $_->Title, @all],
+          "offset";
+
+@all = Film->search_where({}, { limit => 1, offset => 1, order_by => "Title ASC" });
+is_deeply ["Super Fuzz"],
+          [map $_->Title, @all],
+          "limit + offset";
+
+@all = Film->search_where({}, { limit => 2, offset => 1,
+                                limit_dialect => "Top", order_by => "Title ASC"
+                              });
+is_deeply ["Super Fuzz", "Superman"],
+          [map $_->Title, @all],
+          "limit_dialect ignored";
+
index 658c500..7b3b1c0 100644 (file)
@@ -4,7 +4,7 @@ use Test::More;
 
 BEGIN {
   eval "use DBIx::Class::CDBICompat;";
-  plan $@ ? (skip_all => 'Class::Trigger and DBIx::ContextualFetch required') : (tests=> 24);
+  plan $@ ? (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@") : (tests=> 24);
 }
 
 
index d303f35..6a4d7f6 100644 (file)
@@ -37,7 +37,7 @@ is(Film->__driver, "SQLite", "Driver set correctly");
        ok $@, "Can't get title with no object";
 } 
 
-eval { my $duh = Film->create; };
+eval { my $duh = Film->insert; };
 like $@, qr/create needs a hashref/, "needs a hashref";
 
 ok +Film->create_test_film;
@@ -163,11 +163,11 @@ is($blrunner_dc->NumExplodingSheep, undef, 'Sheep correct');
 }
 
 eval {
-       my $ishtar = Film->create({ Title => 'Ishtar', Director => 'Elaine May' });
+       my $ishtar = Film->insert({ Title => 'Ishtar', Director => 'Elaine May' });
        my $mandn =
-               Film->create({ Title => 'Mikey and Nicky', Director => 'Elaine May' });
+               Film->insert({ Title => 'Mikey and Nicky', Director => 'Elaine May' });
        my $new_leaf =
-               Film->create({ Title => 'A New Leaf', Director => 'Elaine May' });
+               Film->insert({ Title => 'A New Leaf', Director => 'Elaine May' });
 
 #use Data::Dumper; die Dumper(Film->search( Director => 'Elaine May' ));
        cmp_ok(Film->search(Director => 'Elaine May'), '==', 3,
@@ -263,7 +263,7 @@ SKIP: {
 
 {                               # update deleted object
        my $rt = "Royal Tenenbaums";
-       my $ten = Film->create({ title => $rt, Rating => "R" });
+       my $ten = Film->insert({ title => $rt, Rating => "R" });
        $ten->rating(18);
        Film->set_sql(drt => "DELETE FROM __TABLE__ WHERE title = ?");
        Film->sql_drt->execute($rt);
@@ -284,7 +284,7 @@ SKIP: {
 
 # Primary key of 0
 {
-       my $zero = Film->create({ Title => 0, Rating => "U" });
+       my $zero = Film->insert({ Title => 0, Rating => "U" });
        ok defined $zero, "Create 0";
        ok my $ret = Film->retrieve(0), "Retrieve 0";
        is $ret->Title,  0,   "Title OK";
@@ -344,7 +344,7 @@ if (0) {
 
 {
        {
-               ok my $byebye = DeletingFilm->create(
+               ok my $byebye = DeletingFilm->insert(
                        {
                                Title  => 'Goodbye Norma Jean',
                                Rating => 'PG',
@@ -386,7 +386,7 @@ SKIP: {
        isnt Scalar::Util::refaddr($btaste2), Scalar::Util::refaddr($btaste4),
                "Clearing cache and retrieving again gives new object";
  
-  $btaste=Film->create({
+  $btaste=Film->insert({
                Title             => 'Bad Taste 2',
                Director          => 'Peter Jackson',
                Rating            => 'R',
index 69b3549..39d3efd 100644 (file)
@@ -13,7 +13,7 @@ BEGIN {
     next;
   }
        eval "use DBD::SQLite";
-       plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 25);
+       plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 34);
 }
 
 INIT {
@@ -79,3 +79,72 @@ eval {    # Multiple false columns
 };
 ok($@, $@);
 
+
+# Test that create() and update() throws out columns that changed
+{
+    my $l = Lazy->create({
+        this => 99,
+        that => 2,
+        oop  => 3,
+        opop => 4,
+    });
+
+    ok $l->db_Main->do(qq{
+        UPDATE @{[ $l->table ]}
+        SET    oop  = ?
+        WHERE  this = ?
+    }, undef, 87, $l->this);
+
+    is $l->oop, 87;
+
+    $l->oop(32);
+    $l->update;
+
+    ok $l->db_Main->do(qq{
+        UPDATE @{[ $l->table ]}
+        SET    oop  = ?
+        WHERE  this = ?
+    }, undef, 23, $l->this);
+
+    is $l->oop, 23;
+    
+    $l->delete;
+}
+
+
+# Now again for inflated values
+{
+    Lazy->has_a(
+        orp     => 'Date::Simple',
+        inflate => sub { Date::Simple->new($_[0] . '-01-01') },
+        deflate => 'format'
+    );
+    
+    my $l = Lazy->create({
+        this => 89,
+        that => 2,
+        orp  => 1998,
+    });
+
+    ok $l->db_Main->do(qq{
+        UPDATE @{[ $l->table ]}
+        SET    orp  = ?
+        WHERE  this = ?
+    }, undef, 1987, $l->this);
+    
+    is $l->orp, '1987-01-01';
+
+    $l->orp(2007);
+    is $l->orp, '2007-01-01';   # make sure it's inflated
+    $l->update;
+    
+    ok $l->db_Main->do(qq{
+        UPDATE @{[ $l->table ]}
+        SET    orp  = ?
+        WHERE  this = ?
+    }, undef, 1942, $l->this);
+
+    is $l->orp, '1942-01-01';
+    
+    $l->delete;
+}
index 94757c3..56a1f86 100644 (file)
@@ -4,7 +4,7 @@ use Test::More;
 BEGIN {
   eval "use DBIx::Class::CDBICompat;";
   if ($@) {
-    plan (skip_all => 'Class::Trigger and DBIx::ContextualFetch required');
+    plan (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@");
     next;
   }
   eval "use DBD::SQLite";
diff --git a/t/cdbi-t/08-inheritcols.t b/t/cdbi-t/08-inheritcols.t
new file mode 100644 (file)
index 0000000..7c66949
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More tests => 3;
+
+use Class::DBI;
+
+package A;
+@A::ISA = qw(Class::DBI);
+__PACKAGE__->columns(Primary => 'id');
+
+package A::B;
+@A::B::ISA = 'A';
+__PACKAGE__->columns(All => qw(id b1));
+
+package A::C;
+@A::C::ISA = 'A';
+__PACKAGE__->columns(All => qw(id c1 c2 c3));
+
+package main;
+is join (' ', sort A->columns),    'id',          "A columns";
+is join (' ', sort A::B->columns), 'b1 id',       "A::B columns";
+is join (' ', sort A::C->columns), 'c1 c2 c3 id', "A::C columns";
index 40b186e..febdd70 100644 (file)
@@ -8,7 +8,7 @@ BEGIN {
     next;
   }
   eval "use DBD::SQLite";
-  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 18);
+  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 22);
 }
 
 use lib 't/testlib';
@@ -67,4 +67,17 @@ Film->create_test_film;
                
 }
 
-       
+{
+    my $host = Film->create({ title => "Gwoemul" });
+    $host->blurb("Monsters are real.");
+    my $info = $host->info;
+    is $info->blurb, "Monsters are real.";
+
+    $host->discard_changes;
+    is $host->info->id, $info->id,
+        'relationships still valid after discard_changes';
+
+    ok $host->info->delete;
+    $host->discard_changes;
+    ok !$host->info, 'relationships rechecked after discard_changes';
+}
\ No newline at end of file
index e683f7d..ad76ad1 100644 (file)
@@ -4,11 +4,12 @@ use Test::More;
 BEGIN {
   eval "use DBIx::Class::CDBICompat;";
   if ($@) {
+      diag $@;
     plan (skip_all => 'Class::Trigger and DBIx::ContextualFetch required');
     next;
   }
   eval "use DBD::SQLite";
-  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 53);
+  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 54);
 }
 
 INIT {
@@ -17,7 +18,9 @@ INIT {
        use lib 't/testlib';
        require Film;
        require Actor;
+        require Director;
        Actor->has_a(film => 'Film');
+        Film->has_a(director => 'Director');
        sub Class::DBI::sheep { ok 0; }
 }
 
@@ -33,12 +36,19 @@ sub Film::accessor_name {
        return $col;
 }
 
-sub Actor::accessor_name {
+sub Actor::accessor_name_for {
        my ($class, $col) = @_;
        return "movie" if lc $col eq "film";
        return $col;
 }
 
+# This is a class with accessor_name_for() but no corresponding mutatori_name_for()
+sub Director::accessor_name_for {
+    my($class, $col) = @_;
+    return "nutty_as_a_fruitcake" if lc $col eq "isinsane";
+    return $col;
+}
+
 my $data = {
        Title    => 'Bad Taste',
        Director => 'Peter Jackson',
@@ -131,8 +141,20 @@ eval {
 
 }
 
-SKIP: {    # have non persistent accessor?
-        #skip "Compat layer doesn't handle TEMP columns yet", 11;
+
+# Make sure a class with an accessor_name() method has a similar mutator.
+{
+    my $aki = Director->create({
+        name     => "Aki Kaurismaki",
+    });
+
+    $aki->nutty_as_a_fruitcake(1);
+    is $aki->nutty_as_a_fruitcake, 1,
+        "a custom accessor without a custom mutator is setable";
+    $aki->update;
+}
+
+{
        Film->columns(TEMP => qw/nonpersistent/);
        ok(Film->find_column('nonpersistent'), "nonpersistent is a column");
        ok(!Film->has_real_column('nonpersistent'), " - but it's not real");
@@ -152,8 +174,7 @@ SKIP: {    # have non persistent accessor?
        }
 }
 
-SKIP: {    # was bug with TEMP and no Essential
-        #skip "Compat layer doesn't have TEMP columns yet", 5;
+{
        is_deeply(
                Actor->columns('Essential'),
                Actor->columns('Primary'),
@@ -166,8 +187,7 @@ SKIP: {    # was bug with TEMP and no Essential
        isa_ok $pj => "Actor";
 }
 
-SKIP: {
-        #skip "Compat layer doesn't handle read-only objects yet", 10;
+{
        Film->autoupdate(1);
        my $naked = Film->create({ title => 'Naked' });
        my $sandl = Film->create({ title => 'Secrets and Lies' });
index 36b66af..f725c89 100644 (file)
@@ -8,7 +8,7 @@ BEGIN {
     next;
   }
   eval "use DBD::SQLite";
-  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 17);
+  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 20);
 }
 
 use lib 't/testlib';
@@ -19,6 +19,11 @@ use Actor;
        my @cols = Film->columns('Essential');
        is_deeply \@cols, ['title'], "1 Column in essential";
        is +Film->transform_sql('__ESSENTIAL__'), 'title', '__ESSENTIAL__ expansion';
+       
+       # This provides a more interesting test
+       Film->columns(Essential => qw(title rating));
+       is +Film->transform_sql('__ESSENTIAL__'), 'title, rating',
+           'multi-col __ESSENTIAL__ expansion';
 }
 
 my $f1 = Film->create({ title => 'A', director => 'AA', rating => 'PG' });
@@ -68,6 +73,22 @@ Film->set_sql(
 };
 
 {
+    Film->set_sql(
+        by_id => qq{
+            SELECT  __ESSENTIAL__
+            FROM    __TABLE__
+            WHERE   __IDENTIFIER__
+        }
+    );
+    
+    my $film = Film->retrieve_all->first;
+    my @found = Film->search_by_id($film->id);
+    is @found, 1;
+    is $found[0]->id, $film->id;
+}
+
+
+{
        Actor->has_a(film => "Film");
        Film->set_sql(
                namerate => qq{
@@ -109,4 +130,3 @@ Film->set_sql(
        is $apg[1]->title, "B", "and B";
 }
 
-#} # end SKIP block
index 3c84f4c..d524423 100644 (file)
@@ -4,11 +4,11 @@ use Test::More;
 BEGIN {
   eval "use DBIx::Class::CDBICompat;";
   if ($@) {
-    plan (skip_all => 'Class::Trigger and DBIx::ContextualFetch required');
+    plan (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@");
     next;
   }
   eval "use DBD::SQLite";
-  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 33);
+  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 37);
 }
 
 use lib 't/testlib';
@@ -49,8 +49,6 @@ my @film  = (
        is $it->next->title, "Film 2", "And 2 is still next";
 }
 
-SKIP: {
-  #skip "Iterator doesn't yet have slice support", 19;
 
 {
        my $it = Film->retrieve_all;
@@ -85,4 +83,14 @@ SKIP: {
        is $it->next->title, "Film 2", "And 2 is still next";
 }
 
-} # End SKIP
+{
+  my $it = Film->retrieve_all;
+  is $it, $it->count, "iterator returns count as a scalar";
+  ok $it, "iterator returns true when there are results";
+}
+
+{
+  my $it = Film->search( Title => "something which does not exist" );
+  is $it, 0;
+  ok !$it, "iterator returns false when no results";
+}
diff --git a/t/cdbi-t/22-deflate_order.t b/t/cdbi-t/22-deflate_order.t
new file mode 100644 (file)
index 0000000..965bc49
--- /dev/null
@@ -0,0 +1,24 @@
+$| = 1;
+use strict;
+
+use Test::More;
+
+eval { require Time::Piece::MySQL };
+plan skip_all => "Need Time::Piece::MySQL for this test" if $@;
+
+eval { require 't/testlib/Log.pm' };
+plan skip_all => "Need MySQL for this test" if $@;
+
+plan tests => 2;
+
+package main;
+
+my $log = Log->insert( { message => 'initial message' } );
+ok eval { $log->datetime_stamp }, "Have datetime";
+diag $@ if $@;
+
+$log->message( 'a revised message' );
+$log->update;
+ok eval { $log->datetime_stamp }, "Have datetime after update";
+diag $@ if $@;
+
diff --git a/t/cdbi-t/23-cascade.t b/t/cdbi-t/23-cascade.t
new file mode 100644 (file)
index 0000000..50a1647
--- /dev/null
@@ -0,0 +1,76 @@
+use strict;
+use Test::More;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  if ($@) {
+    plan (skip_all => 'Class::Trigger and DBIx::ContextualFetch required');
+    next;
+  }
+  eval "use DBD::SQLite";
+  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 12);
+}
+
+INIT {
+    use lib 't/testlib';
+    use Film;
+    use Director;
+}
+
+{ # Cascade on delete
+    Director->has_many(nasties => 'Film');
+
+    my $dir = Director->insert({
+        name => "Lewis Teague",
+    });
+    my $kk = $dir->add_to_nasties({
+        Title => 'Alligator'
+    });
+    is $kk->director, $dir, "Director set OK";
+    is $dir->nasties, 1, "We have one nasty";
+
+    ok $dir->delete;
+    ok !Film->retrieve("Alligator"), "has_many cascade deletes by default";
+}
+
+
+# Two ways of saying not to cascade
+for my $args ({ no_cascade_delete => 1 }, { cascade => "None" }) {
+    Director->has_many(nasties => 'Film', $args);
+
+    my $dir = Director->insert({
+        name => "Lewis Teague",
+    });
+    my $kk = $dir->add_to_nasties({
+        Title => 'Alligator'
+    });
+    is $kk->director, $dir, "Director set OK";
+    is $dir->nasties, 1, "We have one nasty";
+
+    ok $dir->delete;
+    ok +Film->retrieve("Alligator"), "has_many with @{[ keys %$args ]} => @{[ values %$args ]}";
+    $kk->delete;
+}
+
+
+#{ # Fail on cascade
+#    local $TODO = 'cascade => "Fail" unimplemented';
+#    
+#    Director->has_many(nasties => Film => { cascade => 'Fail' });
+#
+#    my $dir = Director->insert({ name => "Nasty Noddy" });
+#    my $kk = $dir->add_to_nasties({ Title => 'Killer Killers' });
+#    is $kk->director, $dir, "Director set OK";
+#    is $dir->nasties, 1, "We have one nasty";
+#
+#    ok !eval { $dir->delete };
+#    like $@, qr/1/, "Can't delete while films exist";
+#
+#    my $rr = $dir->add_to_nasties({ Title => 'Revenge of the Revengers' });
+#    ok !eval { $dir->delete };
+#    like $@, qr/2/, "Still can't delete";
+#
+#    $dir->nasties->delete_all;
+#    ok eval { $dir->delete };
+#    is $@, '', "Can delete once films are gone";
+#}
diff --git a/t/cdbi-t/24-meta_info.t b/t/cdbi-t/24-meta_info.t
new file mode 100644 (file)
index 0000000..b7301f9
--- /dev/null
@@ -0,0 +1,66 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More tests => 12;
+use Test::Warn;
+
+package Temp::DBI;
+use base qw(DBIx::Class::CDBICompat);
+Temp::DBI->columns(All => qw(id date));
+Temp::DBI->has_a( date => 'Time::Piece', inflate => sub { 
+       Time::Piece->strptime(shift, "%Y-%m-%d") 
+});
+
+
+package Temp::Person;
+use base 'Temp::DBI';
+Temp::Person->table('people');
+Temp::Person->columns(Info => qw(name pet));
+Temp::Person->has_a( pet => 'Temp::Pet' );
+
+package Temp::Pet;
+use base 'Temp::DBI';
+Temp::Pet->table('pets');
+Temp::Pet->columns(Info => qw(name));
+Temp::Pet->has_many(owners => 'Temp::Person');
+
+package main;
+
+{
+    my $pn_meta = Temp::Person->meta_info('has_a');
+    is_deeply [sort keys %$pn_meta], [qw/date pet/], "Person has Date and Pet";
+}
+
+{
+    my $pt_meta = Temp::Pet->meta_info;
+    is_deeply [keys %{$pt_meta->{has_a}}], [qw/date/], "Pet has Date";
+    is_deeply [keys %{$pt_meta->{has_many}}], [qw/owners/], "And owners";
+}
+
+{
+    my $pet = Temp::Person->meta_info( has_a => 'pet' );
+    is $pet->class,         'Temp::Person';
+    is $pet->foreign_class, 'Temp::Pet';
+    is $pet->accessor,      'pet';
+    is $pet->name,          'has_a';
+}
+
+{
+    my $owners = Temp::Pet->meta_info( has_many => 'owners' );
+    warning_like {
+        local $TODO = 'args is unlikely to ever work';
+
+        is_deeply $owners->args, {
+            foreign_key     => 'pet',
+            mapping         => [],
+            order_by        => undef
+        };
+    } qr/^\Qargs() is unlikely to ever work/;
+}
+
+{
+    my $date = Temp::Pet->meta_info( has_a => 'date' );
+    is $date->class,            'Temp::DBI';
+    is $date->foreign_class,    'Time::Piece';
+    is $date->accessor,         'date';
+}
diff --git a/t/cdbi-t/26-mutator.t b/t/cdbi-t/26-mutator.t
new file mode 100644 (file)
index 0000000..a7f8f98
--- /dev/null
@@ -0,0 +1,41 @@
+use strict;
+use Test::More;
+
+BEGIN {
+       eval "use DBD::SQLite";
+       plan $@
+               ? (skip_all => 'needs DBD::SQLite for testing')
+               : (tests => 6);
+}
+
+use lib 't/testlib';
+require Film;
+
+sub Film::accessor_name_for {
+       my ($class, $col) = @_;
+       return "sheep" if lc $col eq "numexplodingsheep";
+       return $col;
+}
+
+my $data = {
+       Title    => 'Bad Taste',
+       Director => 'Peter Jackson',
+       Rating   => 'R',
+};
+
+my $bt;
+eval {
+       my $data = $data;
+       $data->{sheep} = 1;
+       ok $bt = Film->insert($data), "Modified accessor - with  
+accessor";
+       isa_ok $bt, "Film";
+};
+is $@, '', "No errors";
+
+eval {
+       ok $bt->sheep(2), 'Modified accessor, set';
+       ok $bt->update, 'Update';
+};
+is $@, '', "No errors";
+
diff --git a/t/cdbi-t/columns_as_hashes.t b/t/cdbi-t/columns_as_hashes.t
new file mode 100644 (file)
index 0000000..f85f50f
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+use Test::Warn;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan $@ ? (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@")
+          : ('no_plan');
+}
+
+use lib 't/testlib';
+use Film;
+
+my $waves = Film->insert({
+    Title     => "Breaking the Waves",
+    Director  => 'Lars von Trier',
+    Rating    => 'R'
+});
+
+local $ENV{DBIC_CDBICOMPAT_HASH_WARN} = 0;
+
+{
+    local $ENV{DBIC_CDBICOMPAT_HASH_WARN} = 1;
+
+    warnings_like {
+        my $rating = $waves->{rating};
+        $waves->Rating("PG");
+        is $rating, "R", 'evaluation of column value is not deferred';
+    } qr{^Column 'rating' of 'Film/$waves' was fetched as a hash at \Q$0};
+
+    warnings_like {
+        is $waves->{title}, $waves->Title, "columns can be accessed as hashes";
+    } qr{^Column 'title' of 'Film/$waves' was fetched as a hash at\b};
+
+    $waves->Rating("G");
+
+    warnings_like {
+        is $waves->{rating}, "G", "updating via the accessor updates the hash";
+    } qr{^Column 'rating' of 'Film/$waves' was fetched as a hash at\b};
+
+
+    warnings_like {
+        $waves->{rating} = "PG";
+    } qr{^Column 'rating' of 'Film/$waves' was stored as a hash at\b};
+
+    $waves->update;
+    my @films = Film->search( Rating => "PG", Title => "Breaking the Waves" );
+    is @films, 1, "column updated as hash was saved";
+}
+
+warning_is {
+    $waves->{rating}
+} '', 'DBIC_CDBICOMPAT_HASH_WARN controls warnings';
+
+
+{    
+    $waves->rating("R");
+    $waves->update;
+    
+    no warnings 'redefine';
+    local *Film::rating = sub {
+        return "wibble";
+    };
+    
+    is $waves->{rating}, "R";
+}
+
+
+{
+    no warnings 'redefine';
+    no warnings 'once';
+    local *Actor::accessor_name_for = sub {
+        my($class, $col) = @_;
+        return "movie" if lc $col eq "film";
+        return $col;
+    };
+    
+    require Actor;
+    Actor->has_a( film => "Film" );
+
+    my $actor = Actor->insert({
+        name    => 'Emily Watson',
+        film    => $waves,
+    });
+    
+    ok !eval { $actor->film };
+    is $actor->{film}->id, $waves->id,
+       'hash access still works despite lack of accessor';
+}
+
+
+# Emulate that Class::DBI inflates immediately
+SKIP: {
+    skip "Need MySQL to run this test", 3 unless eval { require MyFoo };
+    
+    my $foo = MyFoo->insert({
+        name    => 'Whatever',
+        tdate   => '1949-02-01',
+    });
+    isa_ok $foo, 'MyFoo';
+    
+    isa_ok $foo->{tdate}, 'Date::Simple';
+    is $foo->{tdate}->year, 1949;
+}
\ No newline at end of file
diff --git a/t/cdbi-t/columns_dont_override_custom_accessors.t b/t/cdbi-t/columns_dont_override_custom_accessors.t
new file mode 100644 (file)
index 0000000..4111b72
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan $@ ? (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@")
+          : (tests=> 5);
+}
+
+{
+    package Thing;
+
+    use base 'DBIx::Class::Test::SQLite';
+
+    Thing->columns(TEMP => qw[foo bar]);
+    Thing->columns(All  => qw[thing_id yarrow flower]);
+    sub foo { 42 }
+    sub yarrow { "hock" }
+}
+
+is_deeply( [sort Thing->columns("TEMP")],
+           [sort qw(foo bar)],
+           "TEMP columns set"
+);
+my $thing = Thing->construct(
+    { thing_id => 23, foo => "this", bar => "that" }
+);
+
+is( $thing->id, 23 );
+is( $thing->yarrow, "hock", 'custom accessor not overwritten by column' );
+is( $thing->foo, 42, 'custom routine not overwritten by temp column' );
+is( $thing->bar, "that", 'temp column accessor generated' );
diff --git a/t/cdbi-t/construct.t b/t/cdbi-t/construct.t
new file mode 100644 (file)
index 0000000..59adef1
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan $@ ? (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@")
+          : (tests=> 4);
+}
+
+INIT {
+    use lib 't/testlib';
+    use Film;
+}
+
+Film->insert({
+    Title     => "Breaking the Waves",
+    Director  => 'Lars von Trier',
+    Rating    => 'R'
+});
+
+my $film = Film->construct({
+    Title     => "Breaking the Waves",
+    Director  => 'Lars von Trier',
+});
+
+isa_ok $film, "Film";
+is $film->title, "Breaking the Waves";
+is $film->director, "Lars von Trier";
+is $film->rating, "R", "constructed objects can get missing data from the db";
\ No newline at end of file
diff --git a/t/cdbi-t/copy.t b/t/cdbi-t/copy.t
new file mode 100644 (file)
index 0000000..cdcae15
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan $@ ? (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@")
+          : (tests=> 4);
+}
+
+INIT {
+    use lib 't/testlib';
+}
+
+{
+    package # hide from PAUSE 
+        MyFilm;
+
+    use base 'DBIx::Class::Test::SQLite';
+    use strict;
+
+    __PACKAGE__->set_table('Movies');
+    __PACKAGE__->columns(All => qw(id title));
+
+    sub create_sql {
+        return qq{
+                id              INTEGER PRIMARY KEY AUTOINCREMENT,
+                title           VARCHAR(255)
+        }
+    }
+}
+
+my $film = MyFilm->create({ title => "For Your Eyes Only" });
+ok $film->id;
+
+my $new_film = $film->copy;
+ok $new_film->id;
+isnt $new_film->id, $film->id, "copy() gets new primary key";
+
+$new_film = $film->copy(42);
+is $new_film->id, 42, "copy() with new id";
+
diff --git a/t/cdbi-t/discard_changes_in_DESTROY.t b/t/cdbi-t/discard_changes_in_DESTROY.t
new file mode 100644 (file)
index 0000000..950d9bd
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan $@ ? (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@")
+          : (tests=> 1);
+}
+
+INIT {
+    use lib 't/testlib';
+    use Film;
+}
+
+{
+    my @warnings;
+    local $SIG{__WARN__} = sub { push @warnings, @_; };
+    {
+        # Test that this doesn't cause infinite recursion.
+        local *Film::DESTROY;
+        local *Film::DESTROY = sub { $_[0]->discard_changes };
+        
+        my $film = Film->insert({ Title => "Eegah!" });
+        $film->director("Arch Hall Sr.");
+    }
+    is_deeply \@warnings, [];
+}
\ No newline at end of file
diff --git a/t/cdbi-t/hasa_without_loading.t b/t/cdbi-t/hasa_without_loading.t
new file mode 100644 (file)
index 0000000..5b8cbdf
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan $@ ? (skip_all => 'Class::Trigger and DBIx::ContextualFetch required')
+          : (tests=> 3);
+}
+
+package Foo;
+
+use base qw(Class::DBI);
+
+eval {
+    Foo->table("foo");
+    Foo->columns(Essential => qw(foo bar));
+    Foo->has_a( bar => "This::Does::Not::Exist::Yet" );
+};
+::is $@, '';
+::is(Foo->table, "foo");
+::is_deeply [sort map lc, Foo->columns], [sort map lc, qw(foo bar)];
diff --git a/t/cdbi-t/max_min_value_of.t b/t/cdbi-t/max_min_value_of.t
new file mode 100644 (file)
index 0000000..f4a0bda
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+
+#----------------------------------------------------------------------
+# Test database failures
+#----------------------------------------------------------------------
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  if ($@) {
+    plan (skip_all => 'Class::Trigger and DBIx::ContextualFetch required');
+    next;
+  }
+  eval "use DBD::SQLite";
+  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 2);
+}
+
+use lib 't/testlib';
+use Film;
+
+Film->create({
+    title => "Bad Taste",
+    numexplodingsheep => 10,
+});
+
+Film->create({
+    title => "Evil Alien Conquerers",
+    numexplodingsheep => 2,
+});
+
+is( Film->maximum_value_of("numexplodingsheep"), 10 );
+is( Film->minimum_value_of("numexplodingsheep"), 2  );
diff --git a/t/cdbi-t/multi_column_set.t b/t/cdbi-t/multi_column_set.t
new file mode 100644 (file)
index 0000000..eb985e3
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan $@ ? (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@")
+          : (tests=> 3);
+}
+
+{
+    package Thing;
+
+    use base 'DBIx::Class::Test::SQLite';
+
+    Thing->columns(TEMP => qw[foo bar baz]);
+    Thing->columns(All  => qw[some real stuff]);
+}
+
+my $thing = Thing->construct({ foo => 23, some => 42, baz => 99 });
+$thing->set( foo => "wibble", some => "woosh" );
+is $thing->foo, "wibble";
+is $thing->some, "woosh";
+is $thing->baz, 99;
+
+$thing->discard_changes;
diff --git a/t/cdbi-t/object_cache.t b/t/cdbi-t/object_cache.t
new file mode 100644 (file)
index 0000000..e194a31
--- /dev/null
@@ -0,0 +1,76 @@
+use strict;
+use Test::More;
+$| = 1;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  if ($@) {
+    plan (skip_all => 'Class::Trigger and DBIx::ContextualFetch required');
+    next;
+  }
+  eval "use DBD::SQLite";
+  plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 5);
+}
+
+INIT {
+    use lib 't/testlib';
+    use Film;
+}
+
+ok +Film->create({
+    Title       => 'This Is Spinal Tap',
+    Director    => 'Rob Reiner',
+    Rating      => 'R',
+});
+
+{
+    my $film1 = Film->retrieve( "This Is Spinal Tap" );
+    my $film2 = Film->retrieve( "This Is Spinal Tap" );
+
+    $film1->Director("Marty DiBergi");
+    is $film2->Director, "Marty DiBergi", 'retrieve returns the same object';
+
+    $film1->discard_changes;
+}
+
+{
+    Film->nocache(1);
+    
+    my $film1 = Film->retrieve( "This Is Spinal Tap" );
+    my $film2 = Film->retrieve( "This Is Spinal Tap" );
+
+    $film1->Director("Marty DiBergi");
+    is $film2->Director, "Rob Reiner",
+       'caching turned off';
+    
+    $film1->discard_changes;
+}
+
+{
+    Film->nocache(0);
+
+    my $film1 = Film->retrieve( "This Is Spinal Tap" );
+    my $film2 = Film->retrieve( "This Is Spinal Tap" );
+
+    $film1->Director("Marty DiBergi");
+    is $film2->Director, "Marty DiBergi",
+       'caching back on';
+
+    $film1->discard_changes;
+}
+
+
+{
+    Film->nocache(1);
+
+    local $Class::DBI::Weaken_Is_Available = 0;
+
+    my $film1 = Film->retrieve( "This Is Spinal Tap" );
+    my $film2 = Film->retrieve( "This Is Spinal Tap" );
+
+    $film1->Director("Marty DiBergi");
+    is $film2->Director, "Rob Reiner",
+       'CDBI::Weaken_Is_Available turns off all caching';
+
+    $film1->discard_changes;
+}
diff --git a/t/cdbi-t/retrieve_from_sql_with_limit.t b/t/cdbi-t/retrieve_from_sql_with_limit.t
new file mode 100644 (file)
index 0000000..e0c452d
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan $@ ? (skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@")
+          : (tests=> 3);
+}
+
+INIT {
+    use lib 't/testlib';
+    use Film;
+}
+
+for my $title ("Bad Taste", "Braindead", "Forgotten Silver") {
+    Film->insert({ Title => $title, Director => 'Peter Jackson' });
+}
+
+Film->insert({ Title => "Transformers", Director => "Michael Bay"});
+
+{
+    my @films = Film->retrieve_from_sql(qq[director = "Peter Jackson" LIMIT 2]);
+    is @films, 2, "retrieve_from_sql with LIMIT";
+    is( $_->director, "Peter Jackson" ) for @films;
+}
diff --git a/t/cdbi-t/set_to_undef.t b/t/cdbi-t/set_to_undef.t
new file mode 100644 (file)
index 0000000..bad9919
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+use Test::NoWarnings;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@"
+    if $@;
+  plan skip_all => "DateTime required" unless eval { require DateTime };
+  plan tests => 1;
+}
+
+{
+    package Thing;
+
+    use base 'DBIx::Class::Test::SQLite';
+
+    Thing->columns(All  => qw[thing_id this that date]);
+}
+
+my $thing = Thing->construct({ thing_id => 23, this => 42 });
+$thing->set( this => undef );
+$thing->discard_changes;
diff --git a/t/cdbi-t/set_vs_DateTime.t b/t/cdbi-t/set_vs_DateTime.t
new file mode 100644 (file)
index 0000000..fb76561
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More;
+use Test::Exception;
+
+BEGIN {
+  eval "use DBIx::Class::CDBICompat;";
+  plan skip_all => "Class::Trigger and DBIx::ContextualFetch required: $@"
+    if $@;
+  plan skip_all => "DateTime required" unless eval { require DateTime };
+  plan tests => 1;
+}
+
+{
+    package Thing;
+
+    use base 'DBIx::Class::Test::SQLite';
+
+    Thing->columns(All  => qw[thing_id this that date]);
+}
+
+my $thing = Thing->construct({ thing_id => 23, date => "01-02-1994" });
+my $date = DateTime->now;
+lives_ok {
+  $thing->set( date => $date );
+  $thing->set( date => $date );
+};
+
+
+
+$thing->discard_changes;
diff --git a/t/deleting_many_to_many.t b/t/deleting_many_to_many.t
new file mode 100644 (file)
index 0000000..5613721
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;  
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+
+my $schema = DBICTest->init_schema();
+
+plan tests => 5;
+
+my $cd = $schema->resultset("CD")->find(2);
+ok $cd->liner_notes;
+ok keys %{$cd->{_relationship_data}}, "_relationship_data populated";
+
+$cd->discard_changes;
+ok $cd->liner_notes, 'relationships still valid after discarding changes';
+
+ok $cd->liner_notes->delete;
+$cd->discard_changes;
+ok !$cd->liner_notes, 'discard_changes resets relationship';
\ No newline at end of file
diff --git a/t/relationship_after_update.t b/t/relationship_after_update.t
new file mode 100644 (file)
index 0000000..aaf7300
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;  
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+
+my $schema = DBICTest->init_schema();
+
+plan tests => 2;
+
+my $bookmark = $schema->resultset("Bookmark")->find(1);
+my $link = $bookmark->link;
+my $link_id = $link->id;
+
+my $new_link = $schema->resultset("Link")->new({
+    id      => 42,
+    url     => "http://monstersarereal.com",
+    title   => "monstersarereal.com"
+});
+
+# Changing a relationship by id rather than by object would cause
+# old related_resultsets to be used.
+$bookmark->link($new_link->id);
+is $bookmark->link->id, $new_link->id;
+
+$bookmark->update;
+is $bookmark->link->id, $new_link->id;
diff --git a/t/relationship_doesnt_exist.t b/t/relationship_doesnt_exist.t
new file mode 100644 (file)
index 0000000..d440b52
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;  
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+
+my $schema = DBICTest->init_schema();
+
+plan tests => 3;
+
+my $bookmark = $schema->resultset("Bookmark")->find(1);
+my $link = $bookmark->link;
+my $link_id = $link->id;
+ok $link->id;
+
+$link->delete;
+is $schema->resultset("Link")->search(id => $link_id)->count, 0,
+    "link $link_id was deleted";
+
+# Get a fresh object with nothing cached
+$bookmark = $schema->resultset("Bookmark")->find($bookmark->id);
+
+# This would create a new link row if none existed
+$bookmark->link;
+
+is $schema->resultset("Link")->search(id => $link_id)->count, 0,
+    'accessor did not create a link object where there was none';
diff --git a/t/resultset_overload.t b/t/resultset_overload.t
new file mode 100644 (file)
index 0000000..c5ecce8
--- /dev/null
@@ -0,0 +1,26 @@
+use strict;
+use warnings;  
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+
+my $schema = DBICTest->init_schema();
+
+plan tests => 6;
+
+{
+  my $rs = $schema->resultset("CD")->search({});
+
+  ok $rs->count;
+  is $rs, $rs->count, "resultset as number with results";
+  ok $rs,             "resultset as boolean always true";
+}
+
+{
+  my $rs = $schema->resultset("CD")->search({ title => "Does not exist" });
+  
+  ok !$rs->count;
+  is $rs, $rs->count, "resultset as number without results";
+  ok $rs,             "resultset as boolean always true";
+}
\ No newline at end of file
index da1e86f..7951482 100644 (file)
@@ -2,7 +2,9 @@ package # hide from PAUSE
     MyBase;
 
 use strict;
-use base qw(DBIx::Class);
+use base qw(DBIx::Class::CDBICompat);
+
+use DBI;
 
 use vars qw/$dbh/;
 
index fa536ab..d645d3d 100644 (file)
@@ -13,7 +13,7 @@ __PACKAGE__->has_a(
        inflate => sub { Date::Simple->new(shift) },
        deflate => 'format',
 );
-__PACKAGE__->find_column('tdate')->placeholder("IF(1, CURDATE(), ?)");
+#__PACKAGE__->find_column('tdate')->placeholder("IF(1, CURDATE(), ?)");
 
 sub create_sql {
        return qq{
index 5428a50..8c13493 100644 (file)
@@ -2,7 +2,7 @@ package # hide from PAUSE
     PgBase;
 
 use strict;
-use base 'DBIx::Class';
+use base 'DBIx::Class::CDBICompat';
 
 my $db   = $ENV{DBD_PG_DBNAME} || 'template1';
 my $user = $ENV{DBD_PG_USER}   || 'postgres';