Writing Conversion Methods in Rails

As developers, we're occasionally tasked with maintaining software that we weren't directly involved in crafting. We don't have extensive knowledge of the domain, yet we can read the code, (hopefully) understand what's there, and modify it. We'll even attempt to improve the application and leave it in a better state than we found it. What follows is one such tale.

I recently had to make a change on a legacy Rails application. In order to make the change, I had to find the correct instance of Site. I didn't know which attribute on Site to query for, so the first thing I did was open the database schema to see a list of all attributes on Site. Wouldn't it be nice if I didn't need to look at the database schema to find the attribute I need? What if I told you I had an easier way of finding instances of your classes?

Avdi Grimm reminded me in his book Confident Ruby (which we loved) about Ruby's conversion methods. Specifically, methods like Array('thing') # => ["thing"] and URI('http://place.com') # => URI object with the URL of 'http://place.com'. I thought, wouldn't it be great if I could write Site('widget') and have it return the correct instance of Site? So that's just what I did.

Writing the conversion method was fairly simple:

def Site(site)
 if site.is_a?(Site)
 site
 elsif site.is_a?(String)
 Site.find_by_subdirectory!(site)
 else
 raise ArgumentError, 'bad argument (expected Site object or string)'
 end
end

The above code says that if site is already an instance of Site, then return it. If site is a string, find the instance of Site by subdirectory. If site is neither an instance of Site nor an instance of String, then raise an ArgumentError, informing the developer what the method was expecting. Great -- now I have a conversion method in which I can write Site('widget') and get the instance of the Widget site. But where do I put this method in a Rails application?

I could put it in an initializer. But it feels like it should really go with the Site class definition, in case a developer wants to extend it in the future. Where does Ruby place its conversion methods? To answer that question, I took a peek at Ruby's URI class. I noticed how the conversion method was with the class definition and at the bottom of the file, within the Kernel module. I took this precedence and modified the Site class to the following:

class Site < ActiveRecord::Base
 # … wow. such code …
end

module Kernel
 # Returns +site+ converted to a Site object.
 def Site(site)
 if site.is_a?(Site)
 site
 elsif site.is_a?(String)
 Site.find_by_subdirectory!(site)
 else
 raise ArgumentError, 'bad argument (expected Site object or string)'
 end
 end
end

I really like that I can now retrieve a site with Site('widget') and not have to look up the correct attribute to query by.

What do you think? Do you use conversion methods in your Rails applications? Let me know in the comments below.

Zachary is a developer in our Durham, NC, office. He helps our clients such as Counter Culture Coffee and the Wildlife Conservation Society use software to tackle their toughest challenges.

More posts by Zachary