sub new {
my ($it_class, $db_class, $sth, $args, $cols, $attrs) = @_;
- $sth->execute(@{$args || []}) unless $sth->{Active};
+ #use Data::Dumper; warn Dumper(@_);
+ $it_class = ref $it_class if ref $it_class;
+ unless ($sth) {
+ $sth = $db_class->_get_sth('select', $cols,
+ $db_class->_table_name, $attrs->{where});
+ }
my $new = {
class => $db_class,
sth => $sth,
cols => $cols,
args => $args,
+ pos => 0,
attrs => $attrs };
return bless ($new, $it_class);
}
+sub slice {
+ my ($self, $min, $max) = @_;
+ my $attrs = { %{ $self->{attrs} || {} } };
+ $self->{class}->throw("Can't slice without where") unless $attrs->{where};
+ $attrs->{offset} = $min;
+ $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
+ my $slice = $self->new($self->{class}, undef, $self->{args},
+ $self->{cols}, $attrs);
+ return (wantarray ? $slice->all : $slice);
+}
+
sub next {
my ($self) = @_;
+ return if $self->{attrs}{rows}
+ && $self->{pos} >= $self->{attrs}{rows}; # + $self->{attrs}{offset});
+ unless ($self->{live_sth}) {
+ $self->{sth}->execute(@{$self->{args} || []});
+ if (my $offset = $self->{attrs}{offset}) {
+ $self->{sth}->fetchrow_array for 1 .. $offset;
+ }
+ $self->{live_sth} = 1;
+ }
my @row = $self->{sth}->fetchrow_array;
return unless @row;
- #unless (@row) { $self->{sth}->finish; return; }
+ $self->{pos}++;
return $self->{class}->_row_to_object($self->{cols}, \@row);
}
sub count {
my ($self) = @_;
+ return $self->{attrs}{rows} if $self->{attrs}{rows};
if (my $cond = $self->{attrs}->{where}) {
my $class = $self->{class};
my $sth = $class->_get_sth( 'select', [ 'COUNT(*)' ],
}
sub reset {
- $_[0]->{sth}->finish if $_[0]->{sth}->{Active};
- $_[0]->{sth}->execute(@{$_[0]->{args} || []});
- return $_[0];
+ my ($self) = @_;
+ $self->{sth}->finish if $self->{sth}->{Active};
+ $self->{pos} = 0;
+ $self->{live_sth} = 0;
+ return $self;
}
sub first {
return $_[0]->reset->next;
}
+sub delete_all {
+ my ($self) = @_;
+ $_->delete for $self->all;
+ return 1;
+}
+
1;
--- /dev/null
+use strict;
+use Test::More;
+
+BEGIN {
+ eval "use DBD::SQLite";
+ plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 23);
+}
+
+use lib 't/testlib';
+use Film;
+
+sub valid_rating {
+ my $value = shift;
+ my $ok = grep $value eq $_, qw/U Uc PG 12 15 18/;
+ return $ok;
+}
+
+Film->add_constraint('valid rating', Rating => \&valid_rating);
+
+my %info = (
+ Title => 'La Double Vie De Veronique',
+ Director => 'Kryzstof Kieslowski',
+ Rating => '18',
+);
+
+{
+ local $info{Title} = "nonsense";
+ local $info{Rating} = 19;
+ eval { Film->create({%info}) };
+ ok $@, $@;
+ ok !Film->retrieve($info{Title}), "No film created";
+ is(Film->retrieve_all, 0, "So no films");
+}
+
+ok(my $ver = Film->create({%info}), "Can create with valid rating");
+is $ver->Rating, 18, "Rating 18";
+
+ok $ver->Rating(12), "Change to 12";
+ok $ver->update, "And update";
+is $ver->Rating, 12, "Rating now 12";
+
+eval {
+ $ver->Rating(13);
+ $ver->update;
+};
+ok $@, $@;
+is $ver->Rating, 12, "Rating still 12";
+ok $ver->delete, "Delete";
+
+# this threw an infinite loop in old versions
+Film->add_constraint('valid director', Director => sub { 1 });
+my $fred = Film->create({ Rating => '12' });
+
+# this test is a bit problematical because we don't supply a primary key
+# to the create() and the table doesn't use auto_increment or a sequence.
+ok $fred, "Got fred";
+
+{
+ ok +Film->constrain_column(rating => [qw/U PG 12 15 19/]),
+ "constraint_column";
+ my $narrower = eval { Film->create({ Rating => 'Uc' }) };
+ like $@, qr/fails.*constraint/, "Fails listref constraint";
+ my $ok = eval { Film->create({ Rating => 'U' }) };
+ is $@, '', "Can create with rating U";
+ SKIP: {
+ skip "No column objects", 2;
+ ok +Film->find_column('rating')->is_constrained, "Rating is constrained";
+ ok +Film->find_column('director')->is_constrained, "Director is not";
+ }
+}
+
+{
+ ok +Film->constrain_column(title => qr/The/), "constraint_column";
+ my $inferno = eval { Film->create({ Title => 'Towering Infero' }) };
+ like $@, qr/fails.*constraint/, "Can't create towering inferno";
+ my $the_inferno = eval { Film->create({ Title => 'The Towering Infero' }) };
+ is $@, '', "But can create THE towering inferno";
+}
+
+{
+
+ sub Film::_constrain_by_untaint {
+ my ($class, $col, $string, $type) = @_;
+ $class->add_constraint(
+ untaint => $col => sub {
+ my ($value, $self, $column_name, $changing) = @_;
+ $value eq "today" ? $changing->{$column_name} = "2001-03-03" : 0;
+ }
+ );
+ }
+ eval { Film->constrain_column(codirector => Untaint => 'date') };
+ is $@, '', 'Can constrain with untaint';
+ my $freeaa =
+ eval { Film->create({ title => "The Freaa", codirector => 'today' }) };
+ is $@, '', "Can create codirector";
+ is $freeaa->codirector, '2001-03-03', "Set the codirector";
+}
+
+__DATA__
+
+use CGI::Untaint;
+
+sub _constrain_by_untaint {
+ my ($class, $col, $string, $type) = @_;
+ $class->add_constraint(untaint => $col => sub {
+ my ($value, $self, $column_name, $changing) = @_;
+ my $h = CGI::Untaint->new({ %$changing });
+ return unless my $val = $h->extract("-as_$type" => $column_name);
+ $changing->{$column_name} = $val;
+ return 1;
+ });
+}
+