Incorporating runtime parameters in has_many :conditions

I'm trying to model the concept of a person who goes by various nicknames. My Person model contains a "name" attribute containing their real name, and a "has_many :nicknames" relationship. Through my business rules, I'm enforcing the requirement that a person's real name is always included in the database as a nickname.

Rule #1: If you're duplicating data, you're doing the wrong thing, unless           you have a VERY SERIOUS performance problem and you have measured           the performance advantage of a denormalization very carefully.

(If this seems like a really bad data structure, I'm open to suggestions. However, having weighed up various alternatives, this seems to me to be the least worst solution.)

I'd recommend either:

   1) Not storing their real name in nicknames.

or

   2) Removing the "name" attribute from Person and adding an extra attribute       to your Nicknames model named real_name that is a boolean.

I prefer #1 because real names are decided NOT nicknames, so why would they be stored there?

If you like #2, then I'd highly recommend renaming that model Names, because they'll decidedly NOT be just nicknames any longer!

I find a lot of DB designers paint themselves into corners by doing the sort of thing that you've done here. If the tables names don't fit the use, it's not that you have a special need, it's because your data model is wrong. :slight_smile:

If the data model is wrong, you're going to write a larger volume of code, and even worse, a larger volume of *complex* code to 'decipher' the data that will become hopelessly entangled over time.

I now want to add an "alternative_nicknames" relationship, which will comprise all of the person's nicknames *excluding* their real name. I've tried

With the advice above, there's no need to do this. In #1 real name is one place and nicknames are another place, or in #2 the two are very obviously separate via the boolean column which will make it easier to pinpoint real names -vs- nicknames.

has_many :alternative_nicknames, :class_name => "Nickname",   :conditions => ["nickname <> ?", self.name]

- however, since has_many is a class-level method, the :conditions block is evaluated at the point of loading the class, and therefore self.name returns "Person" rather than the person's name. Out of desperation I've even tried wrapping the has_many declaration in an after_initialize callback, but then it complains that has_many is not defined. The examples in the Rails docs and the Pragmatic book only demonstrate static SQL fragments being used in the :conditions clause, not ones incorporating parameters. Any ideas how I can achieve this?

There's nothing magic in the *operation* of has_many for the simplistic cases, so you *could* leave out the has_many declaration and just code an accessor method of your own:

def alternative_nicknames    self.nicknames.find(:all, :conditions => ["nickname <> ?", self.name]) end

One thing I think you need to be a bit skeptical of, however, is in the clear overloading of the Nicknames.nickname column. You're not just storing a nickname there, but through usage, you're also using it to define a boolean relationship as well, based on variable data in the parent table.

If you use method #1 above, you could access the various pieces very simply and without any sort of magic at all.

person.name person.nicknames person.all_names

class Person    def all_names      [name,nicknames.collect { |n| n.nickname}]    end end