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:
- You can use symbolic enum values, not integers
- The order is explicit and easy to change
- The query remains database-agnostic
- Sorting happens at the SQL level
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:
- All records must be loaded into memory
- Pagination breaks
- Performance degrades as data grows
in_order_of avoids these problems by keeping ordering inside the database, where it belongs.