pagenationを実現するモジュールあれこれ

俺々ライブラリ・俺々フレームワーク生活からの脱却を目指すべく、今後即戦力になる武器を少しずつ模索していたりする。pagenationも自作のページャーを利用していたのだが、改めて探してみると似たようなモジュールが色々と見つかったので、それぞれの特徴を大雑把に比較してみた。なお、以下で $dumper は Dumpvalue.pm のインスタンス

HTML::Pager

my $pager = HTML::Pager->new(
    query => CGI->new,
    get_data_callback => sub {
        my ($offset, $rows) = @_;
        return [ $model->get_data($offset, $rows) ];
    },
    rows => 100,
    page_size => 10
);
print $pager->output;

最古参のpagenationモジュール。提供されているのは「ページャーの付いたエントリ一覧のHTML」を作成する機能のみであり、一覧を作るために「エントリを抽出して返すcode-ref」を渡さなくてならない。他にもCGI.pmのインスタンスを渡す必要があったり、書き出されるHTMLがテーブルで構成されていたり、流石に現代で使うにはレガシー過ぎる印象。

HTML::Paginator

my $pager = HTML::Paginator->new($entries_per_page, @entries);
@entries = $pager->Contents($now_page);

print $pager->Page_Nav_HTML($now_page);

最終更新が2003年頃とかなり古いモジュール。エントリ全件が入ったリストを渡して、そこから指定ページの分を切り出して使うという発想に基づく。ページャーのHTMLを書き出す機能も備えるが、「前へ」「次へ」に相当するリンクが作られるのみ。敢えて使う理由が思い浮かばない。

Data::Pagination

my $pager = Data::Pagination->new($total_entries, $entries_per_page, $pages_per_set, $current_page);

コンストラクタ以外のメソッドを持たず、インスタンスが作成された時点でpagenationに必要な値を計算してプロパティにセットする。 $dumper->dumpValue($pager) すると、以下のように出力される($total_entries=30, $entries_per_page=5, $pages_per_set=1, $current_page=3 の場合)。

'current_page' => 3
'end_of_set' => 3
'end_of_slice' => 14
'entries_per_page' => 5
'length_of_slice' => 5
'next_page' => 4
'page_of_next_set' => 4
'page_of_prev_set' => 2
'pages_per_set' => 1
'prev_page' => 2
'start_of_set' => 3
'start_of_slice' => 10
'total_entries' => 30
'total_pages' => 6

Data::Scroller

my $page_params = Data::Scroller->new(
    max_value => $total_entries,
    selected  => $selected,
    increment => $entries_per_page,
  )->display;

display()はpagenationに必要な値をhash-refの構造体に詰めて返す。 $dumper->dumpValue($page_params) すると、以下のように出力される($total_entries=30, $selected=2, $entries_per_page=10 の場合)

'page_first' => 0
'page_increment' => 10
'page_last' => 20
'page_list' => ARRAY(0x3dee6c)
   0  HASH(0xb1a74c)
      'page_display' => 1
      'page_value' => 0
   1  HASH(0xb7798c)
      'page_display' => 2
      'page_value' => 10
   2  HASH(0xb779cc)
      'page_display' => 3
      'page_value' => 20
'page_name' => 'row_num'
'page_next' => 10
'page_prev' => 0
'page_total' => 3

CGI::Pager

my $pager = CGI::Pager->new(
    total_count => $total_entries,
    page_len    => $entries_per_page,
);
foreach my $page ($pager->pages) {
    if ($page->{is_current}) {
        print $page->{number};
    } else {
        print "<a href=\"$page->{url}\">$page->{number}</a>";
    }
}

print $pager->html('combined');

pagenationに必要な情報を返す一通りのメソッドを提供しており、またいくつかのパターンでページャーのHTMLを作成する機能も備える。エントリのoffsetは URI::QueryParam を利用していわゆるクエリ・パラメータから自動的に取得する。

上記の例にある最後のprint文は以下のHTMLを出力する($total_entries=30, $entries_per_page=5, offset=7の場合)

<a href="">First</a> &nbsp; <a href="?offset=2">Previous</a> &nbsp; <strong>1</strong>  <a href="?offset=5">2</a>  <a href="?offset=10">3</a>  <a href="?offset=15">4</a>  <a href="?offset=20">5</a>  <a href="?offset=25">6</a> &nbsp; <a href="?offset=12">Next</a> &nbsp; <a href="?offset=25">Last</a>

Data::Page

my $page = Data::Page->new($total_entries, $entries_per_page, $current_page);
print "         First page: ", $page->first_page, "\n";
print "          Last page: ", $page->last_page, "\n";
print "First entry on page: ", $page->first, "\n";
print " Last entry on page: ", $page->last, "\n";

シンプルなインターフェースでpaginationの最低限の機能だけを提供する定番モジュール。ページャーをHTMLに書き出す機能などはない。
Data::Pageset や Data::Page::Navigation など、 Data::Page を親クラスにしたバリエーション的なモジュールがいくつも存在する。

Data::Paginator

Data::Page互換のインターフェースに、Data::Pagesetと同等の機能を備えたモジュール。
Mooseベースならではのスッキリしたソースとなっている。ただ、この手の小さな機能を提供するライブラリにMooseは重いように思う。

Data::SimplePaginator

my $paginator = Data::SimplePaginator->new($number_per_page, @data);
foreach ( $paginator->page($now_page) ) {
    print $_;
}

与えたリストから指定したページ分の要素を切り出す機能に特化したモジュール。ページ遷移に関わる機能などはすっぱりと省かれている。