kaminari のページネーションリンクに Twitter Bootstrap 4 のスタイルを適用する

# kaminari のページネーションリンクをカスタマイズする
Rails でページネーションを行うための定番 gem、 [kaminari](https://github.com/amatsuda/kaminari)。
ページネーションリンクを view に表示するための[ヘルパ（`paginate`）](https://github.com/amatsuda/kaminari#helpers)も用意されていてたいへん便利です。
このヘルパを使ってデフォルトで出力されるリンクはとても簡素なものですが、テンプレートをカスタマイズすることで好みのスタイルに変更することができます。
今回は、Twitter Bootstrap4 [^2]に用意されている [Pagination コンポーネント](http://v4-alpha.getbootstrap.com/components/pagination/) のスタイルを適用してみたので、手順をメモしておきます。

[^2]: 今回は Bootstrap 4 を用いていますが、Bootstrap 3 でも同じマークアップで動くと思います。

# スタイルのプレビュー
## デフォルト
![スクリーンショット 2016-04-26 2.01.49.png](https://qiita-image-store.s3.amazonaws.com/0/5550/93a828bb-c440-a236-3d85-2f2b8796f7a2.png)

## カスタマイズ後
![スクリーンショット 2016-04-26 2.00.18.png](https://qiita-image-store.s3.amazonaws.com/0/5550/ea37ff6c-9858-affc-1420-8d1d475abe23.png)

最初のページへ戻るリンクの表示／非表示や、何ページ先まで省略せずリンクを出すかなどの細かい設定は、ヘルパのオプションで変更できます。[^1]


# 環境
* Ruby 2.3.0
* Rails 4.2.3
* kaminari 0.16.3
* Bootstrap 4.0.0.alpha3
* haml 4.0.7

# gem のインストール／セットアップ
Rails のプロジェクトは作成済みとします。

## Twitter Bootstrap 4
Bootstrap 4 には公式の gem が用意されています。
以下のページに従って Bootstrap をインストール／設定してください。
https://github.com/twbs/bootstrap-rubygem

## kaminari
kaminari のインストールは以下の公式ドキュメントを参照。

https://github.com/amatsuda/kaminari#install

Gemfile に追記して `bundle install` するだけです。

# ページネーションリンクのカスタムテンプレートを出力
以下のコマンドで、ページネーションリンクの view テンプレートが `app/views/kaminari` 以下に展開されます。

```bash
% rails g kaminari:views default -e haml

# erb な人はこちら:
% rails g kaminari:views default

% tree app/views/kaminari
app/views/kaminari/
├── _first_page.html.haml # 最初のページに戻るリンク
├── _gap.html.haml        # 省略されたページを示す表示（...）
├── _last_page.html.haml  # 最後のページヘのリンク
├── _next_page.html.haml  # 次のページへのリンク
├── _page.html.haml       # 1, 2, 3 など、各ページに飛ぶためのリンク
├── _paginator.html.haml  # 同じ他のテンプレートを呼び出すための親テンプレート
└── _prev_page.html.haml  # 前のページへのリンク
```

テンプレートは細かい部品に分かれています。
これらを上書きして Bootstrap の Pagination コンポーネントが要求するマークアップに変更しましょう。

# テンプレートの上書き
以下のように上書きします。

```haml:_first_page.html.haml
%li.page-item.prev{ class: "#{'disabled' if current_page.first?}" }
  = link_to (current_page.first? ? "#" : url), class: "page-link", "aria-label" => "First", :remote => remote do
    %span{"aria-hidden" => "true"} «
    %span.sr-only
      = t('views.pagination.first').html_safe
```

```haml:_gap.html.haml
%li.page-item.gap
  .page-link
    = t('views.pagination.truncate').html_safe
```

```haml:_last_page.html.haml
%li.page-item.next{ class: "#{'disabled' if current_page.last?}" }
  = link_to (current_page.last? ? "#" : url), class: "page-link", "aria-label" => "Last", :remote => remote do
    %span{"aria-hidden" => "true"} »
    %span.sr-only
      = t('views.pagination.last').html_safe
```

```haml:_next_page.html.haml 
%li.page-item.next{ class: "#{'disabled' if current_page.last?}" }
  = link_to (current_page.last? ? "#" : url), class: "page-link", "aria-label" => "Next", :remote => remote do
    %span{"aria-hidden" => "true"} ›
    %span.sr-only
      = t('views.pagination.next').html_safe
```

```haml:_page.html.haml
%li.page-item{ class: "#{'active' if page.current?}" }
  = link_to url, class: "page-link", remote: remote do
    = page
    - if page.current?
      %span.sr-only (current)
```

```haml:_paginator.html.haml
= paginator.render do
  %nav.text-xs-center
    %ul.pagination.pagination-sm
      = first_page_tag
      = prev_page_tag
      - each_page do |page|
        - if page.left_outer? || page.right_outer? || page.inside_window?
          = page_tag page
        - elsif !page.was_truncated?
          = gap_tag
      = next_page_tag
      = last_page_tag
```

```haml:_prev_page.html.haml
%li.page-item.prev{ class: "#{'disabled' if current_page.first?}" }
  = link_to (current_page.first? ? "#" : url), class: "page-link", "aria-label" => "Previous", :remote => remote do
    %span{"aria-hidden" => "true"} ‹
    %span.sr-only
      = t('views.pagination.previous').html_safe
```

# ページネーションリンクの出力
モデルの配列に相当するオブジェクトを引数に、ビューで `paginate` ヘルパを呼び出せば OK。

```haml
= paginate @users
```

当該ページをブラウザで開くと、前掲のスタイルでページネーションリンクが出ているはずです。

# 参考文献
* [Bootstrap 4 公式ドキュメント](http://v4-alpha.getbootstrap.com/getting-started/introduction/)
* [Bootstrap 3 公式ドキュメント](http://getbootstrap.com/)
* [kaminari 公式ドキュメント](https://github.com/amatsuda/kaminari#kaminari---)

[^1]: 具体例は公式ドキュメントにまとまっています: https://github.com/amatsuda/kaminari#helpers