Pagination for non-REST actions with kaminari

Sat, 25.06.2011 pagination, kaminari, params

A few months ago, our not-enough-praised Ryan Bates presented to us an alternative pagination gem, fully Rails 3 Ready TM, called kaminari. It is really simple to use and very powerful as well, but I found a little gotcha that got me crazing around for too long.

Say you have a typical paginated index action in your controller:

1
2
3
4
5
class PostController < ActionController::Base
  def index
    @posts = Post.page(params[:page])
  end
end

Quite fancy and smooth. Alright, say we are in real life, then this would be another controller:

1
2
3
4
5
6
class CommentsController < ActionController::Base
  def index
    @post = Post.find(params[:post_id])
    @comments = post.comments.page(params[:page])
  end
end

A second paginating step, but still quite simple. So let’s write the corresponding view:

1
2
3
4
5
= post.title
= post.body
- for comment in @comments
  = comment.body
= paginate @comments

You can try that out (in case you wonder, that’s not HAML, but Slim, kind of a lighter version of it): this paginate thing is great and it works. But, imagine that today you woke up a little unconventional, you know, sort of against REST conventions, and want to render that view from the PostsController, like this:

1
2
3
4
5
6
class PostsController < ActionController::Base
  def comments
    @post = Post.find(params[:post_id])
    @comments = post.comments.page(params[:page])
  end
end

Not a big deal. You might have your good reasons for it. For instance, ‘comments#index’ could be rendering comments not after post, but after user (or whatever association you could imagine). Well, paginate is not going to work like that, look:

1
No route matches {:controller=>"posts", :action=>"comments", :page=>nil, :post_id=>"1"} (ActionView::Template::Error)

Unless you tweak your declaration a little, of course. That is what drove me nuts (what “no route matches page nil”? ), and I blamed subsequently decent_exposure, cancan and kaminari. But no one was to blame, but myself. Look and get delighted by the simplicity of the solution:

1
2
3
4
5
= post.title
= post.body
- for comment in @comments
  = comment.body
= paginate @comments, :params => {:post_id => post }

It just needed some love, i.e. a params declaration to scope the comments out of their natural environment. Now we are talking…

0 comments