package Class::DBI::Pager;

use strict;
use vars qw($VERSION $AUTOLOAD);
$VERSION = 0.08;

use Class::DBI 0.90;
use Data::Page;

sub import {
    my $class = shift;
    my $pkg   = caller(0);
    no strict 'refs';
    *{"$pkg\::pager"} = \&_pager;
}

sub _croak { require Carp; Carp::croak(@_); }

sub _pager {
    my($pkg, $entry, $curr) = @_;
    bless {
	pkg   => $pkg,
	entry => $entry,
	curr  => $curr,
	pager => undef,
    }, __PACKAGE__;
}

BEGIN {
    my @methods = qw(total_entries entries_per_page current_page entries_on_this_page
		     first_page last_page first last previous_page next_page pages_in_navigation);
    for my $method (@methods) {
	no strict 'refs';
	*$method = sub {
	    my $self = shift;
	    $self->{pager} or _croak("Can't call pager methods without searching");
	    $self->{pager}->$method(@_);
	};
    }
}

sub DESTROY { }

sub AUTOLOAD {
    my $self = shift;
    (my $method = $AUTOLOAD) =~ s/.*://;
    if (ref($self) && $self->{pkg}->can($method)) {
	my $iter  = $self->{pkg}->$method(@_);
	UNIVERSAL::isa($iter, 'Class::DBI::Iterator')
	    or _croak("$method doesn't return Class::DBI::Itertor");
	my $pager = $self->{pager} = Data::Page->new(
	    $iter->count, $self->{entry}, $self->{curr},
	);
	return $iter->slice($pager->first-1, $pager->last-1);
    }
    else {
	_croak(qq(Can't locate object method "$method" via package ) . ref($self) || $self);
    }
}

1;
__END__

=head1 NAME

Class::DBI::Pager - Pager utility for Class::DBI

=head1 SYNOPSIS

  package CD;
  use base qw(Class::DBI);
  __PACKAGE__->set_db(...);

  use Class::DBI::Pager;	# just use it

  # then, in client code!
  package main;

  use CD;
  my $pager = CD->pager(20, 1);     # ($items_per_page, $current_page)
  my @disks = $pager->retrieve_all;

=head1 DESCRIPTION

Class::DBI::Pager is a plugin for Class::DBI, which glues Data::Page
with Class::DBI. This module reduces your work a lot, for example when
you have to do something like:

  * retrieve objects from a database
  * display objects with 20 items per page

In addition, your work will be reduced more, when you use
Template-Toolkit as your templating engine. See L</"EXAMPLE"> for
details.

=head1 EXAMPLE

  # Controller: (MVC's C)
  my $query    = CGI->new;
  my $template = Template->new;

  my $pager    = Film->pager(20, $query->param('page') || 1);
  my $movies   = $pager->retrieve_all;
  $template->process($input, {
      movies => $movies,
      pager  => $pager,
  });

  # View: (MVC's V)
  Matched [% pager.total_entries %] items.

  [% WHILE (movie = movies.next) %]
  Title: [% movie.title | html %]
  [% END %]

  ### navigation like: [1] [2] [3]
  [% FOREACH num = [pager.first_page .. pager.last_page] %]
  [% IF num == pager.current_page %][[% num %]]
  [% ELSE %]<a href="display?page=[% num %]">[[% num %]]</a>[% END %]
  [% END %]

  ### navigation like: prev 20 | next 20
  [% IF pager.previous_page %]
  <a href="display?page=[% pager.previous_page %]">
  prev [% pager.entries_per_page %]</a> |
  [% END %]
  [% IF pager.next_page %]
  <a href="display?page=[% pager.next_page %]">
  next [% pager.entries_per_page %]</a>
  [% END %]

=head1 NOTE / TODO

This modules internally retrieves itertors, then creates C<Data::Page>
object for paging utility. Using SQL clauses C<LIMIT> and/or C<OFFSET>
with C<DBIx::Pager> might be more memory efficient.

=head1 AUTHOR

Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>

Original idea by Tomohiro Ikebe E<lt>ikebe@cpan.orgE<gt>

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 SEE ALSO

L<Class::DBI>, L<Data::Page>

=cut
