DIY Pagination in Rails

Brad Pauly — February 24, 2023

There are a lot of great gems that provide pagination for your ActiveRecord models, other ORM libraries, and data structures. Why would you want to write your own? It's a great way to practice good design and someday you might find that you'd like to change the way pagination works in your app.

What do we need to implement pagination? Let's start by assuming we want the simplest possible solution. We want to show 10 rows per page and have the ability to show the rows in any of the pages. For example, if our query returns 33 rows, we would have 4 total pages, each showing the rows described below.

  • Page 1: Show rows 1-10
  • Page 2: Show rows 11-20
  • Page 3: Show rows 21-30
  • Page 4: Show rows 31-33

At a minimum we need to figure out how to break the rows into pages. Let's try sovling this part in a ruby console.

Bingo. We now have a way to determine the total number of pages. Let's move this into a class to organize it a bit and write some tests to cover more cases.

So far so good, however, we're missing a very important piece. The most important in fact, which you can probably guess. There aren't any actual rows yet, we're only calculating numbers based on an already known total number of rows.

Let's change our code so that we can give the Pager class rows to use for the calculation. I'm going to start by using an Array because it's a quick and convenient place to start writing tests. I'd encourage you to think of rows, not as an Array, but as something that's playing a role. We know that Array has a count method that will give us the total number of items in the array. That's enough for us to make the calculation.

Here's the key: anything that responds to a count method could play the role of rows. To illustrate this, below is the same code tested using an OpenStruct instead of an Array.

Now that we have the rows we need to organize them into pages as described above. Let's take a step back and consider how this code might be used to help us decide if the design should evolve. It's very common to show paginated data in table form with links to other pages. Imagine we're building a database of recipes. It might looks something like the table below.

Favorite Recipes

Recipe
Tuna Sashimi
Chicken Fajitas
Bruschette with Tomato
Mushroom Risotto
Caprese Salad
Pasta and Beans
French Fries with Sausages
Teriyaki Chicken Donburi
Pasta and Beans
French Toast
Pages: 1 2 3 4

We want to be able to show the 10 rows for any given page. We could add a rows method that takes a page number and returns the appropriate rows.

This is confusing to me. The word rows is used for different purposes making it hard to know what's being referred to when reading the code.

Eventually we're going to use this in a rails application. Does it help to sketch out how we might use it there? We want to show the 10 rows for a page out of all the results from a query. What if we gave the Pager results instead of rows? And since we're only dealing with one page per request, we don't need to call rows with a different page, so let's move that to the initializer method too. Our controller and ERB code might look like this:

What do you think? There might be something better but I think it's good enough for now. We removed the potential confusion around rows.

More soon..

This post isn't finished yet, come back for updates soon!

Want new posts sent to your email?

I'm always looking for new topics to write about. Stuck on a problem or working on something interesting? You can reach me on Twitter @bradpauly or send me an email.