I recently went to a mobile payments hackathon and worked on a reminder app with my co-founder of Flock. You input the name of your item and the day of the month that it’s due. The app then shows you how long you have to complete the given task based on a green to red spectrum inspired by Clear.
Being a hackathon project, we wanted to keep the app simple. It contained a Reminder model with 2 attributes: 1) name and 2) day of the month you want to complete the task.
I got to a point where I wanted to sort the items by number of days left until the item was due.
If I had a column in the database called ‘days_until_due’ I could have used the order method:
But I didn’t have a database column called ‘days_until_due’…
This got me asking myself, “What’s the best way to sort a model based on a calculated value instead of a value stored in the database?”
After googling around I stumbled upon a nice solution. It suggested creating an instance method to calculate the value, and then creating a class method that combines a query with the sort_by method.
Step 1: Create an instance method to calculate the value
In reminder.rb, I created an instance method to find out how many days until the item is due based on the day of the month the item was suppoed to be completed by and the current date:
1 2 3 4 5 6 7 8 9 10 11
Step 2: Create a class method to query and sort the model
Again in reminder.rb, I then created a class method, which combined a query, sort_by, and the instance method from above.
1 2 3
You may be asking yourself what is going on in that one line method? Let’s break it down step by step. First, Reminder.all returns an array of all the reminders (unsorted).
Next, I call the sort_by method on this array. This method takes a block as an argument and generates a sorted array by mapping the values through the given block. If no block is given, an enumerator is returned instead. Below is an example:
1 2 3 4 5
Now you’re probably asking yourself, but how does ‘&:days_until_due’ translate into a block? It has to do with procs and blocks. If you’re unfamiliar with these terms, you should check out a post I wrote discussing these parts of ruby. In one sentence, the ‘&:’ syntax is converting the instance method into a proc, and then converting the proc into a block, which is what Array#sort_by takes as an argument.
Below I will make the example above look like the method I used in my reminder app.
1 2 3 4 5 6 7 8 9 10 11
The above syntax works by combining implicit type casting with the ‘&’ operator. The ‘&’ operator is used in an argument list to convert a Proc instance into a block. If you combine the operator with something other than a Proc instance, implicit type casting will try to convert it to a Proc instance using the to_proc method. Since Symbol#to_proc exists, when we pass a symbol after the ‘&’ operator, it is converted into a proc, which is then converted into a block.
Going back to my original problem, all this talk about procs and blocks and type casting is what allowed me to create the following one line method:
1 2 3
This method allowed me to succinctly include a sorted array of reminders in my view with the following:
Now back to coding!