[Jifty-commit] r5052 - in Jifty-DBI/branches/tisql: lib/Jifty/DBI t
Jifty commits
jifty-commit at lists.jifty.org
Tue Feb 5 21:19:48 EST 2008
Author: ruz
Date: Tue Feb 5 21:19:48 2008
New Revision: 5052
Added:
Jifty-DBI/branches/tisql/t/20overload.t
Modified:
Jifty-DBI/branches/tisql/ (props changed)
Jifty-DBI/branches/tisql/MANIFEST
Jifty-DBI/branches/tisql/lib/Jifty/DBI/Collection.pm
Log:
r4985 at cubic-pc (orig r4984): audreyt | 2008-02-02 17:17:40 +0300
* Add <$collection> and @$collection syntax sugar that simply
calls $collection->next and $collection->items_array_ref
behind the scenes.
Modified: Jifty-DBI/branches/tisql/MANIFEST
==============================================================================
--- Jifty-DBI/branches/tisql/MANIFEST (original)
+++ Jifty-DBI/branches/tisql/MANIFEST Tue Feb 5 21:19:48 2008
@@ -28,6 +28,7 @@
lib/Jifty/DBI/Filter/base64.pm
lib/Jifty/DBI/Filter/Date.pm
lib/Jifty/DBI/Filter/DateTime.pm
+lib/Jifty/DBI/Filter/Duration.pm
lib/Jifty/DBI/Filter/SaltHash.pm
lib/Jifty/DBI/Filter/Storable.pm
lib/Jifty/DBI/Filter/Time.pm
@@ -69,6 +70,7 @@
t/04memcached.t
t/06filter.t
t/06filter_datetime.t
+t/06filter_duration.t
t/06filter_storable.t
t/06filter_truncate.t
t/06filter_utf8.t
@@ -83,6 +85,7 @@
t/17virtualtypes.t
t/18triggers.t
t/19reference.t
+t/20overload.t
t/case_sensitivity.t
t/metadata.t
t/pod-coverage.t
Modified: Jifty-DBI/branches/tisql/lib/Jifty/DBI/Collection.pm
==============================================================================
--- Jifty-DBI/branches/tisql/lib/Jifty/DBI/Collection.pm (original)
+++ Jifty-DBI/branches/tisql/lib/Jifty/DBI/Collection.pm Tue Feb 5 21:19:48 2008
@@ -4,6 +4,11 @@
use strict;
use Scalar::Defer qw/lazy/;
use Scalar::Util qw/weaken/;
+use overload (
+ '@{}' => \&items_array_ref,
+ '<>' => \&next,
+ fallback => 1
+);
=head1 NAME
@@ -866,6 +871,13 @@
L</next> will start over with the first item retrieved from the
database.
+You may also call this method via the built-in iterator syntax.
+The two lines below are equivalent:
+
+ while ($_ = $collection->next) { ... }
+
+ while (<$collection>) { ... }
+
=cut
sub next {
@@ -959,6 +971,13 @@
Return a reference to an array containing all objects found by this
search.
+You may also call this method via the built-in array dereference syntax.
+The two lines below are equivalent:
+
+ for (@{$collection->items_array_ref}) { ... }
+
+ for (@$collection) { ... }
+
=cut
sub items_array_ref {
Added: Jifty-DBI/branches/tisql/t/20overload.t
==============================================================================
--- (empty file)
+++ Jifty-DBI/branches/tisql/t/20overload.t Tue Feb 5 21:19:48 2008
@@ -0,0 +1,406 @@
+#!/usr/bin/env perl -w
+
+
+use strict;
+use warnings;
+use File::Spec;
+use Test::More;
+BEGIN { require "t/utils.pl" }
+our (@available_drivers);
+
+use constant TESTS_PER_DRIVER => 109;
+
+my $total = scalar(@available_drivers) * TESTS_PER_DRIVER;
+plan tests => $total;
+
+foreach my $d ( @available_drivers ) {
+SKIP: {
+ unless( has_schema( 'TestApp', $d ) ) {
+ skip "No schema for '$d' driver", TESTS_PER_DRIVER;
+ }
+ unless( should_test( $d ) ) {
+ skip "ENV is not defined for driver '$d'", TESTS_PER_DRIVER;
+ }
+
+ my $handle = get_handle( $d );
+ connect_handle( $handle );
+ isa_ok($handle->dbh, 'DBI::db');
+
+ {my $ret = init_schema( 'TestApp', $handle );
+ isa_ok($ret,'DBI::st', "Inserted the schema. got a statement handle back" );}
+
+ my $count_all = init_data( 'TestApp::User', $handle );
+ ok( $count_all, "init users data" );
+
+ my $users_obj = TestApp::UserCollection->new( handle => $handle );
+ isa_ok( $users_obj, 'Jifty::DBI::Collection' );
+ is( $users_obj->_handle, $handle, "same handle as we used in constructor");
+
+# check that new object returns 0 records in any case
+ is( $users_obj->_record_count, 0, '_record_count returns 0 on not limited obj' );
+ is( $users_obj->count, 0, 'count returns 0 on not limited obj' );
+ is( $users_obj->is_last, undef, 'is_last returns undef on not limited obj after count' );
+ is( $users_obj->first, undef, 'first returns undef on not limited obj' );
+ is( $users_obj->is_last, undef, 'is_last returns undef on not limited obj after first' );
+ is( $users_obj->last, undef, 'last returns undef on not limited obj' );
+ is( $users_obj->is_last, undef, 'is_last returns undef on not limited obj after last' );
+ $users_obj->goto_first_item;
+ is( $users_obj->peek, undef, 'peek returns undef on not limited obj' );
+ is( <$users_obj>, undef, 'next returns undef on not limited obj' );
+ is( $users_obj->is_last, undef, 'is_last returns undef on not limited obj after next' );
+ # XXX TODO FIXME: may be this methods should be implemented
+ # $users_obj->goto_last_item;
+ # is( $users_obj->prev, undef, 'prev returns undef on not limited obj' );
+ my $items_ref = \@$users_obj;
+ isa_ok( $items_ref, 'ARRAY', 'items_array_ref always returns array reference' );
+ is_deeply( $items_ref, [], 'items_array_ref returns [] on not limited obj' );
+
+# unlimit new object and check
+ $users_obj->unlimit;
+ is( $users_obj->count, $count_all, 'count returns same number of records as was inserted' );
+ isa_ok( $users_obj->first, 'Jifty::DBI::Record', 'first returns record object' );
+ isa_ok( $users_obj->last, 'Jifty::DBI::Record', 'last returns record object' );
+ $users_obj->goto_first_item;
+ isa_ok( $users_obj->peek, 'Jifty::DBI::Record', 'peek returns record object' );
+ isa_ok( <$users_obj>, 'Jifty::DBI::Record', 'next returns record object' );
+ $items_ref = \@$users_obj;
+ isa_ok( $items_ref, 'ARRAY', 'items_array_ref always returns array reference' );
+ is( scalar @{$items_ref}, $count_all, 'items_array_ref returns same number of records as was inserted' );
+ $users_obj->redo_search;
+ $items_ref = \@$users_obj;
+ isa_ok( $items_ref, 'ARRAY', 'items_array_ref always returns array reference' );
+ is( scalar @{$items_ref}, $count_all, 'items_array_ref returns same number of records as was inserted' );
+
+# try to use $users_obj for all tests, after each call to clean_slate it should look like new obj.
+# and test $obj->new syntax
+ my $clean_obj = $users_obj->new( handle => $handle );
+ isa_ok( $clean_obj, 'Jifty::DBI::Collection' );
+
+# basic limits
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'login', value => 'obra' );
+ is( $users_obj->count, 1, 'found one user with login obra' );
+ TODO: {
+ local $TODO = 'require discussion';
+ is( $users_obj->is_last, undef, 'is_last returns undef before we fetch any record' );
+ }
+ my $first_rec = $users_obj->first;
+ isa_ok( $first_rec, 'Jifty::DBI::Record', 'First returns record object' );
+ is( $users_obj->is_last, 1, '1 record in the collection then first rec is last');
+ is( $first_rec->login, 'obra', 'login is correct' );
+ my $last_rec = $users_obj->last;
+ is( $last_rec, $first_rec, 'last returns same object as first' );
+ is( $users_obj->is_last, 1, 'is_last always returns 1 after last call');
+ $users_obj->goto_first_item;
+ my $peek_rec = $users_obj->peek;
+ my $next_rec = <$users_obj>;
+ is( $next_rec, $peek_rec, 'peek returns same object as next' );
+ is( $next_rec, $first_rec, 'next returns same object as first' );
+ is( $users_obj->is_last, 1, 'is_last returns 1 after fetch first record with next method');
+ is( $users_obj->peek, undef, 'only one record in the collection' );
+ is( <$users_obj>, undef, 'only one record in the collection' );
+ TODO: {
+ local $TODO = 'require discussion';
+ is( $users_obj->is_last, undef, 'next returns undef, is_last returns undef too');
+ }
+ $items_ref = \@$users_obj;
+ isa_ok( $items_ref, 'ARRAY', 'items_array_ref always returns array reference' );
+ is( scalar @{$items_ref}, 1, 'items_array_ref has only 1 record' );
+
+# similar basic limit, but with different operators and less first/next/last tests
+ # LIKE
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', operator => 'MATCHES', value => 'Glass' );
+ is( $users_obj->count, 1, "found one user with 'Glass' in the name" );
+ $first_rec = $users_obj->first;
+ isa_ok( $first_rec, 'Jifty::DBI::Record', 'First returns record object' );
+ is( $first_rec->login, 'glasser', 'login is correct' );
+
+ # LIKE with wildcard
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', operator => 'MATCHES', value => 'G_ass' );
+ is( $users_obj->count, 1, "found one user with 'Glass' in the name" );
+ $first_rec = $users_obj->first;
+ isa_ok( $first_rec, 'Jifty::DBI::Record', 'First returns record object' );
+ is( $first_rec->login, 'glasser', 'login is correct' );
+
+ # LIKE with escaped wildcard
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ # XXX: don't use backslashes; Pg (only Pg?) requires special
+ # treatment like "LIKE E'%g\\_ass%'" for that case,
+ # which is not supported yet (but this should be fixed)
+ $users_obj->limit( column => 'name', operator => 'MATCHES', value => 'G at _ass', escape => '@' );
+ is( $users_obj->count, 0, "should not find users with 'Glass' in the name" );
+
+ # LIKE with wildcard
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', operator => 'MATCHES', value => 'Glass%' );
+ is( $users_obj->count, 1, "found one user with 'Glass' in the name" );
+ $first_rec = $users_obj->first;
+ isa_ok( $first_rec, 'Jifty::DBI::Record', 'First returns record object' );
+ is( $first_rec->login, 'glasser', 'login is correct' );
+
+ # MATCHES with escaped wildcard
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ # XXX: don't use backslashes; reason above
+ $users_obj->limit( column => 'name', operator => 'MATCHES', value => 'Glass@%', escape => '@' );
+ is( $users_obj->count, 0, "should not find users with 'Glass' in the name" );
+
+ # STARTSWITH
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', operator => 'STARTSWITH', value => 'Ruslan' );
+ is( $users_obj->count, 1, "found one user who name starts with 'Ruslan'" );
+ $first_rec = $users_obj->first;
+ isa_ok( $first_rec, 'Jifty::DBI::Record', 'First returns record object' );
+ is( $first_rec->login, 'cubic', 'login is correct' );
+
+ # ENDSWITH
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', operator => 'ENDSWITH', value => 'Tang' );
+ is( $users_obj->count, 1, "found one user who name ends with 'Tang'" );
+ $first_rec = $users_obj->first;
+ isa_ok( $first_rec, 'Jifty::DBI::Record', 'First returns record object' );
+ is( $first_rec->login, 'audreyt', 'login is correct' );
+
+ # IN
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'login', operator => 'IN',
+ value => ['cubic', 'obra', 'glasser', 'audreyt'] );
+ is( $users_obj->count, 4, "found 4 user ids" );
+ my %logins = (cubic => 1, obra => 1, glasser => 1, audreyt => 1);
+ while ( my $user = <$users_obj> ) {
+ is ( defined $logins{$user->login}, 1, 'Found login' );
+ delete $logins{$user->login};
+ }
+ is ( scalar( keys( %logins ) ), 0, 'All logins found' );
+
+ # IS NULL
+ # XXX TODO FIXME: column => undef should be handled as NULL
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'phone', operator => 'IS', value => 'NULL' );
+ is( $users_obj->count, 2, "found 2 users who has unknown phone number" );
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'address', operator => 'IS', value => 'NULL' );
+ is( $users_obj->count, 0, "found 0 users who has unknown address" );
+
+ # IS NOT NULL
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'phone', operator => 'IS NOT', value => 'NULL', quotevalue => 0 );
+ is( $users_obj->count, $count_all - 2, "found users who have phone number filled" );
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'address', operator => 'IS NOT', value => 'NULL', quotevalue => 0 );
+ is( $users_obj->count, $count_all, "found users who have address filled" );
+
+ # CASE SENSITIVITY, default is limits are not case sensitive
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', value => 'Jesse Vincent' );
+ is( $users_obj->count, 1, "case insensitive, matching case, should find one row");
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', value => 'jesse vincent' );
+ is( $users_obj->count, 1, "case insensitive, non-matched case, should find one row");
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', value => ['Jesse Vincent', 'Audrey Tang'], operator => 'IN');
+ is( $users_obj->count, 2, "case insensitive, matching case, should find two rows");
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', value => ['jesse vincent', 'audrey tang'], operator => 'IN');
+ is( $users_obj->count, 2, "case insensitive, non-matched case, should find two rows");
+
+ # CASE SENSITIVITY, testing with case_sensitive => 1
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', value => 'Jesse Vincent', case_sensitive => 1 );
+ is( $users_obj->count, 1, "case sensitive search, should find one row");
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', value => 'jesse vincent', case_sensitive => 1 );
+ TODO: {
+ local $TODO = "MySQL still needs case sensitive fixes" if ( $d eq 'mysql' || $d eq 'mysqlPP' );
+ is( $users_obj->count, 0, "case sensitive search, should find zero rows");
+ }
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', value => ['Jesse Vincent', 'Audrey Tang'], operator => 'IN',
+ case_sensitive => 1 );
+ is( $users_obj->count, 2, "case sensitive search, should find two rows");
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'name', value => ['jesse vincent', 'audrey tang'], operator => 'IN',
+ case_sensitive => 1 );
+ TODO: {
+ local $TODO = "MySQL still needs case sensitive fixes" if ( $d eq 'mysql' || $d eq 'mysqlPP' );
+ is( $users_obj->count, 0, "case sensitive search, should find zero rows");
+ }
+
+ # ORDER BY / GROUP BY
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->unlimit;
+ $users_obj->group_by(column => 'login');
+ $users_obj->order_by(column => 'login', order => 'desc');
+ $users_obj->column(column => 'login');
+ is( $users_obj->count, $count_all, "group by / order by finds right amount");
+ $first_rec = $users_obj->first;
+ isa_ok( $first_rec, 'Jifty::DBI::Record', 'First returns record object' );
+ is( $first_rec->login, 'obra', 'login is correct' );
+
+ $users_obj->clean_slate;
+ TODO: {
+ local $TODO = 'we leave order_by after clean slate, fixing this results in many RT failures';
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj = TestApp::UserCollection->new( handle => $handle );
+ }
+
+# Let's play a little with 'entry_aggregator'
+ # EA defaults to OR for the same field
+ $users_obj->limit( column => 'phone', operator => 'IS', value => 'NULL', quote_value => 0 );
+ $users_obj->limit( column => 'phone', operator => 'LIKE', value => '%X%' );
+ is( $users_obj->count, 4, "found users who has no phone or it has X char" );
+
+ # set AND for the same field
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'Login', operator => 'NOT LIKE', value => '%c%' );
+ $users_obj->limit(
+ entry_aggregator => 'AND', column => 'Login', operator => 'LIKE', value => '%u%'
+ );
+ is( $users_obj->count, 1, "found users who has no phone or it has X char" );
+
+ # default is AND for different fields
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+ $users_obj->limit( column => 'phone', operator => 'IS', value => 'NULL', quote_value => 0 );
+ $users_obj->limit( column => 'login', operator => 'LIKE', value => '%r%' );
+ is( $users_obj->count, 2, "found users who has no phone number or login has 'r' char" );
+
+ $users_obj->clean_slate;
+ is_deeply( $users_obj, $clean_obj, 'after clean_slate looks like new object');
+
+ cleanup_schema( 'TestApp', $handle );
+ disconnect_handle( $handle );
+}} # SKIP, foreach blocks
+
+1;
+
+package TestApp;
+
+sub schema_mysql {
+<<EOF;
+CREATE TEMPORARY table users (
+ id integer AUTO_INCREMENT,
+ login varchar(18) NOT NULL,
+ name varchar(36),
+ phone varchar(18),
+ address varchar(18),
+ PRIMARY KEY (id))
+EOF
+
+}
+
+sub schema_pg {
+<<EOF;
+CREATE TEMPORARY table users (
+ id serial PRIMARY KEY,
+ login varchar(18) NOT NULL,
+ name varchar(36),
+ phone varchar(18),
+ address varchar(18)
+)
+EOF
+
+}
+
+sub schema_sqlite {
+
+<<EOF;
+CREATE table users (
+ id integer primary key,
+ login varchar(18) NOT NULL,
+ name varchar(36),
+ phone varchar(18),
+ address varchar(18))
+EOF
+
+}
+
+sub schema_oracle { [
+ "CREATE SEQUENCE Users_seq",
+ "CREATE TABLE users (
+ id integer CONSTRAINT users_key PRIMARY KEY,
+ Login varchar(18) NOT NULL,
+ name varchar(36),
+ phone varchar(18)
+ )",
+] }
+
+sub cleanup_schema_oracle { [
+ "DROP SEQUENCE users_seq",
+ "DROP TABLE users",
+] }
+
+
+1;
+
+package TestApp::User;
+
+use base qw/Jifty::DBI::Record/;
+
+sub _init {
+ my $self = shift;
+ $self->table('users');
+ $self->SUPER::_init(@_);
+}
+
+sub init_data {
+ return (
+ [ 'login', 'name', 'phone', 'address' ],
+ [ 'cubic', 'Ruslan U. Zakirov', '+7-903-264-XX-XX', undef ],
+ [ 'obra', 'Jesse Vincent', undef, undef ],
+ [ 'glasser', 'David Glasser', undef, 'somewhere' ],
+ [ 'audreyt', 'Audrey Tang', '+X-XXX-XXX-XX-XX', 'someplace' ],
+ );
+}
+
+1;
+
+BEGIN {
+ use Jifty::DBI::Schema;
+ use Jifty::DBI::Record schema {
+ column login => type is 'varchar(18)';
+ column name => type is 'varchar(36)';
+ column phone => type is 'varchar(18)', default is undef;
+ column address => type is 'varchar(18)', default is '';
+ }
+}
+
+1;
+
+package TestApp::UserCollection;
+
+# use TestApp::User;
+use base qw/Jifty::DBI::Collection/;
+
+sub _init {
+ my $self = shift;
+ $self->SUPER::_init(@_);
+ $self->table('users');
+}
+
+1;
+
More information about the Jifty-commit
mailing list