So, I don’t remember if I mentioned my hardware engineering background before moving to the softer field. Anyhow, the low-level person inside me came up with this data model, inspired by the one-hot coding scheme of a state machine.
Assuming that we are trying to keep track of the moods of people in a database and do actions based on combinations of these moods. Here’s an alternative to using long logical statements such as “mood = 0 and mood = 1 and mood = 4 …”.
First, let’s define the model, Person.
# == Schema Information
#
# Table name: persons
#
# id :integer(11) not null, primary key
# mood :integer(64)
# ...
class Person < ActiveRecord::Base
MOOD = [
[:happy, 0x0001],
[:sad, 0x0002],
[:angry, 0x0004],
[:excited, 0x0008],
[:mellow, 0x0010],
[:lost, 0x0020],
[:refreshed, 0x0040],
[:hopeful, 0x0080]
]
# create an array
MOOD_HASH = Hash[*MOOD.flatten]
# complex lookup constants
# ...
end
In order to find all the people who are happy, excited, mellow, refreshed, and hopeful, all you need to do is to define a constant once (capturing your business logic):
# this needs to be
MOOD_POSITIVE = [:happy, :excited, :mellow, :refreshed, :hopeful].inject(0) do |mask, stat|
mask |= MOOD_HASH[stat]
end
and perform the following query:
Person.find(:all, :condition => ['mood & ?', Person::MOOD_POSITIVE])
Compare the cleanness and maintainability of the above against the following.
MOOD_INV_HASH = Person::MOOD_HASH.invert
Person.find(:all, :condition => [
'mood = ? and mood = ? and mood = ? and mood = ? and mood = ?',
MOOD_INV_HASH[:happy], MOOD_INV_HASH[:excited], MOOD_INV_HASH[:mellow],
MOOD_INV_HASH[:refreshed], MOOD_INV_HASH[:hopeful]
])
If there were only one query like that in the whole system, we would be able to get around this awkward query in other ways. However, when you’re dealing with many complex queries I think this way helps you write more readable and maintainable code. Please, let me know your opinion if you disagree.
One last note, it turns out that SQL has native support for this kind of status coding via SETs. However, I couldn’t find a nice Rails port for this data type.
Feedback is more than welcome. Thanks.