will_paginate で表示をカスタマイズする

趣旨

Rails 2.0 になって、ページ送りの機能が Rails 本体から消えてしまった。will_paginate という gem か、 paginating_find というプラグインを使うのがいまは普通であるらしい。私は will_paginate で、ちょっとページ送り部分の表示をカスタマイズして使ってみた。

情報ソース

will_paginateに移行
will_paginate を使う上で必要な知識がまとまっている。ただし、いまは gem になっているのに注意。

インストール

% sudo gem install will_paginate

として gem をインストールする。そして、 config/environment.rb で

require 'will_paginate'

として読み込めば、準備 OK。

後はコントローラで、

@posts = Post.paginate_by_board_id @board.id, :page => params[:page], :order => 'updated_at DESC'

とすればよい。通常 find_* と各部分を paginate_* と置き換えるだけである。ページ送りのコントロールを表示するには、ビューで、

 <%= will_paginate @posts %>

とする。すこぶる簡単。

ページ送りのコントロールのカスタマイズ

ただし、普通はページ送りのコントロールをそのまま使うことはなく、その表示をカスタマイズしたいものだろう。will_paginate gem ディレクトリ以下の lib/will_paginate/view_helpers.rb に、ビューで使用する will_paginate ヘルパーメソッドの定義がある。基本的には、リンクの HTML を生成するカスタマイズされた LinkRenderer クラスを will_paginate ヘルパーメソッドのオプションで指定すればよい。

たとえば、ページ送り部分に次のような HTML を生成したいとする。(正確には、結果は多少異なることになるが、本質的な違いではない)

<ul>
<li><a class="prev"><img src="/images/prev.gif" alt="前へ" /></a></li>
<li><a href="..." class="">1</a></li>
<li><a href="..." class="">2</a></li>
<li><a class="gap"><img src="/images/gap.gif" /></a></li>
<li><a href="...">6</a></li>
<li><a href="...">7</a></li>
<li><a href="...">8</a></li>
<li><a href="...">9</a></li>
<li><a href="..." class="current">10</a></li>
<li><a href="...">11</a></li>
<li><a href="...">12</a></li>
<li><a href="..." class="next"><img src="/images/web/next.gif" /></a></li>
</ul>

そのためには、たとえば、config/environment.rb に次のような記述をおく。デフォルトの LinkRenderer である WillPaginate::LinkRenderer を継承してカスタマイズされた CustomLinkRenderer を定義する。

class CustomLinkRenderer < WillPaginate::LinkRenderer
  def to_html
    links = @options[:page_links] ? windowed_links : []
    # previous/next buttons
    links.unshift page_link_or_span(@collection.previous_page, %w(disabled prev), %(<img src="/images/prev.gif" alt="前へ">))
    links.push    page_link_or_span(@collection.next_page,     %w(disabled next), %(<img src="/images/next.gif" alt="次へ">))
    
    html = links.join(@options[:separator])
    @options[:container] ? @template.content_tag(:ul, html, html_attributes) : html
  end

  def gap_marker
    '<li><a class="gap"><img src="/images/gap.gif" /></a></li>'
  end
  
  def page_link_or_span(page, span_class, text = nil)
    text ||= page.to_s
    classnames = Array[*span_class]
    
    if page and page != current_page
      "<li>" + @template.link_to(text, url_for(page), :rel => rel_value(page), :class => classnames[1]) + "</li>"
    else
      "<li><a class='#{classnames.join(' ')}'>#{text}</a></li>"
    end
  end
end

そして、ビューで次のように記述する。

 <%= will_paginate @posts, :renderer => 'CustomLinkRenderer' %>

この例自体に深い意味はない。要するに、こういうやり方で簡単に表示がカスタマイズできるということを示してみたかっただけである。詳しくは、lib/will_paginate/view_helpers.rb を参照のこと。