Three ways you can bend the ransack gem to your liking

Written by on

In the world's best yoga studio software for independent yoga studios, we have a page which lists all people in your studio. It's super basic: it only shows their name and role at the studio along with some actions you can take i.e. edit, delete. We haven't improved it much along the way.

I wanted to change that. TULA, in its essence, is a yoga centric CRM + billing system. Armed with inspiration from Intercom and their brilliant list of people, I set out to improve our people page.

We've had requests from our customers like "show me all the students who haven't been to class since X date" and "show me all the students who have joined the studio since Y date". These are great questions to ask and TULA should give you the answers. It's as simple as that.

To give them this, I did some investigation and found a great gem called ransack (if you haven't already, check out the ransack GitHub page). I quickly became satisfied with ransack as the basis of this new page. However, there were a few things I was constantly fighting with.

Ransackers derived from column aliases

This frustrated me for some time. I had a query like this:

I wanted to use their role, last attendance date, last purchase date, and join date as sortable columns in the table. However, it wasn't working out of the box until I realized I could simply pass an Arel node (thanks to this GitHub issue) of the column alias to a ransacker.

Default sort order

You can also use ransack to set a default sort order for when your users first visit the page.

Nulls last with ransackers

When you start sorting by column, you'll quickly notice null values getting in your way of the important data. For example, if you're sorting by the last time a user has made a purchase, you'll most likely want to see an order like this: about an hour ago, three hours ago, one week ago, etc. You wouldn't want to see an order like this: never, never, never, never, ... , about an hour ago, etc. The important data is getting buried by the nulls.

Ransack doesn't support this by default. However, there's a workaround (read more about how to add nulls last to ransack).

The workaround is great; however, it doesn't immediately work when you're using ransackers which return an instance of `Arel::Nodes::InfixOperation` or similar. You'll see a generated query similar to the following:

Obviously, this isn't valid SQL. This is actually due to the `full_name` ransacker defined above and in this case, the default sort order set to `full_name`. However, `Arel::Nodes::InfixOperation` responds to `to_sql`, so you can test for this case and transform it into valid SQL for the query. The code below will show you how:

Ransack is powerful, and we're just scratching the surface of its power. If you think I missed anything important, just mention me on Twitter or add a comment below.