Working on a rails project, I created a very simple table to display key information about some customers. Some of the information are straight from the database columns, such as names and email addresses. In addition, there are couple of calculated columns, such as total number of transactions per person. These calculated amounts are methods within the customer model.
My next goal was to enable the ability to sort each column in my view table by clicking on the column header. Ryan Bates has a great screencast on how to do this for fields that match the database column name. You simply turn the text of each <th> into a link with a “sort” parameter that matches the database column name. Then, you modify your controller to take in that parameter using the order method of whichever model you are calling.
Unfortunately, that wasn’t enough to help me sort by my calculated fields, like total_transactions.
As I dug around on Google, I found a forum post answered by Ryan Bates (the same Ryan.) This was very helpful in understanding how to sort using a calculated field:
Customer.find(:all).sort_by { |customer| customer.total_transactions }
However, there was still a piece missing: how to incorporate that into the example he presented in his Railscast. For this, I took a guess of my own. It works, though I won’t claim it’s the best way to solve the problem.
Anyway, I basically added a second parameter named “type” to the <th> links. For the static column names, I set
:type => “column”, :sort => “column_name” – where column_name varies based on the link/column.
For the calculated solumns, I did the same thing, but set
:type => “calculated”, :sort => “method_name” – where method_name varies based on the link/model method.
I then changed my show method in the customers controller – instead of using the simple:
@customers = Customer.order(params[:sort])
like Ryan does in his RailsCast, I created a private method to return the ordered list of customers based on the type of sort I wanted. In this case, the show method has:
@customers = sorted_customers(params)
Now, when creating the method to return the list of properly sorted customers, I started with the simple step of making sure the basic column sort would work with the logic.
def sorted_customers(params)
if params[:type] == “calculated”
# do something different
else
return Customers.order(params[:sort])
end
end
For the columns which matched database field names, this worked. So I moved on to the next step, which was to use sort_by on the calculated fields, per Ryan’s answer in the Rails forum. However, the following did not work:
def sorted_customers(params)
if params[:type] == “calculated”
return Customers.find(:all).sort_by { |customer| customer.”#{params[:sort]}” }
else
return Customers.order(params[:sort])
end
end
I tinkered with syntax around the customer._______ and how to allow for a variable method name based on a parameter. After some searching, which was tough to figure out based on the keywords to use, I came across the following on StackOverflow (once again, to the rescue.) You can call the “send” method on any object and pass in the name of the method you wish to use. So, my end result is:
def sorted_customers(params)
if params[:type] == “calculated”
return Customers.find(:all).sort_by { |customer| customer.send(params[:sort].to_sym) }
else
return Customers.order(params[:sort])
end
end
Again, I make no representation about efficiency, but overall, this isn’t too complex a solution for a small app like mine. When it grows up, perhaps I’ll have to find a better way of doing it. Anyone out there have a more elegant solution? Would love to hear/learn it:)




