Railsでページネートと言えばkaminariで、インストールも簡単ですが、JSONに件数とか入るようになってなかったので、もっとも簡単な方法を試行錯誤したので、メモしておきます。
結論は、viewsのindex.json.jbuilderを直すだけで、良い感じになりました。
日本語はkaminari-i18nを利用。

kaminariインストール

Gemfileの最終行に追加

# Use kaminari
gem 'kaminari'
gem 'kaminari-i18n'
$ bundle install
$ rails g kaminari:config
      create  config/initializers/kaminari_config.rb
$ rails g kaminari:views default
      create  app/views/kaminari/_next_page.html.erb
      create  app/views/kaminari/_last_page.html.erb
      create  app/views/kaminari/_first_page.html.erb
      create  app/views/kaminari/_page.html.erb
      create  app/views/kaminari/_paginator.html.erb
      create  app/views/kaminari/_prev_page.html.erb
      create  app/views/kaminari/_gap.html.erb

変更可能にする為、翻訳ファイルをgemの中からコピー

$ cp ~/.rvm/gems/ruby-2.6.3/gems/kaminari-i18n-0.5.0/config/locales/ja.yml config/locales/kaminari.ja.yml 

【参考】ここまでのコミット内容
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/e39139cd3660cb4c272cc2886adee9eb70694376

kaminariの使い方

app/controllers/spaces_controller.rb での例
変更前

  def index
    @spaces = Space.all
  end

変更後

  def index
    @spaces = Space.order(created_at: 'DESC', id: 'DESC').page(params[:page]).per(Settings['default_spaces_limit'])
  end

並び順(order)は、DBに依存する場合もあるので、必ず一意になるようにidも加えた方が良い。(検証や問題があった時の再現がし易い)

表示数(per)は未指定なら、config/initializers/kaminari_config.rbの設定が使われるが、機能によって変えたい場合も多いし、ベタ書きは良くないのでconfig(gem)に定義。

config/settings.yml

default_spaces_limit: 25

app/views/spaces/index.html.erb に下記を追加。

<p><%= page_entries_info @spaces %></p>
<p><%= paginate @spaces %></p>

JSONページネート対応

app/views/spaces/index.json.jbuilder での例
変更前

json.array! @spaces, partial: 'spaces/space', as: :space

変更後

json.total_count @spaces.total_count
json.current_page @spaces.current_page
json.total_pages @spaces.total_pages
json.limit_value @spaces.limit_value
json.spaces do
  json.array! @spaces
end

partial消したので、app/views/spaces/_space.json.jbuilder も削除

http://localhost:3000/spaces.json の出力結果を整形。良い感じですね。

{
    "total_count": 26,
    "current_page": 1,
    "total_pages": 2,
    "limit_value": 25,
    "spaces": [
        {
            "id": 26,
            "subdomain": "myspace26",
            "name": "マイスペース26",
            "created_at": "2020-05-25T09:50:48.884+09:00",
            "updated_at": "2020-05-25T09:50:48.884+09:00"
        },
<省略>
        {
            "id": 2,
            "subdomain": "myspace2",
            "name": "マイスペース2",
            "created_at": "2020-05-25T09:50:48.604+09:00",
            "updated_at": "2020-05-25T09:50:48.604+09:00"
        }
    ]
}

テスト追加

多いので抜粋して書きます。実際には、contextからit_behaves_likeで、
下記のshared_examples_forを呼び出してあげる必要があります。

spec/requests/spaces_spec.rb

    shared_examples_for 'ベースドメイン、1ページ、1番新しいスペース' do
      it '(json)名前が一致する' do
        get spaces_url, headers: base_headers.merge(json_headers)
        response_spaces = JSON.parse(response.body)['spaces']
        expect(response_spaces[0]['name']).to eq(@create_spaces[@create_spaces.count - 1].name)
      end
      it '(json)サブドメインが一致する' do
        get spaces_url, headers: base_headers.merge(json_headers)
        response_spaces = JSON.parse(response.body)['spaces']
        expect(response_spaces[0]['subdomain']).to eq(@create_spaces[@create_spaces.count - 1].subdomain)
      end
    end

    shared_examples_for "ベースドメイン、1ページ、#{Settings['default_spaces_limit']}番目に新しいスペース" do
      it '(json)名前が一致する' do
        get spaces_url, headers: base_headers.merge(json_headers)
        response_spaces = JSON.parse(response.body)['spaces']
        expect(response_spaces[Settings['default_spaces_limit'] - 1]['name']).to eq(@create_spaces[@create_spaces.count - Settings['default_spaces_limit']].name)
      end
      it '(json)サブドメインが一致する' do
        get spaces_url, headers: base_headers.merge(json_headers)
        response_spaces = JSON.parse(response.body)['spaces']
        expect(response_spaces[Settings['default_spaces_limit'] - 1]['subdomain']).to eq(@create_spaces[@create_spaces.count - Settings['default_spaces_limit']].subdomain)
      end
    end

    shared_examples_for "ベースドメイン、2ページ、#{Settings['default_spaces_limit'] + 1}番目に新しいスペース" do
      it '(json)名前が一致する' do
        get spaces_url(page: 2), headers: base_headers.merge(json_headers)
        response_spaces = JSON.parse(response.body)['spaces']
        expect(response_spaces[0]['name']).to eq(@create_spaces[@create_spaces.count - (Settings['default_spaces_limit'] + 1)].name)
      end
      it '(json)サブドメインが一致する' do
        get spaces_url(page: 2), headers: base_headers.merge(json_headers)
        response_spaces = JSON.parse(response.body)['spaces']
        expect(response_spaces[0]['subdomain']).to eq(@create_spaces[@create_spaces.count - (Settings['default_spaces_limit'] + 1)].subdomain)
      end
    end

    shared_examples_for 'ベースドメイン' do
      it '(json)total_countが一致する' do
        get spaces_url, headers: base_headers.merge(json_headers)
        expect(JSON.parse(response.body)['total_count']).to eq(@create_spaces.count)
      end
      it '(json)1ページ、current_pageが一致する' do
        get spaces_url, headers: base_headers.merge(json_headers)
        expect(JSON.parse(response.body)['current_page']).to eq(1)
      end
      it '(json)2ページ、current_pageが一致する' do
        get spaces_url(page: 2), headers: base_headers.merge(json_headers)
        expect(JSON.parse(response.body)['current_page']).to eq(2)
      end
      it '(json)total_pagesが一致する' do
        get spaces_url, headers: base_headers.merge(json_headers)
        expect(JSON.parse(response.body)['total_pages']).to eq((@create_spaces.count - 1).div(Settings['default_spaces_limit']) + 1)
      end
      it '(json)limit_valueが一致する' do
        get spaces_url, headers: base_headers.merge(json_headers)
        expect(JSON.parse(response.body)['limit_value']).to eq(Settings['default_spaces_limit'])
      end
    end

【参考】ここまでのコミット内容
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/f617c93953699f2e190f11bee6ecab0b71d02fca

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です