Wed, 08 Mar 06

Rails table pluralization

I was talking to someone recently about the pluralisation of table names in rails. It was grating him because some hand built queries no longer read very naturally.

-- (in rails) requesting the forename of the 'first' person
SELECT forename FROM people WHERE = 1;

-- would read better as
SELECT forename FROM person WHERE = 1;

Paul (a colleague at reevoo) pointed out that there are two sides to the singular/plural table name debate. You will always have some queries (as above) that will work better when the table is singular; and others (as below) that will work better when the table is plural.

-- requesting all people having a country of 'uk'
SELECT * FROM person WHERE country = 'UK';

-- (in rails) would read better as
SELECT * FROM people WHERE country = 'UK';

Personally, because of the very small amount of hand written sql I’ve had to use with Rails this has never been an issue to me. However, it did make me think about a slight adaptation to Active Record…

By default (in Rails), you create a singular (active record) model (person) that maps to the plural table (people). Although very convenient (it’s the reason I’ve rarely had to touch sql), it too suffers from the same problems outlined in the queries above. I think the two statements below show the same sort of natural vs unnatural language that appears in the sql above.

# good english (acting on a single person) = 'chris'

# bad english (we are asking for multiple people)
Person.find(:all, :conditions => "country = 'uk'")

The adaptation to active record I was thinking about would allow you to operate on both a Person and People, using the variant that made the code read more naturally. That is, we could leave the example above as is, but change the Person.find to People.find1. There are different ways to go about this but to get some of the way there with a little code (i.e. not having to manually create both singular and plural classes for each table); I came up with the following simple hack.

class ActiveRecord::Base
  class << self
    alias_method :__existing_inherited, :inherited
    def inherited(child)
      plural_class = child.to_s.pluralize
      unless plural_class =~ /#<Class:./ or Object.constants.include?(plural_class)

Just add it to the bottom of your environment.rb file and when you first reference one of your singular classes it will automatically descend a plural class from it.

There are lots of things wrong with this code and many better ways for it to be implemented but it was really just for me to see if I could do it.

1 Boring and sad. I was going to start that sentence with i.e. but wasn’t sure whether that was grammatically correct (I’m not very good at English). I did a quick search and found that it translates as ‘that is’ from the Latin which is much better.