Hi all,
I would like to discuss a design idea I have in mind with you, in order
to get critical feedback.
ActiveRecord supports single table inheritance(STI) “per se”, BUT you
must add all possible instance variables(properties) of all subclasses
to the “base table”(as columns). I would like to circumvent this
restriction.
Say we have a
class AbstractGenericThing < ActiveRecord:Base
has_many :properties
end
In order to support STI the abstract_generic_things-table must have a
:type column.
Next comes the Property class
class Property < ActiveRecord:Base
belongs_to :GenericObject
end
The properties table has the following columns
:key, :string
:value, :string
-> hence we have a simple key-value-pair table.
Now any class which subclasses AbstractGenericThing inherits the
properties-array. A subclass can have as many properties as it requires.
So far we can have concrete (sub)classes for our concrete domain objects
but the property handling is not yet “intuitive”.
Enter ‘method_missing’-hook
class AbstractGenericThing
all methods which are not defined are handled as
properties
def method_missing(methodname, *args)
methodnameStr = methodname.to_s
if (methodnameStr.include? “=”)
# it’s a setter!
# remove the trailing =
methodnameStr.chop!
return setProperty(methodnameStr, args[0])
else
# it’s a getter!
return getProperty(methodnameStr)
end
end
helper method for setting the given property
def setProperty(propertyName, value)
property = self.properties.find(:first, :conditions => [ “name = ?”,
propertyName])
if (property.nil?)
# Looks like the property does not yet exist.
# Create it and set the value…
property = Property.new(:name => propertyName, :value => value)
# add this new property to my properties
self.properties << property
# safe?
else
# change the property’s value
p “modifying property”
property.value = value
property.save
#self.safe
end
end
helper method for getting the value of the given property
def getProperty(propertyName)
# there should be(at most) one
property = properties.find(:first, :conditions => [ “name = ?”,
propertyName])
if (property.nil?)
return nil
else
return property.value
end
end
end
Now we can dynamically “add” properties to our conrete objects(classes).
E.g.:
class Person < AbstractGenericThing
end
person = Person.new
person.GivenName = “John”
person.Name = “Smith”
person.GivenName
John
person.NameSmith
…
Persistence works as expected, hence I am inclined to go with this
design.
BEFORE I do I would like to hear what you think? What am I missing(apart
from the semantics I loose as all properties are stored as strings)?
Thanks for any critical input!
Clemens