When we started work on the new Harvest Help Center, it was with the fresh perspective of our recent Rails 3 upgrades. We looked for a simple row sorting solution, but the traditional plugin ActsAsList is showing it’s age with old ActiveRecord conventions and naive logic. Rising to the challenge, we rolled up our sleeves and built our own ordering solution, that we have dubbed RankedModel.
ActsAsList
ActsAsList has been around for a long time. I can’t recall what release of Rails it came out with, but it was well before Rails 1.0. The fact that it has remained the default solution for row ordering so long speaks to how simple the problem of row sorting is. There isn’t anything too crazy going on in this code:
- Keep the order of items in an integer column.
- When fetching a set of items, default the order statement to sort by that column.
- When we move an item to a new position, assign all the other items to their new positions.
But there were a few downsides to using ActsAsList:
- The code does not use ARel. This means we can’t chain sorted item collections into complex ARel queries.
- If one item is moved, many of the other items are adjusted to compensate for the movement. Many rows in MySQL can be updated by one row’s reassignment.
What’s a modern Rails developer to do?
Enter RankedModel
RankedModel is an ActsAsList replacement developed by the crew here at Harvest, written with ARel from the ground up and using an optimized position storage mechanism. It’s tested with Rspec. Check it out on Github:
https://github.com/harvesthq/ranked-model
RankedModel stores rank as any number in a range of 0 to 65534. When you assign a new position to an item, it is assigned a rank between it’s new neighbors. This means we update one row, not several. Let’s look at an example involving ducks:
- Waddly (ranked 13106)
- Quacky (ranked 26212)
- Webby (ranked 39318)
- Beaky (ranked 52424)
If we move Beaky to position #2, we need not adjust the ranks of other items to maintain the new order:
- Waddly (ranked 13106)
- Beaky (ranked 19659)
- Quacky (ranked 26212)
- Webby (ranked 39318)
When a newly positioned row is assigned a conflicting rank, the library will redistribute the rank values evenly. The logic is made simpler by a nice abstraction layer that allows developers to focus on the sort logic of a given rank scope and instance.
We think RankedModel is a great, modern solution for a simple problem developers need to deal with often, and we’ve decided to share it with the community-at-large as an open source solution. We sure hope it’s as helpful for you as it has been for us.
Want to work on projects like the Ranked Model Gem? We’re hiring Rails Developers at all skill levels.