Initial commit of DBIx::Class (experimental Class::DBI-inspired ORM)
Matt S Trout [Tue, 19 Jul 2005 10:07:29 +0000 (10:07 +0000)]
29 files changed:
lib/DBIx/Class.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/AccessorMapping.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/ColumnCase.pm [new file with mode: 0644]
lib/DBIx/Class/CDBICompat/ColumnGroups.pm [new file with mode: 0644]
lib/DBIx/Class/Core.pm [new file with mode: 0644]
lib/DBIx/Class/DB.pm [new file with mode: 0644]
lib/DBIx/Class/SQL.pm [new file with mode: 0644]
lib/DBIx/Class/Table.pm [new file with mode: 0644]
lib/DBIx/Class/Test/SQLite.pm [new file with mode: 0644]
t/01-columns.t [new file with mode: 0644]
t/02-Film.t [new file with mode: 0644]
t/testlib/Actor.pm [new file with mode: 0644]
t/testlib/Binary.pm [new file with mode: 0644]
t/testlib/Blurb.pm [new file with mode: 0644]
t/testlib/CDBase.pm [new file with mode: 0644]
t/testlib/Director.pm [new file with mode: 0644]
t/testlib/Film.pm [new file with mode: 0644]
t/testlib/Lazy.pm [new file with mode: 0644]
t/testlib/Log.pm [new file with mode: 0644]
t/testlib/MyBase.pm [new file with mode: 0644]
t/testlib/MyFilm.pm [new file with mode: 0644]
t/testlib/MyFoo.pm [new file with mode: 0644]
t/testlib/MyStar.pm [new file with mode: 0644]
t/testlib/MyStarLink.pm [new file with mode: 0644]
t/testlib/MyStarLinkMCPK.pm [new file with mode: 0644]
t/testlib/Order.pm [new file with mode: 0644]
t/testlib/OtherFilm.pm [new file with mode: 0644]
t/testlib/PgBase.pm [new file with mode: 0644]

diff --git a/lib/DBIx/Class.pm b/lib/DBIx/Class.pm
new file mode 100644 (file)
index 0000000..43ce538
--- /dev/null
@@ -0,0 +1,5 @@
+package DBIx::Class;
+
+use base qw/DBIx::Class::CDBICompat DBIx::Class::Core/;
+
+1;
diff --git a/lib/DBIx/Class/CDBICompat.pm b/lib/DBIx/Class/CDBICompat.pm
new file mode 100644 (file)
index 0000000..22fd5e7
--- /dev/null
@@ -0,0 +1,10 @@
+package DBIx::Class::CDBICompat;
+
+use strict;
+use warnings;
+
+use base qw/DBIx::Class::CDBICompat::AccessorMapping
+            DBIx::Class::CDBICompat::ColumnCase
+            DBIx::Class::CDBICompat::ColumnGroups/;
+
+1;
diff --git a/lib/DBIx/Class/CDBICompat/AccessorMapping.pm b/lib/DBIx/Class/CDBICompat/AccessorMapping.pm
new file mode 100644 (file)
index 0000000..cdfd851
--- /dev/null
@@ -0,0 +1,29 @@
+package DBIx::Class::CDBICompat::AccessorMapping;
+
+use strict;
+use warnings;
+
+use NEXT;
+
+sub _mk_column_accessors {
+  my ($class, @cols) = @_;
+  unless ($class->can('accessor_name') || $class->can('mutator_name')) {
+    return $class->NEXT::_mk_column_accessors(@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);
+    if ($ro_meth eq $wo_meth) {
+      $class->mk_accessors($col);
+    } else {
+      $class->mk_ro_accessors($ro_meth);
+      $class->mk_wo_accessors($wo_meth);
+    }
+  }
+}
+
+1;
diff --git a/lib/DBIx/Class/CDBICompat/ColumnCase.pm b/lib/DBIx/Class/CDBICompat/ColumnCase.pm
new file mode 100644 (file)
index 0000000..fe9084a
--- /dev/null
@@ -0,0 +1,40 @@
+package DBIx::Class::CDBICompat::ColumnCase;
+
+use strict;
+use warnings;
+use NEXT;
+
+sub _register_column_group {
+  my ($class, $group, @cols) = @_;
+  return $class->NEXT::_register_column_group($group => map lc, @cols);
+}
+
+sub _register_columns {
+  my ($class, @cols) = @_;
+  return $class->NEXT::_register_columns(map lc, @cols);
+}
+
+sub get {
+  my ($class, $get, @rest) = @_;
+  return $class->NEXT::get(lc $get, @rest);
+}
+
+sub set {
+  my ($class, $set, @rest) = @_;
+  return $class->NEXT::set(lc $set, @rest);
+}
+
+sub find_column {
+  my ($class, $col) = @_;
+  return $class->NEXT::find_column(lc $col);
+}
+
+sub _mk_accessors {
+  my ($class, $type, @fields) = @_;
+  my %fields;
+  $fields{$_} = 1 for @fields,
+                    map lc, grep { !defined &{"${class}::${_}"} } @fields;
+  return $class->NEXT::_mk_accessors($type, keys %fields);
+}
+
+1;
diff --git a/lib/DBIx/Class/CDBICompat/ColumnGroups.pm b/lib/DBIx/Class/CDBICompat/ColumnGroups.pm
new file mode 100644 (file)
index 0000000..3bea78d
--- /dev/null
@@ -0,0 +1,93 @@
+package DBIx::Class::CDBICompat::ColumnGroups;
+
+use strict;
+use warnings;
+use NEXT;
+
+use base qw/Class::Data::Inheritable/;
+
+__PACKAGE__->mk_classdata('_column_groups' => { });
+
+sub table {
+  shift->_table_name(@_);
+}
+
+sub columns {
+  my $proto = shift;
+  my $class = ref $proto || $proto;
+  my $group = shift || "All";
+  $class->_set_column_group($group => @_) if @_;
+  return $class->all_columns    if $group eq "All";
+  return $class->primary_column if $group eq "Primary";
+  return keys %{$class->_column_groups->{$group}};
+}
+
+sub _set_column_group {
+  my ($class, $group, @cols) = @_;
+  $class->_register_column_group($group => @cols);
+  $class->_register_columns(@cols);
+  $class->_mk_column_accessors(@cols);
+}
+
+sub _register_column_group {
+  my ($class, $group, @cols) = @_;
+  if ($group eq 'Primary') {
+    my %pri;
+    $pri{$_} = {} for @cols;
+    $class->_primaries(\%pri);
+  }
+
+  my $groups = { %{$class->_column_groups} };
+
+  if ($group eq 'All') {
+    unless ($class->_column_groups->{'Primary'}) {
+      $groups->{'Primary'}{$cols[0]} = {};
+      $class->_primaries({ $cols[0] => {} });
+    }
+    unless ($class->_column_groups->{'Essential'}) {
+      $groups->{'Essential'}{$cols[0]} = {};
+    }
+  }
+
+  $groups->{$group}{$_} ||= {} for @cols;
+  $class->_column_groups($groups);
+}
+
+sub all_columns { return keys %{$_[0]->_columns}; }
+
+sub primary_column {
+  my ($class) = @_;
+  my @pri = keys %{$class->_primaries};
+  return wantarray ? @pri : $pri[0];
+}
+
+sub find_column {
+  my ($class, $col) = @_;
+  return $col if $class->_columns->{$col};
+}
+
+sub __grouper {
+  my ($class) = @_;
+  return bless({ class => $class}, 'DBIx::Class::CDBICompat::ColumnGroups::GrouperShim');
+}
+
+sub _find_columns {
+  my ($class, @col) = @_;
+  return map { $class->find_column($_) } @col;
+}
+
+package DBIx::Class::CDBICompat::ColumnGroups::GrouperShim;
+
+sub groups_for {
+  my ($self, @cols) = @_;
+  my %groups;
+  foreach my $col (@cols) {
+    foreach my $group (keys %{$self->{class}->_column_groups}) {
+      $groups{$group} = 1 if $self->{class}->_column_groups->{$group}->{$col};
+    }
+  }
+  return keys %groups;
+}
+    
+
+1;
diff --git a/lib/DBIx/Class/Core.pm b/lib/DBIx/Class/Core.pm
new file mode 100644 (file)
index 0000000..c90d7bc
--- /dev/null
@@ -0,0 +1,8 @@
+package DBIx::Class::Core;
+
+use strict;
+use warnings;
+
+use base qw/DBIx::Class::Table DBIx::Class::SQL DBIx::Class::DB/;
+
+1;
diff --git a/lib/DBIx/Class/DB.pm b/lib/DBIx/Class/DB.pm
new file mode 100644 (file)
index 0000000..3fc3013
--- /dev/null
@@ -0,0 +1,29 @@
+package DBIx::Class::DB;
+
+use base qw/Class::Data::Inheritable/;
+
+__PACKAGE__->mk_classdata('_dbi_connect_info');
+__PACKAGE__->mk_classdata('_dbi_connect_package');
+__PACKAGE__->mk_classdata('_dbh');
+
+sub _get_dbh {
+  my ($class) = @_;
+  unless ((my $dbh = $class->_dbh) && $dbh->FETCH('Active') && $dbh->ping) {
+    $class->_populate_dbh;
+  }
+  return $class->_dbh;
+}
+
+sub _populate_dbh {
+  my ($class) = @_;
+  my @info = @{$class->_dbi_connect_info || []};
+  my $pkg = $class->_dbi_connect_package || $class;
+  $pkg->_dbh($class->_dbi_connect(@info));
+}
+
+sub _dbi_connect {
+  my ($class, @info) = @_;
+  return DBI->connect_cached(@info);
+}
+
+1;
diff --git a/lib/DBIx/Class/SQL.pm b/lib/DBIx/Class/SQL.pm
new file mode 100644 (file)
index 0000000..2f6326e
--- /dev/null
@@ -0,0 +1,41 @@
+package DBIx::Class::SQL;
+
+use strict;
+use warnings;
+
+use base qw/Class::Data::Inheritable/;
+
+use constant COLS => 0;
+use constant FROM => 1;
+use constant COND => 2;
+
+__PACKAGE__->mk_classdata('_sql_statements',
+  {
+    'select' =>
+      sub { "SELECT ".join(', ', @$_[COLS])." FROM $_[FROM] WHERE $_[COND]"; },
+    'update' =>
+      sub { "UPDATE $_[FROM] SET ".join(', ', map { "$_ = ?" } @$_[COLS]).
+              " WHERE $_[COND]"; },
+    'insert' =>
+      sub { "INSERT INTO $_[FROM] (".join(', ', @$_[COLS]).") VALUES (".
+              join(', ', map { '?' } @$_[COLS]).")"; },
+    'delete' =>
+      sub { "DELETE FROM $_[FROM] WHERE $_[COND]"; },
+  } );
+
+sub _get_sql {
+  my ($class, $name, $cols, $from, $cond) = @_;
+  return $class->_sql_statements->{$name}->($cols, $from, $cond);
+}
+
+sub _sql_to_sth {
+  my ($class, $sql) = @_;
+  return $class->_get_dbh->prepare($sql);
+}
+
+sub _get_sth {
+  my $class = shift;
+  return $class->_sql_to_sth($class->_get_sql(@_));
+}
+
+1;
diff --git a/lib/DBIx/Class/Table.pm b/lib/DBIx/Class/Table.pm
new file mode 100644 (file)
index 0000000..9556eb9
--- /dev/null
@@ -0,0 +1,98 @@
+package DBIx::Class::Table;
+
+use strict;
+use warnings;
+
+use base qw/Class::Data::Inheritable Class::Accessor DBIx::Class::SQL/;
+
+__PACKAGE__->mk_classdata('_columns' => {});
+
+__PACKAGE__->mk_classdata('_primaries' => {});
+
+__PACKAGE__->mk_classdata('_table_name');
+
+sub new {
+  my ($class, $attrs) = @_;
+  $class = ref $class if ref $class;
+  my $new = bless({ _column_data => { } }, $class);
+  if ($attrs) {
+    die "Attrs must be a hashref" unless ref($attrs) eq 'HASH';
+    while (my ($k, $v) = each %{$attrs}) {
+      $new->set_column($k => $v);
+    }
+  }
+}
+
+sub insert {
+  my ($self) = @_;
+  return if $self->{_in_database};
+  my $sth = $self->_get_sth('insert', [ keys %{$self->{_column_data}} ],
+                              $self->_table_name, undef);
+  $sth->execute(values %{$self->{_column_data}});
+  $self->{_in_database} = 1;
+  return $self;
+}
+
+sub create {
+  my ($class, $attrs) = @_;
+  return $class->new($attrs)->insert;
+}
+
+sub update {
+  my ($self) = @_;
+  die "Not in database" unless $self->{_in_database};
+  my @to_update = keys %{$self->{_dirty_columns} || {}};
+  my $sth = $self->_get_sth('update', \@to_update,
+                              $self->_table_name, $self->_ident_cond);
+  $sth->execute( (map { $self->{_column_data}{$_} } @to_update),
+                  $self->_ident_values );
+  $self->{_dirty_columns} = {};
+  return $self;
+}
+
+sub delete {
+  my ($self) = @_;
+  my $sth = $self->_get_sth('delete', undef,
+                              $self->_table_name, $self->_ident_cond);
+  $sth->execute($self->_ident_values);
+  delete $self->{_in_database};
+  return $self;
+}
+
+sub get {
+  my ($self, $column) = @_;
+  die "No such column '${column}'" unless $self->_columns->{$column};
+  return $self->{_column_data}{$column};
+}
+
+sub set {
+  my ($self, $column, $value) = @_;
+  die "No such column '${column}'" unless $self->_columns->{$column};
+  die "set_column called for ${column} without value" if @_ < 3;
+  $self->{_dirty_columns}{$column} = 1;
+  return $self->{_column_data}{$column} = $value;
+}
+
+sub _ident_cond {
+  my ($class) = @_;
+  return join(" AND ", map { "$_ = ?" } keys %{$class->_primaries});
+}
+
+sub _ident_values {
+  my ($self) = @_;
+  return (map { $self->{_column_data}{$_} } keys %{$self->_primaries});
+}
+
+sub _register_columns {
+  my ($class, @cols) = @_;
+  my $names = { %{$class->_columns} };
+  $names->{$_} ||= {} for @cols;
+  $class->_columns($names); 
+}
+
+sub _mk_column_accessors {
+  my ($class, @cols) = @_;
+  $class->mk_accessors(@cols);
+}
+
+1;
diff --git a/lib/DBIx/Class/Test/SQLite.pm b/lib/DBIx/Class/Test/SQLite.pm
new file mode 100644 (file)
index 0000000..02d300d
--- /dev/null
@@ -0,0 +1,87 @@
+package DBIx::Class::Test::SQLite;
+
+=head1 NAME
+
+DBIx::Class::Test::SQLite - Base class for DBIx::Class tests, shamelessly ripped from Class::DBI::Test::SQLite
+
+=head1 SYNOPSIS
+
+       use base 'DBIx::Class::Test::SQLite';
+
+       __PACKAGE__->set_table('test');
+       __PACKAGE__->columns(All => qw/id name film salary/);
+
+       sub create_sql { 
+               return q{
+                       id     INTEGER PRIMARY KEY,
+                       name   CHAR(40),
+                       film   VARCHAR(255),
+                       salary INT
+               }
+       }
+       
+=head1 DESCRIPTION
+
+This provides a simple base class for DBIx::Class tests using SQLite.
+Each class for the test should inherit from this, provide a create_sql()
+method which returns a string representing the SQL used to create the
+table for the class, and then call set_table() to create the table, and
+tie it to the class.
+
+=cut
+
+use strict;
+
+use base 'DBIx::Class';
+use File::Temp qw/tempfile/;
+my (undef, $DB) = tempfile();
+END { unlink $DB if -e $DB }
+
+my @DSN = ("dbi:SQLite:dbname=$DB", '', '', { AutoCommit => 1 });
+
+__PACKAGE__->connection(@DSN);
+__PACKAGE__->set_sql(_table_pragma => 'PRAGMA table_info(__TABLE__)');
+__PACKAGE__->set_sql(_create_me    => 'CREATE TABLE __TABLE__ (%s)');
+
+=head1 METHODS
+
+=head2 set_table
+
+       __PACKAGE__->set_table('test');
+
+This combines creating the table with the normal DBIx::Class table()
+call.
+
+=cut
+
+sub set_table {
+       my ($class, $table) = @_;
+       $class->table($table);
+       $class->_create_test_table;
+}
+
+sub _create_test_table {
+       my $class = shift;
+       my @vals  = $class->sql__table_pragma->select_row;
+       $class->sql__create_me($class->create_sql)->execute unless @vals;
+}
+
+=head2 create_sql (abstract)
+
+       sub create_sql { 
+               return q{
+                       id     INTEGER PRIMARY KEY,
+                       name   CHAR(40),
+                       film   VARCHAR(255),
+                       salary INT
+               }
+       }
+
+This should return, as a text string, the schema for the table represented
+by this class.
+
+=cut
+
+sub create_sql { die "create_sql() not implemented by $_[0]\n" }
+
+1;
diff --git a/t/01-columns.t b/t/01-columns.t
new file mode 100644 (file)
index 0000000..50d5831
--- /dev/null
@@ -0,0 +1,131 @@
+use strict;
+
+use Test::More tests => 25;
+
+#-----------------------------------------------------------------------
+# Make sure that we can set up columns properly
+#-----------------------------------------------------------------------
+package State;
+
+use base 'DBIx::Class';
+
+State->table('State');
+State->columns(Essential => qw/Abbreviation Name/);
+State->columns(Primary =>   'Name');
+State->columns(Weather =>   qw/Rain Snowfall/);
+State->columns(Other =>     qw/Capital Population/);
+#State->has_many(cities => "City");
+
+sub accessor_name {
+       my ($class, $column) = @_;
+       my $return = $column eq "Rain" ? "Rainfall" : $column;
+       return $return;
+}
+
+sub mutator_name {
+       my ($class, $column) = @_;
+       my $return = $column eq "Rain" ? "set_Rainfall" : "set_$column";
+       return $return;
+}
+
+sub Snowfall { 1 }
+
+
+package City;
+
+use base 'DBIx::Class';
+
+City->table('City');
+City->columns(All => qw/Name State Population/);
+#City->has_a(State => 'State');
+
+
+#-------------------------------------------------------------------------
+package CD;
+use base 'DBIx::Class';
+
+CD->table('CD');
+CD->columns('All' => qw/artist title length/);
+
+#-------------------------------------------------------------------------
+
+package main;
+
+is(State->table,          'State', 'State table()');
+is(State->primary_column, 'name',  'State primary()');
+is_deeply [ State->columns('Primary') ] => [qw/name/],
+       'State Primary:' . join ", ", State->columns('Primary');
+is_deeply [ sort State->columns('Essential') ] => [qw/abbreviation name/],
+       'State Essential:' . join ", ", State->columns('Essential');
+is_deeply [ sort State->columns('All') ] =>
+       [ sort qw/name abbreviation rain snowfall capital population/ ],
+       'State All:' . join ", ", State->columns('All');
+
+is(CD->primary_column, 'artist', 'CD primary()');
+is_deeply [ CD->columns('Primary') ] => [qw/artist/],
+       'CD primary:' . join ", ", CD->columns('Primary');
+is_deeply [ sort CD->columns('All') ] => [qw/artist length title/],
+       'CD all:' . join ", ", CD->columns('All');
+is_deeply [ sort CD->columns('Essential') ] => [qw/artist/],
+       'CD essential:' . join ", ", CD->columns('Essential');
+
+ok(State->find_column('Rain'), 'find_column Rain');
+ok(State->find_column('rain'), 'find_column rain');
+ok(!State->find_column('HGLAGAGlAG'), '!find_column HGLAGAGlAG');
+
+{
+    
+    can_ok +State => qw/Rainfall _Rainfall_accessor set_Rainfall
+       _set_Rainfall_accessor Snowfall _Snowfall_accessor set_Snowfall
+       _set_Snowfall_accessor/;
+    
+    foreach my $method (qw/Rain _Rain_accessor rain snowfall/) { 
+       ok !State->can($method), "State can't $method";
+    }
+
+}
+
+{
+        SKIP: {
+          skip "Different error message", 1;
+
+         eval { my @grps = State->__grouper->groups_for("Huh"); };
+         ok $@, "Huh not in groups";
+        }
+
+       my @grps = sort State->__grouper->groups_for(State->_find_columns(qw/rain capital/));
+       is @grps, 2, "Rain and Capital = 2 groups";
+        my @grps = sort @grps; # Because DBIx::Class is hash-based
+       is $grps[0], 'Other',   " - Other";
+       is $grps[1], 'Weather', " - Weather";
+}
+
+SKIP: {
+        skip "->retrieve not yet supported by CDBI compat", 1;
+       local $SIG{__WARN__} = sub { };
+       eval { DBIx::Class->retrieve(1) };
+       like $@, qr/Can't retrieve unless primary columns are defined/, "Need primary key for retrieve";
+}
+
+#-----------------------------------------------------------------------
+# Make sure that columns inherit properly
+#-----------------------------------------------------------------------
+package State;
+
+package A;
+@A::ISA = qw(DBIx::Class);
+__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";
+
diff --git a/t/02-Film.t b/t/02-Film.t
new file mode 100644 (file)
index 0000000..ea2b2d7
--- /dev/null
@@ -0,0 +1,370 @@
+use strict;
+use Test::More;
+$| = 1;
+
+BEGIN {
+       eval "use DBD::SQLite";
+       plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 93);
+}
+
+INIT {
+       use lib 't/testlib';
+       use Film;
+}
+
+ok(Film->can('db_Main'), 'set_db()');
+is(Film->__driver, "SQLite", "Driver set correctly");
+
+SKIP: {
+  skip "Bunch of slightly different error messages", 5;
+
+{
+       my $nul = eval { Film->retrieve() };
+       is $nul, undef, "Can't retrieve nothing";
+       like $@, qr/./, "retrieve needs parameters";    # TODO fix this...
+}
+
+{
+       eval { my $id = Film->id };
+       like $@, qr/class method/, "Can't get id with no object";
+}
+
+{
+       eval { my $id = Film->title };
+       like $@, qr/class method/, "Can't get title with no object";
+} 
+
+eval { my $duh = Film->create; };
+like $@, qr/create needs a hashref/, "create needs a hashref";
+
+} # End skip block
+
+ok +Film->create_test_film;
+
+my $btaste = Film->retrieve('Bad Taste');
+isa_ok $btaste, 'Film';
+is($btaste->Title,             'Bad Taste',     'Title() get');
+is($btaste->Director,          'Peter Jackson', 'Director() get');
+is($btaste->Rating,            'R',             'Rating() get');
+is($btaste->NumExplodingSheep, 1,               'NumExplodingSheep() get');
+
+{
+       my $bt2 = Film->find_or_create(Title => 'Bad Taste');
+       is $bt2->Director, $btaste->Director, "find_or_create";
+       my @bt = Film->search(Title => 'Bad Taste');
+       is @bt, 1, " doesn't create a new one";
+}
+
+ok my $gone = Film->find_or_create(
+       {
+               Title             => 'Gone With The Wind',
+               Director          => 'Bob Baggadonuts',
+               Rating            => 'PG',
+               NumExplodingSheep => 0
+       }
+       ),
+       "Add Gone With The Wind";
+isa_ok $gone, 'Film';
+ok $gone = Film->retrieve(Title => 'Gone With The Wind'),
+       "Fetch it back again";
+isa_ok $gone, 'Film';
+
+# Shocking new footage found reveals bizarre Scarlet/sheep scene!
+is($gone->NumExplodingSheep, 0, 'NumExplodingSheep() get again');
+$gone->NumExplodingSheep(5);
+is($gone->NumExplodingSheep, 5, 'NumExplodingSheep() set');
+is($gone->numexplodingsheep, 5, 'numexplodingsheep() set');
+
+is($gone->Rating, 'PG', 'Rating() get again');
+$gone->Rating('NC-17');
+is($gone->Rating, 'NC-17', 'Rating() set');
+$gone->update;
+
+{
+       my @films = eval { Film->retrieve_all };
+       is(@films, 2, "We have 2 films in total");
+}
+
+my $gone_copy = Film->retrieve('Gone With The Wind');
+ok($gone->NumExplodingSheep == 5, 'update()');
+ok($gone->Rating eq 'NC-17', 'update() again');
+
+# Grab the 'Bladerunner' entry.
+Film->create(
+       {
+               Title    => 'Bladerunner',
+               Director => 'Bob Ridley Scott',
+               Rating   => 'R'
+       }
+);
+
+my $blrunner = Film->retrieve('Bladerunner');
+is(ref $blrunner, 'Film', 'retrieve() again');
+is $blrunner->Title,    'Bladerunner',      "Correct title";
+is $blrunner->Director, 'Bob Ridley Scott', " and Director";
+is $blrunner->Rating,   'R',                " and Rating";
+is $blrunner->NumExplodingSheep, undef, " and sheep";
+
+# Make a copy of 'Bladerunner' and create an entry of the directors cut
+my $blrunner_dc = $blrunner->copy(
+       {
+               title  => "Bladerunner: Director's Cut",
+               rating => "15",
+       }
+);
+is(ref $blrunner_dc, 'Film', "copy() produces a film");
+is($blrunner_dc->Title,    "Bladerunner: Director's Cut", 'Title correct');
+is($blrunner_dc->Director, 'Bob Ridley Scott',            'Director correct');
+is($blrunner_dc->Rating,   '15',                          'Rating correct');
+is($blrunner_dc->NumExplodingSheep, undef, 'Sheep correct');
+
+# Set up own SQL:
+{
+       Film->add_constructor(title_asc  => "title LIKE ? ORDER BY title");
+       Film->add_constructor(title_desc => "title LIKE ? ORDER BY title DESC");
+
+       {
+               my @films = Film->title_asc("Bladerunner%");
+               is @films, 2, "We have 2 Bladerunners";
+               is $films[0]->Title, $blrunner->Title, "Ordered correctly";
+       }
+       {
+               my @films = Film->title_desc("Bladerunner%");
+               is @films, 2, "We have 2 Bladerunners";
+               is $films[0]->Title, $blrunner_dc->Title, "Ordered correctly";
+       }
+}
+
+# Multi-column search
+{
+       my @films = $blrunner->search_like(title => "Bladerunner%", rating => '15');
+       is @films, 1, "Only one Bladerunner is a 15";
+}
+
+# Inline SQL
+{
+       my @films = Film->retrieve_from_sql("numexplodingsheep > 0 ORDER BY title");
+       is @films, 2, "Inline SQL";
+       is $films[0]->id, $btaste->id, "Correct film";
+       is $films[1]->id, $gone->id,   "Correct film";
+}
+
+# Inline SQL removes WHERE
+{
+       my @films =
+               Film->retrieve_from_sql(" WHErE numexplodingsheep > 0 ORDER BY title");
+       is @films, 2, "Inline SQL";
+       is $films[0]->id, $btaste->id, "Correct film";
+       is $films[1]->id, $gone->id,   "Correct film";
+}
+
+eval {
+       my $ishtar = Film->create({ Title => 'Ishtar', Director => 'Elaine May' });
+       my $mandn =
+               Film->create({ Title => 'Mikey and Nicky', Director => 'Elaine May' });
+       my $new_leaf =
+               Film->create({ Title => 'A New Leaf', Director => 'Elaine May' });
+       is(Film->search(Director => 'Elaine May')->count,
+               3, "3 Films by Elaine May");
+       ok(Film->retrieve('Ishtar')->delete,
+               "Ishtar doesn't deserve an entry any more");
+       ok(!Film->retrieve('Ishtar'), 'Ishtar no longer there');
+       {
+               my $deprecated = 0;
+               local $SIG{__WARN__} = sub { $deprecated++ if $_[0] =~ /deprecated/ };
+               ok(
+                       Film->delete(Director => 'Elaine May'),
+                       "In fact, delete all films by Elaine May"
+               );
+               is(Film->search(Director => 'Elaine May')->count,
+                       0, "0 Films by Elaine May");
+               is $deprecated, 1, "Got a deprecated warning";
+       }
+};
+is $@, '', "No problems with deletes";
+
+# Find all films which have a rating of NC-17.
+my @films = Film->search('Rating', 'NC-17');
+is(scalar @films, 1, ' search returns one film');
+is($films[0]->id, $gone->id, ' ... the correct one');
+
+# Find all films which were directed by Bob
+@films = Film->search_like('Director', 'Bob %');
+is(scalar @films, 3, ' search_like returns 3 films');
+ok(
+       eq_array(
+               [ sort map { $_->id } @films ],
+               [ sort map { $_->id } $blrunner_dc, $gone, $blrunner ]
+       ),
+       'the correct ones'
+);
+
+# Find Ridley Scott films which don't have vomit
+@films =
+       Film->search(numExplodingSheep => undef, Director => 'Bob Ridley Scott');
+is(scalar @films, 2, ' search where attribute is null returns 2 films');
+ok(
+       eq_array(
+               [ sort map { $_->id } @films ],
+               [ sort map { $_->id } $blrunner_dc, $blrunner ]
+       ),
+       'the correct ones'
+);
+
+# Test that a disconnect doesnt harm anything.
+Film->db_Main->disconnect;
+@films = Film->search({ Rating => 'NC-17' });
+ok(@films == 1 && $films[0]->id eq $gone->id, 'auto reconnection');
+
+# Test discard_changes().
+my $orig_director = $btaste->Director;
+$btaste->Director('Lenny Bruce');
+is($btaste->Director, 'Lenny Bruce', 'set new Director');
+$btaste->discard_changes;
+is($btaste->Director, $orig_director, 'discard_changes()');
+
+{
+       Film->autoupdate(1);
+       my $btaste2 = Film->retrieve($btaste->id);
+       $btaste->NumExplodingSheep(18);
+       my @warnings;
+       local $SIG{__WARN__} = sub { push @warnings, @_; };
+       {
+
+               # unhook from live object cache, so next one is not from cache
+               $btaste2->remove_from_object_index;
+               my $btaste3 = Film->retrieve($btaste->id);
+               is $btaste3->NumExplodingSheep, 18, "Class based AutoCommit";
+               $btaste3->autoupdate(0);    # obj a/c should override class a/c
+               is @warnings, 0, "No warnings so far";
+               $btaste3->NumExplodingSheep(13);
+       }
+       is @warnings, 1, "DESTROY without update warns";
+       Film->autoupdate(0);
+}
+
+{                               # update unchanged object
+       my $film   = Film->retrieve($btaste->id);
+       my $retval = $film->update;
+       is $retval, -1, "Unchanged object";
+}
+
+{                               # update deleted object
+       my $rt = "Royal Tenenbaums";
+       my $ten = Film->create({ title => $rt, Rating => "R" });
+       $ten->rating(18);
+       Film->set_sql(drt => "DELETE FROM __TABLE__ WHERE title = ?");
+       Film->sql_drt->execute($rt);
+       my @films = Film->search({ title => $rt });
+       is @films, 0, "RT gone";
+       my $retval = eval { $ten->update };
+       like $@, qr/row not found/, "Update deleted object throws error";
+       $ten->discard_changes;
+}
+
+{
+       $btaste->autoupdate(1);
+       $btaste->NumExplodingSheep(32);
+       my $btaste2 = Film->retrieve($btaste->id);
+       is $btaste2->NumExplodingSheep, 32, "Object based AutoCommit";
+       $btaste->autoupdate(0);
+}
+
+# Primary key of 0
+{
+       my $zero = Film->create({ Title => 0, Rating => "U" });
+       ok defined $zero, "Create 0";
+       ok my $ret = Film->retrieve(0), "Retrieve 0";
+       is $ret->Title,  0,   "Title OK";
+       is $ret->Rating, "U", "Rating OK";
+}
+
+# Change after_update policy
+{
+       my $bt = Film->retrieve($btaste->id);
+       $bt->autoupdate(1);
+
+       $bt->rating("17");
+       ok !$bt->_attribute_exists('rating'), "changed column needs reloaded";
+       ok $bt->_attribute_exists('title'), "but we still have the title";
+
+       # Don't re-load
+       $bt->add_trigger(
+               after_update => sub {
+                       my ($self, %args) = @_;
+                       my $discard_columns = $args{discard_columns};
+                       @$discard_columns = qw/title/;
+               }
+       );
+       $bt->rating("19");
+       ok $bt->_attribute_exists('rating'), "changed column needs reloaded";
+       ok !$bt->_attribute_exists('title'), "but no longer have the title";
+}
+
+# Make sure that we can have other accessors. (Bugfix in 0.28)
+if (0) {
+       Film->mk_accessors(qw/temp1 temp2/);
+       my $blrunner = Film->retrieve('Bladerunner');
+       $blrunner->temp1("Foo");
+       $blrunner->NumExplodingSheep(2);
+       eval { $blrunner->update };
+       ok(!$@, "Other accessors");
+}
+
+# overloading
+{
+       is "$blrunner", "Bladerunner", "stringify";
+
+       ok(Film->columns(Stringify => 'rating'), "Can change stringify column");
+       is "$blrunner", "R", "And still stringifies correctly";
+
+       ok(
+               Film->columns(Stringify => qw/title rating/),
+               "Can have multiple stringify columns"
+       );
+       is "$blrunner", "Bladerunner/R", "And still stringifies correctly";
+
+       no warnings 'once';
+       local *Film::stringify_self = sub { join ":", $_[0]->title, $_[0]->rating };
+       is "$blrunner", "Bladerunner:R", "Provide stringify_self()";
+}
+
+{
+       {
+               ok my $byebye = DeletingFilm->create(
+                       {
+                               Title  => 'Goodbye Norma Jean',
+                               Rating => 'PG',
+                       }
+                       ),
+                       "Add a deleting Film";
+
+               isa_ok $byebye, 'DeletingFilm';
+               isa_ok $byebye, 'Film';
+               ok(Film->retrieve('Goodbye Norma Jean'), "Fetch it back again");
+       }
+       my $film;
+       eval { $film = Film->retrieve('Goodbye Norma Jean') };
+       ok !$film, "It destroys itself";
+}
+
+SKIP: {
+       skip "Scalar::Util::weaken not available", 3
+               #if !$Class::DBI::Weaken_Is_Available;
+
+       # my bad taste is your bad taste
+       my $btaste  = Film->retrieve('Bad Taste');
+       my $btaste2 = Film->retrieve('Bad Taste');
+       is Scalar::Util::refaddr($btaste), Scalar::Util::refaddr($btaste2),
+               "Retrieving twice gives ref to same object";
+
+       $btaste2->remove_from_object_index;
+       my $btaste3 = Film->retrieve('Bad Taste');
+       isnt Scalar::Util::refaddr($btaste2), Scalar::Util::refaddr($btaste3),
+               "Removing from object_index and retrieving again gives new object";
+
+       $btaste3->clear_object_index;
+       my $btaste4 = Film->retrieve('Bad Taste');
+       isnt Scalar::Util::refaddr($btaste2), Scalar::Util::refaddr($btaste4),
+               "Clearing cache and retrieving again gives new object";
+}
diff --git a/t/testlib/Actor.pm b/t/testlib/Actor.pm
new file mode 100644 (file)
index 0000000..837b095
--- /dev/null
@@ -0,0 +1,29 @@
+package Actor;
+
+BEGIN { unshift @INC, './t/testlib'; }
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Test::SQLite';
+
+__PACKAGE__->set_table('Actor');
+
+__PACKAGE__->columns(Primary => 'id');
+__PACKAGE__->columns(All     => qw/ Name Film Salary /);
+__PACKAGE__->columns(TEMP    => qw/ nonpersistent /);
+__PACKAGE__->add_constructor(salary_between => 'salary >= ? AND salary <= ?');
+
+sub mutator_name { "set_$_[1]" }
+
+sub create_sql {
+       return qq{
+               id     INTEGER PRIMARY KEY,
+               name   CHAR(40),
+               film   VARCHAR(255),   
+               salary INT
+       }
+}
+
+1;
+
diff --git a/t/testlib/Binary.pm b/t/testlib/Binary.pm
new file mode 100644 (file)
index 0000000..d29849f
--- /dev/null
@@ -0,0 +1,17 @@
+package Binary;
+
+BEGIN { unshift @INC, './t/testlib'; }
+
+use strict;
+use base 'PgBase';
+
+__PACKAGE__->table(cdbibintest => 'cdbibintest');
+__PACKAGE__->sequence('binseq');
+__PACKAGE__->columns(All => qw(id bin));
+
+# __PACKAGE__->data_type(bin => DBI::SQL_BINARY);
+
+sub schema { "id INTEGER, bin BYTEA" }
+
+1;
+
diff --git a/t/testlib/Blurb.pm b/t/testlib/Blurb.pm
new file mode 100644 (file)
index 0000000..8173cb8
--- /dev/null
@@ -0,0 +1,20 @@
+package Blurb;
+
+BEGIN { unshift @INC, './t/testlib'; }
+
+use strict;
+use base 'DBIx::Class::Test::SQLite';
+
+__PACKAGE__->set_table('Blurbs');
+__PACKAGE__->columns('Primary', 'Title');
+__PACKAGE__->columns('Blurb',   qw/ blurb/);
+
+sub create_sql {
+       return qq{
+                       title                   VARCHAR(255) PRIMARY KEY,
+                       blurb                   VARCHAR(255) NOT NULL
+  }
+}
+
+1;
+
diff --git a/t/testlib/CDBase.pm b/t/testlib/CDBase.pm
new file mode 100644 (file)
index 0000000..0a9ecc0
--- /dev/null
@@ -0,0 +1,14 @@
+package CDBase;
+
+use strict;
+use base qw(DBIx::Class);
+
+use File::Temp qw/tempfile/;
+my (undef, $DB) = tempfile();
+my @DSN = ("dbi:SQLite:dbname=$DB", '', '', { AutoCommit => 1 });
+
+END { unlink $DB if -e $DB }
+
+__PACKAGE__->connection(@DSN);
+
+1;
diff --git a/t/testlib/Director.pm b/t/testlib/Director.pm
new file mode 100644 (file)
index 0000000..aa13661
--- /dev/null
@@ -0,0 +1,20 @@
+package Director;
+
+BEGIN { unshift @INC, './t/testlib'; }
+
+use strict;
+use base 'DBIx::Class::Test::SQLite';
+
+__PACKAGE__->set_table('Directors');
+__PACKAGE__->columns('All' => qw/ Name Birthday IsInsane /);
+
+sub create_sql {
+       return qq{
+                       name                    VARCHAR(80),
+                       birthday                INTEGER,
+                       isinsane                INTEGER
+       };
+}
+
+1;
+
diff --git a/t/testlib/Film.pm b/t/testlib/Film.pm
new file mode 100644 (file)
index 0000000..6747656
--- /dev/null
@@ -0,0 +1,39 @@
+package Film;
+
+BEGIN { unshift @INC, './t/testlib'; }
+use base 'DBIx::Class::Test::SQLite';
+use strict;
+
+__PACKAGE__->set_table('Movies');
+__PACKAGE__->columns('Primary',   'Title');
+__PACKAGE__->columns('Essential', qw( Title ));
+__PACKAGE__->columns('Directors', qw( Director CoDirector ));
+__PACKAGE__->columns('Other',     qw( Rating NumExplodingSheep HasVomit ));
+
+sub create_sql {
+       return qq{
+               title                   VARCHAR(255),
+               director                VARCHAR(80),
+               codirector              VARCHAR(80),
+               rating                  CHAR(5),
+               numexplodingsheep       INTEGER,
+               hasvomit                CHAR(1)
+  }
+}
+
+sub create_test_film { 
+       return shift->create({
+               Title             => 'Bad Taste',
+               Director          => 'Peter Jackson',
+               Rating            => 'R',
+               NumExplodingSheep => 1,
+       });
+}
+
+package DeletingFilm;
+
+use base 'Film';
+sub DESTROY { shift->delete }
+
+1;
+
diff --git a/t/testlib/Lazy.pm b/t/testlib/Lazy.pm
new file mode 100644 (file)
index 0000000..74dc069
--- /dev/null
@@ -0,0 +1,26 @@
+package Lazy;
+
+BEGIN { unshift @INC, './t/testlib'; }
+use base 'DBIx::Class::Test::SQLite';
+use strict;
+
+__PACKAGE__->set_table("Lazy");
+__PACKAGE__->columns('Primary',   qw(this));
+__PACKAGE__->columns('Essential', qw(opop));
+__PACKAGE__->columns('things',    qw(this that));
+__PACKAGE__->columns('horizon',   qw(eep orp));
+__PACKAGE__->columns('vertical',  qw(oop opop));
+
+sub create_sql {
+       return qq{
+               this INTEGER,
+               that INTEGER,
+               eep  INTEGER,
+               orp  INTEGER,
+               oop  INTEGER,
+               opop INTEGER
+       };
+}
+
+1;
+
diff --git a/t/testlib/Log.pm b/t/testlib/Log.pm
new file mode 100644 (file)
index 0000000..af4c6f6
--- /dev/null
@@ -0,0 +1,35 @@
+package Log;
+
+BEGIN { unshift @INC, './t/testlib'; }
+use base 'MyBase';
+
+use strict;
+use Time::Piece::MySQL;
+use POSIX;
+
+__PACKAGE__->set_table();
+__PACKAGE__->columns(All => qw/id message datetime_stamp/);
+__PACKAGE__->has_a(
+       datetime_stamp => 'Time::Piece',
+       inflate        => 'from_mysql_datetime',
+       deflate        => 'mysql_datetime'
+);
+
+__PACKAGE__->add_trigger(before_create => \&set_dts);
+__PACKAGE__->add_trigger(before_update => \&set_dts);
+
+sub set_dts {
+       shift->datetime_stamp(
+               POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime(time)));
+}
+
+sub create_sql {
+       return qq{
+    id             INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
+    message        VARCHAR(255),
+    datetime_stamp DATETIME
+  };
+}
+
+1;
+
diff --git a/t/testlib/MyBase.pm b/t/testlib/MyBase.pm
new file mode 100644 (file)
index 0000000..4950087
--- /dev/null
@@ -0,0 +1,44 @@
+package MyBase;
+
+use strict;
+use base qw(DBIx::Class);
+
+use vars qw/$dbh/;
+
+my @connect = ("dbi:mysql:test", "", "");
+
+$dbh = DBI->connect(@connect) or die DBI->errstr;
+my @table;
+
+END { $dbh->do("DROP TABLE $_") foreach @table }
+
+__PACKAGE__->connection(@connect);
+
+sub set_table {
+       my $class = shift;
+       $class->table($class->create_test_table);
+}
+
+sub create_test_table {
+       my $self   = shift;
+       my $table  = $self->next_available_table;
+       my $create = sprintf "CREATE TABLE $table ( %s )", $self->create_sql;
+       push @table, $table;
+       $dbh->do($create);
+       return $table;
+}
+
+sub next_available_table {
+       my $self   = shift;
+       my @tables = sort @{
+               $dbh->selectcol_arrayref(
+                       qq{
+    SHOW TABLES
+  }
+               )
+               };
+       my $table = $tables[-1] || "aaa";
+       return "z$table";
+}
+
+1;
diff --git a/t/testlib/MyFilm.pm b/t/testlib/MyFilm.pm
new file mode 100644 (file)
index 0000000..676a4a9
--- /dev/null
@@ -0,0 +1,26 @@
+package MyFilm;
+
+BEGIN { unshift @INC, './t/testlib'; }
+use base 'MyBase';
+use MyStarLink;
+
+use strict;
+
+__PACKAGE__->set_table();
+__PACKAGE__->columns(All => qw/filmid title/);
+__PACKAGE__->has_many(_stars => 'MyStarLink');
+__PACKAGE__->columns(Stringify => 'title');
+
+sub _carp { }
+
+sub stars { map $_->star, shift->_stars }
+
+sub create_sql {
+       return qq{
+    filmid  TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+    title   VARCHAR(255)
+  };
+}
+
+1;
+
diff --git a/t/testlib/MyFoo.pm b/t/testlib/MyFoo.pm
new file mode 100644 (file)
index 0000000..4ed37d8
--- /dev/null
@@ -0,0 +1,27 @@
+package MyFoo;
+
+BEGIN { unshift @INC, './t/testlib'; }
+use base 'MyBase';
+
+use strict;
+
+__PACKAGE__->set_table();
+__PACKAGE__->columns(All => qw/myid name val tdate/);
+__PACKAGE__->has_a(
+       tdate   => 'Date::Simple',
+       inflate => sub { Date::Simple->new(shift) },
+       deflate => 'format',
+);
+__PACKAGE__->find_column('tdate')->placeholder("IF(1, CURDATE(), ?)");
+
+sub create_sql {
+       return qq{
+    myid mediumint not null auto_increment primary key,
+    name varchar(50) not null default '',
+    val  char(1) default 'A',
+    tdate date not null
+  };
+}
+
+1;
+
diff --git a/t/testlib/MyStar.pm b/t/testlib/MyStar.pm
new file mode 100644 (file)
index 0000000..e8e79b2
--- /dev/null
@@ -0,0 +1,22 @@
+package MyStar;
+
+BEGIN { unshift @INC, './t/testlib'; }
+use base 'MyBase';
+
+use strict;
+
+__PACKAGE__->set_table();
+__PACKAGE__->columns(All => qw/starid name/);
+__PACKAGE__->has_many(films => [ MyStarLink => 'film' ]);
+
+# sub films { map $_->film, shift->_films }
+
+sub create_sql {
+       return qq{
+               starid  TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+               name   VARCHAR(255)
+       };
+}
+
+1;
+
diff --git a/t/testlib/MyStarLink.pm b/t/testlib/MyStarLink.pm
new file mode 100644 (file)
index 0000000..fe31e25
--- /dev/null
@@ -0,0 +1,22 @@
+package MyStarLink;
+
+BEGIN { unshift @INC, './t/testlib'; }
+use base 'MyBase';
+
+use strict;
+
+__PACKAGE__->set_table();
+__PACKAGE__->columns(All => qw/linkid film star/);
+__PACKAGE__->has_a(film  => 'MyFilm');
+__PACKAGE__->has_a(star  => 'MyStar');
+
+sub create_sql {
+       return qq{
+    linkid  TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+    film    TINYINT NOT NULL,
+    star    TINYINT NOT NULL
+  };
+}
+
+1;
+
diff --git a/t/testlib/MyStarLinkMCPK.pm b/t/testlib/MyStarLinkMCPK.pm
new file mode 100644 (file)
index 0000000..f81292f
--- /dev/null
@@ -0,0 +1,29 @@
+package MyStarLinkMCPK;
+
+BEGIN { unshift @INC, './t/testlib'; }
+use base 'MyBase';
+
+use MyStar;
+use MyFilm;
+
+use strict;
+
+# This is a many-to-many mapping table that uses the two foreign keys
+# as its own primary key - there's no extra 'auto-inc' column here
+
+__PACKAGE__->set_table();
+__PACKAGE__->columns(Primary => qw/film star/);
+__PACKAGE__->columns(All     => qw/film star/);
+__PACKAGE__->has_a(film => 'MyFilm');
+__PACKAGE__->has_a(star => 'MyStar');
+
+sub create_sql {
+       return qq{
+    film    INTEGER NOT NULL,
+    star    INTEGER NOT NULL,
+    PRIMARY KEY (film, star)
+  };
+}
+
+1;
+
diff --git a/t/testlib/Order.pm b/t/testlib/Order.pm
new file mode 100644 (file)
index 0000000..a48a135
--- /dev/null
@@ -0,0 +1,21 @@
+package Order;
+
+BEGIN { unshift @INC, './t/testlib'; }
+
+use strict;
+use base 'DBIx::Class::Test::SQLite';
+
+__PACKAGE__->set_table('orders');
+__PACKAGE__->table_alias('orders');
+__PACKAGE__->columns(Primary => 'film');
+__PACKAGE__->columns(Others  => qw/orders/);
+
+sub create_sql {
+       return qq{
+               film     VARCHAR(255),
+               orders   INTEGER
+       };
+}
+
+1;
+
diff --git a/t/testlib/OtherFilm.pm b/t/testlib/OtherFilm.pm
new file mode 100644 (file)
index 0000000..2e78316
--- /dev/null
@@ -0,0 +1,20 @@
+package OtherFilm;
+
+use strict;
+use base 'Film';
+
+__PACKAGE__->set_table('Different_Film');
+
+sub create_sql {
+       return qq{
+               title                   VARCHAR(255),
+               director                VARCHAR(80),
+               codirector              VARCHAR(80),
+               rating                  CHAR(5),
+               numexplodingsheep       INTEGER,
+               hasvomit                CHAR(1)
+       };
+}
+
+1;
+
diff --git a/t/testlib/PgBase.pm b/t/testlib/PgBase.pm
new file mode 100644 (file)
index 0000000..c75773b
--- /dev/null
@@ -0,0 +1,22 @@
+package PgBase;
+
+use strict;
+use base 'DBIx::Class';
+
+my $db   = $ENV{DBD_PG_DBNAME} || 'template1';
+my $user = $ENV{DBD_PG_USER}   || 'postgres';
+my $pass = $ENV{DBD_PG_PASSWD} || '';
+
+__PACKAGE__->connection("dbi:Pg:dbname=$db", $user, $pass,
+       { AutoCommit => 1 });
+
+sub CONSTRUCT {
+       my $class = shift;
+       my ($table, $sequence) = ($class->table, $class->sequence || "");
+       my $schema = $class->schema;
+       $class->db_Main->do("CREATE TEMPORARY SEQUENCE $sequence") if $sequence;
+       $class->db_Main->do("CREATE TEMPORARY TABLE $table ( $schema )");
+}
+
+1;
+