1package Class::DBI::Iterator;
2
3=head1 NAME
4
5Class::DBI::Iterator - Iterate over Class::DBI search results
6
7=head1 SYNOPSIS
8
9	my $it = My::Class->search(foo => 'bar');
10
11	my $results = $it->count;
12
13	my $first_result = $it->first;
14	while ($it->next) { ... }
15
16	my @slice = $it->slice(10,19);
17	my $slice = $it->slice(10,19);
18
19	$it->reset;
20
21	$it->delete_all;
22
23=head1 DESCRIPTION
24
25Any Class::DBI search (including a has_many method) which returns multiple
26objects can be made to return an iterator instead simply by executing
27the search in scalar context.
28
29Then, rather than having to fetch all the results at the same time, you
30can fetch them one at a time, potentially saving a considerable amount
31of processing time and memory.
32
33=head1 CAVEAT
34
35Note that there is no provision for the data changing (or even being
36deleted) in the database inbetween performing the search and retrieving
37the next result.
38
39=cut
40
41use strict;
42use overload
43	'0+'     => 'count',
44	fallback => 1;
45
46sub new {
47	my ($me, $them, $data, @mapper) = @_;
48	bless {
49		_class  => $them,
50		_data   => $data,
51		_mapper => [@mapper],
52		_place  => 0,
53		},
54		ref $me || $me;
55}
56
57sub set_mapping_method {
58	my ($self, @mapper) = @_;
59	$self->{_mapper} = [@mapper];
60	$self;
61}
62
63sub class  { shift->{_class} }
64sub data   { @{ shift->{_data} } }
65sub mapper { @{ shift->{_mapper} } }
66
67sub count {
68	my $self = shift;
69	$self->{_count} ||= scalar $self->data;
70}
71
72sub next {
73	my $self = shift;
74	my $use  = $self->{_data}->[ $self->{_place}++ ] or return;
75	my @obj  = ($self->class->construct($use));
76	foreach my $meth ($self->mapper) {
77		@obj = map $_->$meth(), @obj;
78	}
79	warn "Discarding extra inflated objects" if @obj > 1;
80	return $obj[0];
81}
82
83sub first {
84	my $self = shift;
85	$self->reset;
86	return $self->next;
87}
88
89sub slice {
90	my ($self, $start, $end) = @_;
91	$end ||= $start;
92	$self->{_place} = $start;
93	my @return;
94	while ($self->{_place} <= $end) {
95		push @return, $self->next || last;
96	}
97	return @return if wantarray;
98
99	my $slice = $self->new($self->class, \@return, $self->mapper,);
100	return $slice;
101}
102
103sub delete_all {
104	my $self = shift;
105	my $count = $self->count or return;
106	$self->first->delete;    # to reset counter
107	while (my $obj = $self->next) {
108		$obj->delete;
109	}
110	$self->{_data} = [];
111	1;
112}
113
114sub reset { shift->{_place} = 0 }
115
1161;
117