July 26, 2007

Ruby code: Finding the closest point

We want to fetch an avatar of specific size. How to find the closest size from all available sizes? Suppose available sizes are 16, 48 and 96.

An obvious version is

  def closest_size(size)
case
when size < 32
16
when size < 72
48
else
96
end
end

The value used in comparison tests is the average of two neighboring candidate sizes. The problem is that when our available sizes change, we need to add or remove when clauses and recalculate average values.


Here is a smarter way

def closest_size(size)
points = [16, 48, 96]
distances = points.map {|a| (size - a).abs}
points[distances.index(distances.min)]
end

It finds the point with shortest distance to the given size. Now if we want to change candidate sizes, we only need to change the array literal. Further, we can even pass the candidate array as an argument.

Rails: Error calling Dispatcher.dispatch...

I ran into a strange issue with one of my rails projects today - the browser displayed html source code in plain text instead of rendering. Nothing is listed in Firebug's net tab. Strange! Later I found the server was always sending back HTTP 404 with Content-type: text/plain. In the log, there were many lines of "Error calling Dispatcher.dispatch #<NameError: cannot remove Object::Handler>".

Finally, I found out that in a controller, someone has put include xxx at file level and in that module he defined class Handler. So that was it. After moving include xxx into the controller class definition, everything went well.

File level include (include outside class/module definition) actually affects Object - the mother of all ruby elves. So it's something we should definitely avoid in real world projects.

Never include outside class/module definition.

BTW, Rails is notoriously good at giving error messages unrelated to the cause of the problem. This is largely due to the dynamic nature of Ruby.