Elegant Enum Sorting with in_order_of

ActiveRecord enums are a convenient way to map human-readable states to integers stored in the database. They make your models more expressive and your code easier to read. However, sorting records by enum values can be tricky when the default numeric order doesn’t match your business logic.

Starting with modern versions of Rails, in_order_of provides a clean and expressive way to define custom ordering directly in SQL—without resorting to database-specific functions or complex CASE statements.

What Are ActiveRecord Enums?

Enums in ActiveRecord allow you to define a set of named values backed by integers in the database.

Example:

1
2
3
4
5
6
7
8
class Order < ApplicationRecord
  enum status: {
    pending: 0,
    paid: 1,
    shipped: 2,
    cancelled: 3
  }
end

This gives you a friendly API while keeping storage efficient:

1
2
3
Order.paid
order.shipped?
order.status # => "pending"

Why Default Ordering Isn’t Always Enough

When you call:

1
Order.order(:status)

Rails sorts by the underlying integer values. This works only if the enum definition matches the order you want to present in the UI or apply in business logic.

Often, that’s not the case. For example, you may want to prioritize shipped orders first, followed by paid ones, regardless of how the enum is defined.

Using in_order_of for Enum Sorting

Rails provides in_order_of as a high-level way to express custom ordering while keeping the sorting logic in SQL.

Example:

1
2
3
4
Order.in_order_of(
  :status,
  [:shipped, :paid, :pending, :cancelled]
)

This reads almost like plain English and clearly communicates intent.

Under the hood, Rails converts the enum values to their integer equivalents and generates the appropriate SQL to preserve the specified order.

Why in_order_of Works Well with Enums

in_order_of integrates seamlessly with enums because:

This makes it safer and more maintainable than hand-written SQL expressions.

Handling Partial Orders

Sometimes, you only care about ordering a subset of enum values.

1
Order.in_order_of(:status, [:shipped, :paid])

Records with other statuses will still be returned, but they’ll appear after the specified values, following the database’s default order.

This is particularly useful when highlighting “important” states without excluding the rest.

Why Not Sort in Ruby?

Sorting in Ruby using sort_by or similar methods may seem convenient, but it has downsides:

in_order_of avoids these problems by keeping ordering inside the database, where it belongs.