Herencia en Rails
Friday, June 20th, 2008Después de leer y reunir información de varios sitios pude entender como implementar la herencia en Ruby on Rails; utiliza el enfoque STI.
STI: En este enfoque cada modelo/entidad perteneciente a la jerarquía de la herencia se representa en una única tabla. Está tiene un atributo especial (columna) que identifica a qué modelo/entidad corresponde dicho registro/tupla.
Por lo tanto, tendremos una única tabla con todos los atributos de todos los modelos/entidades más un atributo especial.
Tuve problemas precisamente cuando tengo una herencia con más de un nivel, la solución es igual que tener un nivel de herencia. Dejo aquí un ejemplo ilustrativo del asunto.

Cada modelo podríamos asignarle los siguientes atributos
- Vehiculo
- nombre, del tipo String
- Aereo
- alas, del tipo Integer
- Terrestre
- ruedas, del tipo Integer
- Auto
- puertas, del tipo integer
Ahora creemos las cuatro clases en app/models las cuales quedarán con el siguiente aspecto
- vehiculo.rb
class Vehiculo < ActiveRecord::Base end - aereo.rb
class Aereo < Vehiculo end - terrestre.rb
class Terrestre < Vehiculo end - auto.rb
class Auto < Terrestre end
Necesitamos crear un archivo migrate para mapear nuestros modelos a la base de datos (en este caso MySQL), este archivo tendría el siguiente aspecto:
class Ejemplo < ActiveRecord::Migration
def self.up
create_table :vehiculos do |t|
t.column :nombre, :string
t.column :alas, :integer
t.column :ruedas, :integer
t.column :puertas, :integer
t.column :type, :string
end
end
def self.down
drop_table :vehiculos
end
end
Indicaremos a Rails que implemente el esquema anterior, sólo bastará un rake:migrate. Esto nos genera la siguiente tabla
mysql> describe vehiculos;
+———+————–+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+———+————–+——+—–+———+—————-+
| id | int(11) | NO | PRI | NULL | auto_increment |
| nombre | varchar(255) | YES | | NULL | |
| alas | int(11) | YES | | NULL | |
| ruedas | int(11) | YES | | NULL | |
| puertas | int(11) | YES | | NULL | |
| type | varchar(255) | YES | | NULL | |
+———+————–+——+—–+———+—————-+
6 rows in set (0.01 sec)
Por último probemos como Rails resuelve la herencia, para esto utilizaremos la consola (script/console). Ire creando objetos (que se mapearan como registros en la base de datos) de los distintos modelos/entidades.
>> a = Vehiculo.create
=> #<Terrestre id: 1, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Vehiculo">
>> b = Aereo.create
=> #<Terrestre id: 2, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Aereo">
>> c = Terrestre.create
=> #<Terrestre id: 3, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Terrestre">
>> d = Auto.create
=> #<Auto id: 4, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Auto">
Como se puede ver, según el tipo de modelo creado, Rails establece el valor de la columna type. Ahora haré una serie de find, así se podrá ver cómo soluciona la herencia Rails.
>> Vehiculo.find :all
=> [#><Vehiculo id: 1, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: nil>, #<Aereo id: 2, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Aereo">, #<Terrestre id: 3, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Terrestre">, #<Auto id: 4, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Auto">]
Acá indiqué que me traiga todos los Vehiculos, es lo que se esperaba, me trajo todo los registros, ya que cada modelo hereda de Vehiculo y por la herencia cada modelo es un vehiculo.
>> Aereo.find :all
=> [#<Aereo id: 2, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Aereo">]
Como cree un sólo registro del tipo Aereo, me trae ese único. Ahora veamos el problema que comenté al inicio del post
>> Terrestre.find :all
=> [#<Terrestre id: 3, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Terrestre">, #<Auto id: 4, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Auto">]
Bien, me ha traído los dos registros, acá es igual que en el primer find que mostré, ya que Auto es un Terrestre (por la herencia), por lo tanto Rails debe traerme también ese tipo de registros
>> Auto.find :all
=> [#<Auto id: 4, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Auto">]
De este modo podemos implementar, en Ruby on Rails, el concepto de herencia..