A simple pagination concern

“Ugh, I need to implement pagination, again.” Whether it’s pagination or something else, I’m never very fond of re-implementing or going on the search for yet another gem to take care of features that are trivially popular. Even as someone with a deep appreciation of Rails, this feels to me like a missing part. One more papercut, of sorts.

A May of WTFs thread, Active Pagination and Active Search, highlighted that need as well, with 600 views for now—an arguably popular topic overall.

My wish would be to write one line of code, and get sensible defaults and conventions, no decisions to make. So I went ahead and did it for my own need.

In my opinion, something like this should already be in Rails. Apparently it was in Rails 1, and it got removed for reasons which were probably sensible at the time.

I also saw that there were proposals in the works for an ActiveSearch module of some kind, but I thought I’d start smaller: just a simple limit-offset pagination.

Here it is:

# app/controllers/concerns/pagination.rb

module Pagination
  extend ActiveSupport::Concern

  def default_per_page
    25
  end

  def page_no
    params[:page]&.to_i || 1
  end

  def per_page
    params[:per_page]&.to_i || default_per_page
  end

  def paginate_offset
    (page_no-1)*per_page
  end

  def paginate
    ->(it){ it.limit(per_page).offset(paginate_offset) }
  end
end

It makes all these helpful methods available in any controller you include it. If like me, you include it in ApplicationController:

class ApplicationController < EvenMoreAbstractController
  include Pagination
end

It does not invoke itself everywhere, but you have access to it anywhere.

Usable like so:
(thanks to Ruby’s new-ish #then method)

def index
  records = Record.all
  records.then(&paginate)
end

# which is a shortcut for

def index
  records = Record.all
  records.limit(per_page).offset(paginate_offset)
end

The param names I’ve chosen are :page and :per_page, both optional. eg:

  • GET /records
  • GET /records?page=3
  • GET /records?page=2&per_page=100.

Done.

It is not perfect, I haven’t tested it extensively, but it feels to me like a simple, sensible defaults approach to pagination.

I would dream of a more polished version getting included in core, but I don’t have any expectations. I’m personally looking forward to never caring about this again, and gladly invite anyone along :slight_smile:

Let’s care about more interesting problems, my friends!

1 Like

Thanks!

Is the kaminari gem a good option?

Have you found out what were the reasons for removing it in Rails 1. I don’t remember them, but would like to revise them.

This is a really nice and readable implementation. I think when it comes to being a drop-in, plug-and-play pagination scheme, you’re going to have to go down the slippery slope of the view layer, and add the options for page links, and the defaults plus overrides for windowing and so forth. Once you start down that path, where do you stop? Is there a default set of CSS for the links? Before long, you’ve written Kaminari or Pagy.

Walter

Actually, no!

Kaminari was the best until Pagy showed up, and just smoked it, performance-wise.

I have a hard time picturing a more-perfect solution at this stage.

Thanks for the blast-from-the-past. I’d completely forgotten that Rails 1 had a built-in pagination helper!