How to paginate Rabl's collections
Asked Answered
A

4

12

I have this template:

# app/views/posts/index.rabl
collection @posts => :posts
attributes :id, :title, :subject
child(:user) { attributes :full_name }
node(:read) { |post| post.read_by?(@user) }

Witch returns:

{
    "posts": [
        {
            "post": {
                "id": 5,
                "title": "...",
                "subject": "...",
                "user": {
                    "full_name": "..."
                },
                "read": true
            }
        }
    ]
}

And I would like to add to add some pagination params in order to rendering this:

{
    "posts": [
        {
            "post": {
                "id": 5,
                "title": "...",
                "subject": "...",
                "user": {
                    "full_name": "..."
                },
                "read": true
            }
        }
    ],
    "total": 42,
    "total_pages": 12
}

Any ideas? Many thanks!

Azimuth answered 16/2, 2012 at 15:8 Comment(2)
It's better to add Pagination information in HEADER['Link'] like do by Github by exampleNarrate
I totally agree. However right now I have to remain compatible with some current APIs.Azimuth
A
16

Sorry for my noob question, whitch was answered by the README. Here's an example of pagination:

object false

node(:total) {|m| @posts.total_count }
node(:total_pages) {|m| @posts.num_pages }

child(@posts) do
  extends "api/v1/posts/show"
end

Note: I'm using Kaminari for pagination.

Azimuth answered 17/2, 2012 at 10:6 Comment(1)
Calling object(false) when the object is an array does not seem to work for me. Is there any chance you could gist what your index.rabl looks like?Adham
W
3

When searching for kaminari and rabl this is the first and pretty much only relevant result. As such, I would like to leave here a solution according to the HAL Specification that generates links like this.

So first, start with the view:

# api/v1/posts/index.rabl
object false

child(@posts) do
  extends 'api/v1/posts/show'
end

node(:_links) do
  paginate @posts
end

Then proceed to define the paginate method:

# app/helpers/api_helper
module ApiHelper
  def paginate(collection)
    current_page_num = collection.current_page
    last_page_num = collection.total_pages

    {
      :first => first_page,
      :previous => previous_page(current_page_num),
      :self => current_page(current_page_num),
      :next => next_page(current_page_num, last_page_num),
      :last => last_page(last_page_num)
    }
  end

  def first_page
    { :href => url_for(:page => 1) }
  end

  def previous_page(current_page_num)
    return nil if current_page_num <= 1
    { :href => url_for(:page => current_page_num-1) }
  end

  def current_page(current_page_num)
    { :href => url_for(:page => current_page_num) }
  end

  def next_page(current_page_num, last_page_num)
    return nil if current_page_num >= last_page_num
    { :href => url_for(:page => current_page_num+1) }
  end

  def last_page(last_page_num)
    { :href => url_for(:page => last_page_num) }
  end
end

And finally, include the helper in the necessary controllers. The helper could be included in a Api::BaseController, from which all API controllers inherit:

helper :api

I could not have done this without Zag zag..'s solution, so.. Thank you so much!

Wail answered 9/5, 2013 at 11:59 Comment(3)
that breaks pagination in normal views because by default rails includes all helpersMinstrel
Then either configure Rails so it doesn't include all helpers or just rename that paginate method.Wail
yeah, it just has to be noted, because rails default behavior is conflicting with solutionMinstrel
G
1

note, for will_paginate 3.0.0 the following works:

node(:total) {|m| @posts.total_entries }
node(:total_pages) {|m| (@posts.total_entries.to_f / @posts.per_page).ceil }
node(:page_num){|m| @posts.current_page}
Gid answered 21/8, 2012 at 12:15 Comment(1)
The total pages calculation should be: stuff.total_entries.to_f / stuff.per_page).ceil, to account for the last page not being full.Mcclain
C
0

This might be what you are looking for ;)

object false
node :comments do
  partial('posts/index', object: @posts)
end

node(:pagination) do
  {
    total:@posts.count,
    total_pages: 20
  }
end
Carnet answered 20/6, 2013 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.